summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--perl/Wallet/ACL.pm196
-rw-r--r--perl/Wallet/Admin.pm102
-rw-r--r--perl/Wallet/Config.pm10
-rw-r--r--perl/Wallet/Database.pm27
-rw-r--r--perl/Wallet/Object/Base.pm318
-rw-r--r--perl/Wallet/Object/Keytab.pm116
-rw-r--r--perl/Wallet/Report.pm298
-rw-r--r--perl/Wallet/Schema.pm282
-rw-r--r--perl/Wallet/Schema/Result/Acl.pm99
-rw-r--r--perl/Wallet/Schema/Result/AclEntry.pm63
-rw-r--r--perl/Wallet/Schema/Result/AclHistory.pm101
-rw-r--r--perl/Wallet/Schema/Result/AclScheme.pm73
-rw-r--r--perl/Wallet/Schema/Result/Enctype.pm34
-rw-r--r--perl/Wallet/Schema/Result/Flag.pm54
-rw-r--r--perl/Wallet/Schema/Result/KeytabEnctype.pm42
-rw-r--r--perl/Wallet/Schema/Result/KeytabSync.pm42
-rw-r--r--perl/Wallet/Schema/Result/Object.pm258
-rw-r--r--perl/Wallet/Schema/Result/ObjectHistory.pm127
-rw-r--r--perl/Wallet/Schema/Result/SyncTarget.pm40
-rw-r--r--perl/Wallet/Schema/Result/Type.pm64
-rw-r--r--perl/Wallet/Server.pm19
-rwxr-xr-xperl/create-ddl93
-rw-r--r--perl/sql/Wallet-Schema-0.07-0.08-MySQL.sql7
-rw-r--r--perl/sql/Wallet-Schema-0.07-0.08-SQLite.sql6
-rw-r--r--perl/sql/Wallet-Schema-0.07-MySQL.sql211
-rw-r--r--perl/sql/Wallet-Schema-0.07-SQLite.sql219
-rw-r--r--perl/sql/Wallet-Schema-0.08-MySQL.sql193
-rw-r--r--perl/sql/Wallet-Schema-0.08-PostgreSQL.sql201
-rw-r--r--perl/sql/Wallet-Schema-0.08-SQLite.sql201
-rwxr-xr-xperl/t/admin.t21
-rw-r--r--perl/t/lib/Util.pm5
-rwxr-xr-xperl/t/report.t2
-rwxr-xr-xperl/t/schema.t111
-rwxr-xr-xperl/t/server.t2
-rwxr-xr-xserver/wallet-admin23
35 files changed, 2886 insertions, 774 deletions
diff --git a/perl/Wallet/ACL.pm b/perl/Wallet/ACL.pm
index 2a06442..4f51c70 100644
--- a/perl/Wallet/ACL.pm
+++ b/perl/Wallet/ACL.pm
@@ -33,26 +33,24 @@ $VERSION = '0.07';
# doesn't exist, throws an exception.
sub new {
my ($class, $id, $dbh) = @_;
- my ($sql, $data, $name);
+ my (%search, $data, $name);
if ($id =~ /^\d+\z/) {
- $sql = 'select ac_id, ac_name from acls where ac_id = ?';
+ $search{ac_id} = $id;
} else {
- $sql = 'select ac_id, ac_name from acls where ac_name = ?';
+ $search{ac_name} = $id;
}
eval {
- ($data, $name) = $dbh->selectrow_array ($sql, undef, $id);
- $dbh->commit;
+ $data = $dbh->resultset('Acl')->find (\%search);
};
if ($@) {
- $dbh->rollback;
die "cannot search for ACL $id: $@\n";
} elsif (not defined $data) {
die "ACL $id not found\n";
}
my $self = {
dbh => $dbh,
- id => $data,
- name => $name,
+ id => $data->ac_id,
+ name => $data->ac_name,
};
bless ($self, $class);
return $self;
@@ -69,18 +67,27 @@ sub create {
$time ||= time;
my $id;
eval {
- my $sql = 'insert into acls (ac_name) values (?)';
- $dbh->do ($sql, undef, $name);
- $id = $dbh->last_insert_id (undef, undef, 'acls', 'ac_id');
+ my $guard = $dbh->txn_scope_guard;
+
+ # Create the new record.
+ my %record = (ac_name => $name);
+ my $acl = $dbh->resultset('Acl')->create (\%record);
+ $id = $acl->ac_id;
die "unable to retrieve new ACL ID" unless defined $id;
+
+ # Add to the history table.
my $date = strftime ('%Y-%m-%d %T', localtime $time);
- $sql = "insert into acl_history (ah_acl, ah_action, ah_by, ah_from,
- ah_on) values (?, 'create', ?, ?, ?)";
- $dbh->do ($sql, undef, $id, $user, $host, $date);
- $dbh->commit;
+ %record = (ah_acl => $id,
+ ah_action => 'create',
+ ah_by => $user,
+ ah_from => $host,
+ ah_on => $date);
+ my $history = $dbh->resultset('AclHistory')->create (\%record);
+ die "unable to create new history entry" unless defined $history;
+
+ $guard->commit;
};
if ($@) {
- $dbh->rollback;
die "cannot create ACL $name: $@\n";
}
my $self = {
@@ -126,13 +133,13 @@ sub scheme_mapping {
my ($self, $scheme) = @_;
my $class;
eval {
- my $sql = 'select as_class from acl_schemes where as_name = ?';
- ($class) = $self->{dbh}->selectrow_array ($sql, undef, $scheme);
- $self->{dbh}->commit;
+ my %search = (as_name => $scheme);
+ my $scheme_rec = $self->{dbh}->resultset('AclScheme')
+ ->find (\%search);
+ $class = $scheme_rec->as_class;
};
if ($@) {
$self->error ($@);
- $self->{dbh}->rollback;
return;
}
if (defined $class) {
@@ -155,11 +162,14 @@ sub log_acl {
unless ($action =~ /^(add|remove)\z/) {
die "invalid history action $action";
}
- my $date = strftime ('%Y-%m-%d %T', localtime $time);
- my $sql = 'insert into acl_history (ah_acl, ah_action, ah_scheme,
- ah_identifier, ah_by, ah_from, ah_on) values (?, ?, ?, ?, ?, ?, ?)';
- $self->{dbh}->do ($sql, undef, $self->{id}, $action, $scheme, $identifier,
- $user, $host, $date);
+ my %record = (ah_acl => $self->{id},
+ ah_action => $action,
+ ah_scheme => $scheme,
+ ah_identifier => $identifier,
+ ah_by => $user,
+ ah_from => $host,
+ ah_on => strftime ('%Y-%m-%d %T', localtime $time));
+ $self->{dbh}->resultset('AclHistory')->create (\%record);
}
##############################################################################
@@ -176,13 +186,15 @@ sub rename {
return;
}
eval {
- my $sql = 'update acls set ac_name = ? where ac_id = ?';
- $self->{dbh}->do ($sql, undef, $name, $self->{id});
- $self->{dbh}->commit;
+ my $guard = $self->{dbh}->txn_scope_guard;
+ my %search = (ac_id => $self->{id});
+ my $acls = $self->{dbh}->resultset('Acl')->find (\%search);
+ $acls->ac_name ($name);
+ $acls->update;
+ $guard->commit;
};
if ($@) {
$self->error ("cannot rename ACL $self->{id} to $name: $@");
- $self->{dbh}->rollback;
return;
}
$self->{name} = $name;
@@ -200,27 +212,44 @@ sub destroy {
my ($self, $user, $host, $time) = @_;
$time ||= time;
eval {
- my $sql = 'select ob_type, ob_name from objects where ob_owner = ?
- or ob_acl_get = ? or ob_acl_store = ? or ob_acl_show = ? or
- ob_acl_destroy = ? or ob_acl_flags = ?';
- my $sth = $self->{dbh}->prepare ($sql);
- $sth->execute (($self->{id}) x 6);
- my $entry = $sth->fetchrow_arrayref;
- if (defined $entry) {
- die "ACL in use by $entry->[0]:$entry->[1]";
+ my $guard = $self->{dbh}->txn_scope_guard;
+
+ # Make certain no one is using the ACL.
+ my @search = ({ ob_owner => $self->{id} },
+ { ob_acl_get => $self->{id} },
+ { ob_acl_store => $self->{id} },
+ { ob_acl_show => $self->{id} },
+ { ob_acl_destroy => $self->{id} },
+ { ob_acl_flags => $self->{id} });
+ my @entries = $self->{dbh}->resultset('Object')->search (\@search);
+ if (@entries) {
+ my ($entry) = @entries;
+ die "ACL in use by ".$entry->ob_type.":".$entry->ob_name;
}
- $sql = 'delete from acl_entries where ae_id = ?';
- $self->{dbh}->do ($sql, undef, $self->{id});
- $sql = 'delete from acls where ac_id = ?';
- $self->{dbh}->do ($sql, undef, $self->{id});
- $sql = "insert into acl_history (ah_acl, ah_action, ah_by, ah_from,
- ah_on) values (?, 'destroy', ?, ?, ?)";
- $self->{dbh}->do ($sql, undef, $self->{id}, $user, $host, $time);
- $self->{dbh}->commit;
+
+ # Delete any entries (there may or may not be any).
+ my %search = (ae_id => $self->{id});
+ @entries = $self->{dbh}->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->{dbh}->resultset('Acl')->find(\%search);
+ $entry->delete if defined $entry;
+
+ # Create new history line for the deletion.
+ my %record = (ah_acl => $self->{id},
+ ah_action => 'destroy',
+ ah_by => $user,
+ ah_from => $host,
+ ah_on => $time);
+ $self->{dbh}->resultset('AclHistory')->create (\%record);
+ $guard->commit;
};
if ($@) {
$self->error ("cannot destroy ACL $self->{id}: $@");
- $self->{dbh}->rollback;
return;
}
return 1;
@@ -239,15 +268,16 @@ sub add {
return;
}
eval {
- my $sql = 'insert into acl_entries (ae_id, ae_scheme, ae_identifier)
- values (?, ?, ?)';
- $self->{dbh}->do ($sql, undef, $self->{id}, $scheme, $identifier);
+ my $guard = $self->{dbh}->txn_scope_guard;
+ my %record = (ae_id => $self->{id},
+ ae_scheme => $scheme,
+ ae_identifier => $identifier);
+ my $entry = $self->{dbh}->resultset('AclEntry')->create (\%record);
$self->log_acl ('add', $scheme, $identifier, $user, $host, $time);
- $self->{dbh}->commit;
+ $guard->commit;
};
if ($@) {
$self->error ("cannot add $scheme:$identifier to $self->{id}: $@");
- $self->{dbh}->rollback;
return;
}
return 1;
@@ -260,23 +290,21 @@ sub remove {
my ($self, $scheme, $identifier, $user, $host, $time) = @_;
$time ||= time;
eval {
- my $sql = 'select * from acl_entries where ae_id = ? and ae_scheme = ?
- and ae_identifier = ?';
- my ($data) = $self->{dbh}->selectrow_array ($sql, undef, $self->{id},
- $scheme, $identifier);
- unless (defined $data) {
+ my $guard = $self->{dbh}->txn_scope_guard;
+ my %search = (ae_id => $self->{id},
+ ae_scheme => $scheme,
+ ae_identifier => $identifier);
+ my $entry = $self->{dbh}->resultset('AclEntry')->find (\%search);
+ unless (defined $entry) {
die "entry not found in ACL\n";
}
- $sql = 'delete from acl_entries where ae_id = ? and ae_scheme = ?
- and ae_identifier = ?';
- $self->{dbh}->do ($sql, undef, $self->{id}, $scheme, $identifier);
+ $entry->delete;
$self->log_acl ('remove', $scheme, $identifier, $user, $host, $time);
- $self->{dbh}->commit;
+ $guard->commit;
};
if ($@) {
my $entry = "$scheme:$identifier";
$self->error ("cannot remove $entry from $self->{id}: $@");
- $self->{dbh}->rollback;
return;
}
return 1;
@@ -294,19 +322,17 @@ sub list {
undef $self->{error};
my @entries;
eval {
- my $sql = 'select ae_scheme, ae_identifier from acl_entries where
- ae_id = ?';
- my $sth = $self->{dbh}->prepare ($sql);
- $sth->execute ($self->{id});
- my $entry;
- while (defined ($entry = $sth->fetchrow_arrayref)) {
- push (@entries, [ @$entry ]);
+ my $guard = $self->{dbh}->txn_scope_guard;
+ my %search = (ae_id => $self->{id});
+ my @entry_recs = $self->{dbh}->resultset('AclEntry')
+ ->search (\%search);
+ for my $entry (@entry_recs) {
+ push (@entries, [ $entry->ae_scheme, $entry->ae_identifier ]);
}
- $self->{dbh}->commit;
+ $guard->commit;
};
if ($@) {
$self->error ("cannot retrieve ACL $self->{id}: $@");
- $self->{dbh}->rollback;
return;
} else {
return @entries;
@@ -338,25 +364,27 @@ sub history {
my ($self) = @_;
my $output = '';
eval {
- my $sql = 'select ah_action, ah_scheme, ah_identifier, ah_by, ah_from,
- ah_on from acl_history where ah_acl = ? order by ah_on';
- my $sth = $self->{dbh}->prepare ($sql);
- $sth->execute ($self->{id});
- my @data;
- while (@data = $sth->fetchrow_array) {
- $output .= "$data[5] ";
- if ($data[0] eq 'add' or $data[0] eq 'remove') {
- $output .= "$data[0] $data[1] $data[2]";
+ my $guard = $self->{dbh}->txn_scope_guard;
+ my %search = (ah_acl => $self->{id});
+ my %options = (order_by => 'ah_on');
+ my @data = $self->{dbh}->resultset('AclHistory')->search (\%search,
+ \%options);
+ for my $data (@data) {
+ $output .= sprintf ("%s %s ", $data->ah_on->ymd,
+ $data->ah_on->hms);
+ if ($data->ah_action eq 'add' || $data->ah_action eq 'remove') {
+ $output .= sprintf ("%s %s %s", $data->ah_action,
+ $data->ah_scheme, $data->ah_identifier);
} else {
- $output .= $data[0];
+ $output .= $data->ah_action;
}
- $output .= "\n by $data[3] from $data[4]\n";
+ $output .= sprintf ("\n by %s from %s\n", $data->ah_by,
+ $data->ah_from);
}
- $self->{dbh}->commit;
+ $guard->commit;
};
if ($@) {
$self->error ("cannot read history for $self->{id}: $@");
- $self->{dbh}->rollback;
return;
}
return $output;
@@ -487,7 +515,7 @@ references.
=item new(ACL, DBH)
Instantiate a new ACL object with the given ACL ID or name. Takes the
-Wallet::Database object to use for retrieving metadata from the wallet
+Wallet::Schema object to use for retrieving metadata from the wallet
database. Returns a new ACL object if the ACL was found and throws an
exception if it wasn't or on any other error.
diff --git a/perl/Wallet/Admin.pm b/perl/Wallet/Admin.pm
index a1aef83..511916d 100644
--- a/perl/Wallet/Admin.pm
+++ b/perl/Wallet/Admin.pm
@@ -1,7 +1,7 @@
# Wallet::Admin -- Wallet system administrative interface.
#
# Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2008, 2009, 2010, 2011
+# Copyright 2008, 2009, 2010, 2011, 2012
# The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.
@@ -17,13 +17,12 @@ use strict;
use vars qw($VERSION);
use Wallet::ACL;
-use Wallet::Database;
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.06';
+$VERSION = '0.07';
##############################################################################
# Constructor, destructor, and accessors
@@ -34,7 +33,7 @@ $VERSION = '0.06';
# Throw an exception if anything goes wrong.
sub new {
my ($class) = @_;
- my $dbh = Wallet::Database->connect;
+ my $dbh = Wallet::Schema->connect;
my $self = { dbh => $dbh };
bless ($self, $class);
return $self;
@@ -61,7 +60,7 @@ sub error {
# Disconnect the database handle on object destruction to avoid warnings.
sub DESTROY {
my ($self) = @_;
- $self->{dbh}->disconnect unless $self->{dbh}->{InactiveDestroy};
+ $self->{dbh}->storage->dbh->disconnect;
}
##############################################################################
@@ -75,17 +74,49 @@ sub DESTROY {
# true on success and false on failure, setting the object error.
sub initialize {
my ($self, $user) = @_;
- my $schema = Wallet::Schema->new;
- eval { $schema->create ($self->{dbh}) };
+
+ # Deploy the database schema from DDL files, if they exist. If not then
+ # we automatically get the database from the Schema modules.
+ $self->{dbh}->deploy ({}, $Wallet::Config::DB_DDL_DIRECTORY);
if ($@) {
$self->error ($@);
return;
}
+ $self->default_data;
+
+ # Create a default admin ACL.
my $acl = Wallet::ACL->create ('ADMIN', $self->{dbh}, $user, 'localhost');
unless ($acl->add ('krb5', $user, $user, 'localhost')) {
$self->error ($acl->error);
return;
}
+
+ return 1;
+}
+
+# Load default data into various tables. We'd like to do this more directly
+# in the schema definitions, but not yet seeing a good way to do that.
+sub default_data {
+ my ($self) = @_;
+
+ # acl_schemes default rows.
+ my ($r1) = $self->{dbh}->resultset('AclScheme')->populate ([
+ [ qw/as_name as_class/ ],
+ [ 'krb5', 'Wallet::ACL::Krb5' ],
+ [ 'krb5-regex', 'Wallet::ACL::Krb5::Regex' ],
+ [ 'ldap-attr', 'Wallet::ACL::LDAP::Attribute' ],
+ [ 'netdb', 'Wallet::ACL::NetDB' ],
+ [ 'netdb-root', 'Wallet::ACL::NetDB::Root' ],
+ ]);
+ warn "default AclScheme not installed" unless defined $r1;
+
+ # types default rows.
+ my @record = ([ qw/ty_name ty_class/ ],
+ [ 'file', 'Wallet::Object::File' ],
+ [ 'keytab', 'Wallet::Object::Keytab' ]);
+ ($r1) = $self->{dbh}->resultset('Type')->populate (\@record);
+ warn "default Type not installed" unless defined $r1;
+
return 1;
}
@@ -102,12 +133,31 @@ sub reinitialize {
# false on failure.
sub destroy {
my ($self) = @_;
- my $schema = Wallet::Schema->new;
- eval { $schema->drop ($self->{dbh}) };
- if ($@) {
- $self->error ($@);
- return;
+
+ # Get an actual DBI handle and use it to delete all tables.
+ my $real_dbh = $self->{dbh}->storage->dbh;
+ my @tables = qw/acls acl_entries acl_history acl_schemes enctypes
+ flags keytab_enctypes keytab_sync objects object_history
+ sync_targets types dbix_class_schema_versions/;
+ for my $table (@tables) {
+ my $sql = "DROP TABLE IF EXISTS $table";
+ $real_dbh->do ($sql);
}
+
+ return 1;
+}
+
+# Save a DDL of the database in every supported database server. Returns
+# true on success and false on failure.
+sub backup {
+ my ($self, $oldversion) = @_;
+
+ my @dbs = qw/MySQL SQLite PostgreSQL/;
+ my $version = $Wallet::Schema::VERSION;
+ $self->{dbh}->create_ddl_dir (\@dbs, $version,
+ $Wallet::Config::DB_DDL_DIRECTORY,
+ $oldversion);
+
return 1;
}
@@ -115,12 +165,16 @@ sub destroy {
# and false on failure.
sub upgrade {
my ($self) = @_;
- my $schema = Wallet::Schema->new;
- eval { $schema->upgrade ($self->{dbh}) };
+
+ if ($self->{dbh}->get_db_version) {
+ eval { $self->{dbh}->upgrade; };
+ }
if ($@) {
$self->error ($@);
+ warn $@;
return;
}
+
return 1;
}
@@ -135,13 +189,14 @@ sub upgrade {
sub register_object {
my ($self, $type, $class) = @_;
eval {
- my $sql = 'insert into types (ty_name, ty_class) values (?, ?)';
- $self->{dbh}->do ($sql, undef, $type, $class);
- $self->{dbh}->commit;
+ my $guard = $self->{dbh}->txn_scope_guard;
+ my %record = (ty_name => $type,
+ ty_class => $class);
+ $self->{dbh}->resultset('Type')->create (\%record);
+ $guard->commit;
};
if ($@) {
$self->error ("cannot register $class for $type: $@");
- $self->{dbh}->rollback;
return;
}
return 1;
@@ -154,13 +209,14 @@ sub register_object {
sub register_verifier {
my ($self, $scheme, $class) = @_;
eval {
- my $sql = 'insert into acl_schemes (as_name, as_class) values (?, ?)';
- $self->{dbh}->do ($sql, undef, $scheme, $class);
- $self->{dbh}->commit;
+ my $guard = $self->{dbh}->txn_scope_guard;
+ my %record = (as_name => $scheme,
+ as_class => $class);
+ $self->{dbh}->resultset('AclScheme')->create (\%record);
+ $guard->commit;
};
if ($@) {
- $self->error ("cannot registery $class for $scheme: $@");
- $self->{dbh}->rollback;
+ $self->error ("cannot register $class for $scheme: $@");
return;
}
return 1;
diff --git a/perl/Wallet/Config.pm b/perl/Wallet/Config.pm
index 71f6e0f..98dae03 100644
--- a/perl/Wallet/Config.pm
+++ b/perl/Wallet/Config.pm
@@ -167,6 +167,16 @@ backends, particularly SQLite, do not need this.
our $DB_PASSWORD;
+=item DB_DDL_DIRECTORY
+
+Specifies the directory used to dump the database schema in formats for
+each possible database server. This also includes diffs between schema
+versions, for upgrades.
+
+=cut
+
+our $DB_DDL_DIRECTORY;
+
=back
=head1 FILE OBJECT CONFIGURATION
diff --git a/perl/Wallet/Database.pm b/perl/Wallet/Database.pm
index 7daab9f..8df338a 100644
--- a/perl/Wallet/Database.pm
+++ b/perl/Wallet/Database.pm
@@ -1,12 +1,12 @@
# Wallet::Database -- Wallet system database connection management.
#
-# This module is a thin wrapper around DBI to handle determination of the
-# database driver and configuration settings automatically on connect. The
+# This module is a thin wrapper around DBIx::Class to handle determination
+# of the database configuration settings automatically on connect. The
# intention is that Wallet::Database objects can be treated in all respects
-# like DBI objects in the rest of the code.
+# like DBIx::Class objects in the rest of the code.
#
# Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2008, 2010 Board of Trustees, Leland Stanford Jr. University
+# Copyright 2008-2012 Board of Trustees, Leland Stanford Jr. University
#
# See LICENSE for licensing terms.
@@ -14,32 +14,21 @@
# Modules and declarations
##############################################################################
-# Set up the subclasses. This is required to avoid warnings under DBI 1.40
-# and later, even though we don't actually make use of any overridden
-# statement handle or database handle methods.
-package Wallet::Database::st;
-use vars qw(@ISA);
-@ISA = qw(DBI::st);
-
-package Wallet::Database::db;
-use vars qw(@ISA);
-@ISA = qw(DBI::db);
-
package Wallet::Database;
require 5.006;
use strict;
use vars qw(@ISA $VERSION);
-use DBI;
+use Wallet::Schema;
use Wallet::Config;
-@ISA = qw(DBI);
+@ISA = qw(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.03';
+$VERSION = '0.04';
##############################################################################
# Core overrides
@@ -65,7 +54,7 @@ sub connect {
}
my $user = $Wallet::Config::DB_USER;
my $pass = $Wallet::Config::DB_PASSWORD;
- my %attrs = (PrintError => 0, RaiseError => 1, AutoCommit => 0);
+ my %attrs = (PrintError => 0, RaiseError => 1);
my $dbh = eval { $class->SUPER::connect ($dsn, $user, $pass, \%attrs) };
if ($@) {
die "cannot connect to database: $@\n";
diff --git a/perl/Wallet/Object/Base.pm b/perl/Wallet/Object/Base.pm
index 87506f4..5bd89a7 100644
--- a/perl/Wallet/Object/Base.pm
+++ b/perl/Wallet/Object/Base.pm
@@ -24,7 +24,7 @@ use Wallet::ACL;
# 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.05';
+$VERSION = '0.06';
##############################################################################
# Constructors
@@ -37,10 +37,11 @@ $VERSION = '0.05';
# probably be usable as-is by most object types.
sub new {
my ($class, $type, $name, $dbh) = @_;
- my $sql = 'select ob_name from objects where ob_type = ? and ob_name = ?';
- my $data = $dbh->selectrow_array ($sql, undef, $type, $name);
- $dbh->commit;
- die "cannot find ${type}:${name}\n" unless ($data and $data eq $name);
+ my %search = (ob_type => $type,
+ ob_name => $name);
+ my $object = $dbh->resultset('Object')->find (\%search);
+ die "cannot find ${type}:${name}\n"
+ unless ($object and $object->ob_name eq $name);
my $self = {
dbh => $dbh,
name => $name,
@@ -59,18 +60,27 @@ sub create {
$time ||= time;
die "invalid object type\n" unless $type;
die "invalid object name\n" unless $name;
+ my $guard = $dbh->txn_scope_guard;
eval {
- my $date = strftime ('%Y-%m-%d %T', localtime $time);
- my $sql = 'insert into objects (ob_type, ob_name, ob_created_by,
- ob_created_from, ob_created_on) values (?, ?, ?, ?, ?)';
- $dbh->do ($sql, undef, $type, $name, $user, $host, $date);
- $sql = "insert into object_history (oh_type, oh_name, oh_action,
- oh_by, oh_from, oh_on) values (?, ?, 'create', ?, ?, ?)";
- $dbh->do ($sql, undef, $type, $name, $user, $host, $date);
- $dbh->commit;
+ my %record = (ob_type => $type,
+ ob_name => $name,
+ ob_created_by => $user,
+ ob_created_from => $host,
+ ob_created_on => strftime ('%Y-%m-%d %T',
+ localtime $time));
+ $dbh->resultset('Object')->create (\%record);
+
+ %record = (oh_type => $type,
+ oh_name => $name,
+ oh_action => 'create',
+ oh_by => $user,
+ oh_from => $host,
+ oh_on => strftime ('%Y-%m-%d %T', localtime $time));
+ $dbh->resultset('ObjectHistory')->create (\%record);
+
+ $guard->commit;
};
if ($@) {
- $dbh->rollback;
die "cannot create object ${type}:${name}: $@\n";
}
my $self = {
@@ -126,30 +136,36 @@ sub log_action {
# We have two traces to record, one in the object_history table and one in
# the object record itself. Commit both changes as a transaction. We
# assume that AutoCommit is turned off.
+ my $guard = $self->{dbh}->txn_scope_guard;
eval {
- my $date = strftime ('%Y-%m-%d %T', localtime $time);
- my $sql = 'insert into object_history (oh_type, oh_name, oh_action,
- oh_by, oh_from, oh_on) values (?, ?, ?, ?, ?, ?)';
- $self->{dbh}->do ($sql, undef, $self->{type}, $self->{name}, $action,
- $user, $host, $date);
+ my %record = (oh_type => $self->{type},
+ oh_name => $self->{name},
+ oh_action => $action,
+ oh_by => $user,
+ oh_from => $host,
+ oh_on => strftime ('%Y-%m-%d %T', localtime $time));
+ $self->{dbh}->resultset('ObjectHistory')->create (\%record);
+
+ my %search = (ob_type => $self->{type},
+ ob_name => $self->{name});
+ my $object = $self->{dbh}->resultset('Object')->find (\%search);
if ($action eq 'get') {
- $sql = 'update objects set ob_downloaded_by = ?,
- ob_downloaded_from = ?, ob_downloaded_on = ? where
- ob_type = ? and ob_name = ?';
- $self->{dbh}->do ($sql, undef, $user, $host, $date, $self->{type},
- $self->{name});
+ $object->ob_downloaded_by ($user);
+ $object->ob_downloaded_from ($host);
+ $object->ob_downloaded_on (strftime ('%Y-%m-%d %T',
+ localtime $time));
} elsif ($action eq 'store') {
- $sql = 'update objects set ob_stored_by = ?, ob_stored_from = ?,
- ob_stored_on = ? where ob_type = ? and ob_name = ?';
- $self->{dbh}->do ($sql, undef, $user, $host, $date, $self->{type},
- $self->{name});
+ $object->ob_stored_by ($user);
+ $object->ob_stored_from ($host);
+ $object->ob_stored_on (strftime ('%Y-%m-%d %T',
+ localtime $time));
}
- $self->{dbh}->commit;
+ $object->update;
+ $guard->commit;
};
if ($@) {
my $id = $self->{type} . ':' . $self->{name};
$self->error ("cannot update history for $id: $@");
- $self->{dbh}->rollback;
return;
}
return 1;
@@ -175,12 +191,18 @@ sub log_set {
unless ($fields{$field}) {
die "invalid history field $field";
}
- my $date = strftime ('%Y-%m-%d %T', localtime $time);
- my $sql = "insert into object_history (oh_type, oh_name, oh_action,
- oh_field, oh_type_field, oh_old, oh_new, oh_by, oh_from, oh_on)
- values (?, ?, 'set', ?, ?, ?, ?, ?, ?, ?)";
- $self->{dbh}->do ($sql, undef, $self->{type}, $self->{name}, $field,
- $type_field, $old, $new, $user, $host, $date);
+
+ my %record = (oh_type => $self->{type},
+ oh_name => $self->{name},
+ oh_action => 'set',
+ oh_field => $field,
+ oh_type_field => $type_field,
+ oh_old => $old,
+ oh_new => $new,
+ oh_by => $user,
+ oh_from => $host,
+ oh_on => strftime ('%Y-%m-%d %T', localtime $time));
+ $self->{dbh}->resultset('ObjectHistory')->create (\%record);
}
##############################################################################
@@ -202,20 +224,21 @@ sub _set_internal {
$self->error ("cannot modify ${type}:${name}: object is locked");
return;
}
+
+ my $guard = $self->{dbh}->txn_scope_guard;
eval {
- my $sql = "select ob_$attr from objects where ob_type = ? and
- ob_name = ?";
- my $old = $self->{dbh}->selectrow_array ($sql, undef, $type, $name);
- $sql = "update objects set ob_$attr = ? where ob_type = ? and
- ob_name = ?";
- $self->{dbh}->do ($sql, undef, $value, $type, $name);
+ my %search = (ob_type => $type,
+ ob_name => $name);
+ my $object = $self->{dbh}->resultset('Object')->find (\%search);
+ my $old = $object->get_column ("ob_$attr");
+
+ $object->update ({ "ob_$attr" => $value });
$self->log_set ($attr, $old, $value, $user, $host, $time);
- $self->{dbh}->commit;
+ $guard->commit;
};
if ($@) {
my $id = $self->{type} . ':' . $self->{name};
$self->error ("cannot set $attr on $id: $@");
- $self->{dbh}->rollback;
return;
}
return 1;
@@ -236,14 +259,13 @@ sub _get_internal {
my $type = $self->{type};
my $value;
eval {
- my $sql = "select $attr from objects where ob_type = ? and
- ob_name = ?";
- $value = $self->{dbh}->selectrow_array ($sql, undef, $type, $name);
- $self->{dbh}->commit;
+ my %search = (ob_type => $type,
+ ob_name => $name);
+ my $object = $self->{dbh}->resultset('Object')->find (\%search);
+ $value = $object->get_column ($attr);
};
if ($@) {
$self->error ($@);
- $self->{dbh}->rollback;
return;
}
return $value;
@@ -356,14 +378,18 @@ sub flag_check {
my $dbh = $self->{dbh};
my $value;
eval {
- my $sql = 'select fl_flag from flags where fl_type = ? and fl_name = ?
- and fl_flag = ?';
- $value = $dbh->selectrow_array ($sql, undef, $type, $name, $flag);
- $dbh->commit;
+ my %search = (fl_type => $type,
+ fl_name => $name,
+ fl_flag => $flag);
+ my $flag = $dbh->resultset('Flag')->find (\%search);
+ if (not defined $flag) {
+ $value = 0;
+ } else {
+ $value = $flag->fl_flag;
+ }
};
if ($@) {
$self->error ("cannot check flag $flag for ${type}:${name}: $@");
- $dbh->rollback;
return;
} else {
return ($value) ? 1 : 0;
@@ -378,22 +404,21 @@ sub flag_clear {
my $name = $self->{name};
my $type = $self->{type};
my $dbh = $self->{dbh};
+ my $guard = $dbh->txn_scope_guard;
eval {
- my $sql = 'select * from flags where fl_type = ? and fl_name = ? and
- fl_flag = ?';
- my ($data) = $dbh->selectrow_array ($sql, undef, $type, $name, $flag);
- unless (defined $data) {
+ my %search = (fl_type => $type,
+ fl_name => $name,
+ fl_flag => $flag);
+ my $flag = $dbh->resultset('Flag')->find (\%search);
+ unless (defined $flag) {
die "flag not set\n";
}
- $sql = 'delete from flags where fl_type = ? and fl_name = ? and
- fl_flag = ?';
- $dbh->do ($sql, undef, $type, $name, $flag);
- $self->log_set ('flags', $flag, undef, $user, $host, $time);
- $dbh->commit;
+ $flag->delete;
+ $self->log_set ('flags', $flag->fl_flag, undef, $user, $host, $time);
+ $guard->commit;
};
if ($@) {
$self->error ("cannot clear flag $flag on ${type}:${name}: $@");
- $dbh->rollback;
return;
}
return 1;
@@ -407,20 +432,18 @@ sub flag_list {
undef $self->{error};
my @flags;
eval {
- my $sql = 'select fl_flag from flags where fl_type = ? and
- fl_name = ? order by fl_flag';
- my $sth = $self->{dbh}->prepare ($sql);
- $sth->execute ($self->{type}, $self->{name});
- my $flag;
- while (defined ($flag = $sth->fetchrow_array)) {
- push (@flags, $flag);
+ my %search = (fl_type => $self->{type},
+ fl_name => $self->{name});
+ my %attrs = (order_by => 'fl_flag');
+ my @flags_rs = $self->{dbh}->resultset('Flag')->search (\%search,
+ \%attrs);
+ for my $flag (@flags_rs) {
+ push (@flags, $flag->fl_flag);
}
- $self->{dbh}->commit;
};
if ($@) {
my $id = $self->{type} . ':' . $self->{name};
$self->error ("cannot retrieve flags for $id: $@");
- $self->{dbh}->rollback;
return;
} else {
return @flags;
@@ -435,22 +458,21 @@ sub flag_set {
my $name = $self->{name};
my $type = $self->{type};
my $dbh = $self->{dbh};
+ my $guard = $dbh->txn_scope_guard;
eval {
- my $sql = 'select * from flags where fl_type = ? and fl_name = ? and
- fl_flag = ?';
- my ($data) = $dbh->selectrow_array ($sql, undef, $type, $name, $flag);
- if (defined $data) {
+ my %search = (fl_type => $type,
+ fl_name => $name,
+ fl_flag => $flag);
+ my $flag = $dbh->resultset('Flag')->find (\%search);
+ if (defined $flag) {
die "flag already set\n";
}
- $sql = 'insert into flags (fl_type, fl_name, fl_flag)
- values (?, ?, ?)';
- $dbh->do ($sql, undef, $type, $name, $flag);
- $self->log_set ('flags', undef, $flag, $user, $host, $time);
- $dbh->commit;
+ $flag = $dbh->resultset('Flag')->create (\%search);
+ $self->log_set ('flags', undef, $flag->fl_flag, $user, $host, $time);
+ $guard->commit;
};
if ($@) {
$self->error ("cannot set flag $flag on ${type}:${name}: $@");
- $dbh->rollback;
return;
}
return 1;
@@ -466,11 +488,10 @@ sub format_acl_id {
my ($self, $id) = @_;
my $name = $id;
- my $sql = 'select ac_name from acls where ac_id = ?';
- my $sth = $self->{dbh}->prepare ($sql);
- $sth->execute ($id);
- if (my @ref = $sth->fetchrow_array) {
- $name = $ref[0] . " ($id)";
+ my %search = (ac_id => $id);
+ my $acl_rs = $self->{dbh}->resultset('Acl')->find (\%search);
+ if (defined $acl_rs) {
+ $name = $acl_rs->ac_name . " ($id)";
}
return $name;
@@ -483,23 +504,29 @@ sub history {
my ($self) = @_;
my $output = '';
eval {
- my $sql = 'select oh_action, oh_field, oh_type_field, oh_old, oh_new,
- oh_by, oh_from, oh_on from object_history where oh_type = ? and
- oh_name = ? order by oh_on';
- my $sth = $self->{dbh}->prepare ($sql);
- $sth->execute ($self->{type}, $self->{name});
- my @data;
- while (@data = $sth->fetchrow_array) {
- $output .= "$data[7] ";
- my ($old, $new) = @data[3..4];
- if ($data[0] eq 'set' and $data[1] eq 'flags') {
- if (defined ($data[4])) {
- $output .= "set flag $data[4]";
- } elsif (defined ($data[3])) {
- $output .= "clear flag $data[3]";
+ my %search = (oh_type => $self->{type},
+ oh_name => $self->{name});
+ my %attrs = (order_by => 'oh_on');
+ my @history = $self->{dbh}->resultset('ObjectHistory')
+ ->search (\%search, \%attrs);
+
+ for my $history_rs (@history) {
+ $output .= sprintf ("%s %s ", $history_rs->oh_on->ymd,
+ $history_rs->oh_on->hms);
+
+ my $old = $history_rs->oh_old;
+ my $new = $history_rs->oh_new;
+ my $action = $history_rs->oh_action;
+ my $field = $history_rs->oh_field;
+
+ if ($action eq 'set' and $field eq 'flags') {
+ if (defined ($new)) {
+ $output .= "set flag $new";
+ } elsif (defined ($old)) {
+ $output .= "clear flag $old";
}
- } elsif ($data[0] eq 'set' and $data[1] eq 'type_data') {
- my $attr = $data[2];
+ } elsif ($action eq 'set' and $field eq 'type_data') {
+ my $attr = $history_rs->oh_type_field;
if (defined ($old) and defined ($new)) {
$output .= "set attribute $attr to $new (was $old)";
} elsif (defined ($old)) {
@@ -507,9 +534,8 @@ sub history {
} elsif (defined ($new)) {
$output .= "add $new to attribute $attr";
}
- } elsif ($data[0] eq 'set'
- and ($data[1] eq 'owner' or $data[1] =~ /^acl_/)) {
- my $field = $data[1];
+ } elsif ($action eq 'set'
+ and ($field eq 'owner' or $field =~ /^acl_/)) {
$old = $self->format_acl_id ($old) if defined ($old);
$new = $self->format_acl_id ($new) if defined ($new);
if (defined ($old) and defined ($new)) {
@@ -519,8 +545,7 @@ sub history {
} elsif (defined ($old)) {
$output .= "unset $field (was $old)";
}
- } elsif ($data[0] eq 'set') {
- my $field = $data[1];
+ } elsif ($action eq 'set') {
if (defined ($old) and defined ($new)) {
$output .= "set $field to $new (was $old)";
} elsif (defined ($new)) {
@@ -529,16 +554,15 @@ sub history {
$output .= "unset $field (was $old)";
}
} else {
- $output .= $data[0];
+ $output .= $action;
}
- $output .= "\n by $data[5] from $data[6]\n";
+ $output .= sprintf ("\n by %s from %s\n", $history_rs->oh_by,
+ $history_rs->oh_from);
}
- $self->{dbh}->commit;
};
if ($@) {
my $id = $self->{type} . ':' . $self->{name};
$self->error ("cannot read history for $id: $@");
- $self->{dbh}->rollback;
return;
}
return $output;
@@ -592,15 +616,14 @@ sub show {
[ ob_downloaded_on => 'Downloaded on' ]);
my $fields = join (', ', map { $_->[0] } @attrs);
my @data;
+ my $object_rs;
eval {
- my $sql = "select $fields from objects where ob_type = ? and
- ob_name = ?";
- @data = $self->{dbh}->selectrow_array ($sql, undef, $type, $name);
- $self->{dbh}->commit;
+ my %search = (ob_type => $type,
+ ob_name => $name);
+ $object_rs = $self->{dbh}->resultset('Object')->find (\%search);
};
if ($@) {
$self->error ("cannot retrieve data for ${type}:${name}: $@");
- $self->{dbh}->rollback;
return;
}
my $output = '';
@@ -609,15 +632,18 @@ sub show {
# Format the results. We use a hack to insert the flags before the first
# trace field since they're not a field in the object in their own right.
# The comment should be word-wrapped at 80 columns.
- for my $i (0 .. $#data) {
- next unless defined $data[$i];
- if ($attrs[$i][0] eq 'ob_comment' && length ($data[$i]) > 79 - 17) {
+ for my $i (0 .. $#attrs) {
+ my $field = $attrs[$i][0];
+ my $fieldtext = $attrs[$i][1];
+ next unless my $value = $object_rs->get_column ($field);
+
+ if ($field eq 'ob_comment' && length ($value) > 79 - 17) {
local $Text::Wrap::columns = 80;
local $Text::Wrap::unexpand = 0;
- $data[$i] = wrap (' ' x 17, ' ' x 17, $data[$i]);
- $data[$i] =~ s/^ {17}//;
+ $value = wrap (' ' x 17, ' ' x 17, $value);
+ $value =~ s/^ {17}//;
}
- if ($attrs[$i][0] eq 'ob_created_by') {
+ if ($field eq 'ob_created_by') {
my @flags = $self->flag_list;
if (not @flags and $self->error) {
return;
@@ -631,15 +657,14 @@ sub show {
}
$output .= $attr_output;
}
- next unless defined $data[$i];
- if ($attrs[$i][0] =~ /^ob_(owner|acl_)/) {
- my $acl = eval { Wallet::ACL->new ($data[$i], $self->{dbh}) };
+ if ($field =~ /^ob_(owner|acl_)/) {
+ my $acl = eval { Wallet::ACL->new ($value, $self->{dbh}) };
if ($acl and not $@) {
- $data[$i] = $acl->name || $data[$i];
- push (@acls, [ $acl, $data[$i] ]);
+ $value = $acl->name || $value;
+ push (@acls, [ $acl, $value ]);
}
}
- $output .= sprintf ("%15s: %s\n", $attrs[$i][1], $data[$i]);
+ $output .= sprintf ("%15s: %s\n", $fieldtext, $value);
}
if (@acls) {
my %seen;
@@ -663,20 +688,31 @@ sub destroy {
$self->error ("cannot destroy ${type}:${name}: object is locked");
return;
}
+ my $guard = $self->{dbh}->txn_scope_guard;
eval {
- my $date = strftime ('%Y-%m-%d %T', localtime $time);
- my $sql = 'delete from flags where fl_type = ? and fl_name = ?';
- $self->{dbh}->do ($sql, undef, $type, $name);
- $sql = 'delete from objects where ob_type = ? and ob_name = ?';
- $self->{dbh}->do ($sql, undef, $type, $name);
- $sql = "insert into object_history (oh_type, oh_name, oh_action,
- oh_by, oh_from, oh_on) values (?, ?, 'destroy', ?, ?, ?)";
- $self->{dbh}->do ($sql, undef, $type, $name, $user, $host, $date);
- $self->{dbh}->commit;
+
+ # Remove any flags that may exist for the record.
+ my %search = (fl_type => $type,
+ fl_name => $name);
+ $self->{dbh}->resultset('Flag')->search (\%search)->delete;
+
+ # Remove any object records
+ %search = (ob_type => $type,
+ ob_name => $name);
+ $self->{dbh}->resultset('Object')->search (\%search)->delete;
+
+ # And create a new history object for the destroy action.
+ my %record = (oh_type => $type,
+ oh_name => $name,
+ oh_action => 'destroy',
+ oh_by => $user,
+ oh_from => $host,
+ oh_on => strftime ('%Y-%m-%d %T', localtime $time));
+ $self->{dbh}->resultset('ObjectHistory')->create (\%record);
+ $guard->commit;
};
if ($@) {
$self->error ("cannot destroy ${type}:${name}: $@");
- $self->{dbh}->rollback;
return;
}
return 1;
@@ -733,7 +769,7 @@ such object exits, throws an exception. Otherwise, returns an object
blessed into the class used for the new() call (so subclasses can leave
this method alone and not override it).
-Takes a Wallet::Database object, which is stored in the object and used
+Takes a Wallet::Schema object, which is stored in the object and used
for any further operations.
=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME])
diff --git a/perl/Wallet/Object/Keytab.pm b/perl/Wallet/Object/Keytab.pm
index fd3001f..083dae6 100644
--- a/perl/Wallet/Object/Keytab.pm
+++ b/perl/Wallet/Object/Keytab.pm
@@ -40,21 +40,29 @@ sub enctypes_set {
my @trace = ($user, $host, $time);
my $name = $self->{name};
my %enctypes = map { $_ => 1 } @$enctypes;
+ my $guard = $self->{dbh}->txn_scope_guard;
eval {
- my $sql = 'select ke_enctype from keytab_enctypes where ke_name = ?';
- my $sth = $self->{dbh}->prepare ($sql);
- $sth->execute ($name);
- my (@current, $entry);
- while (defined ($entry = $sth->fetchrow_arrayref)) {
- push (@current, @$entry);
+
+ # Find all enctypes for the given keytab.
+ my %search = (ke_name => $name);
+ my @enctypes = $self->{dbh}->resultset('KeytabEnctype')
+ ->search (\%search);
+ my (@current);
+ for my $enctype_rs (@enctypes) {
+ push (@current, $enctype_rs->ke_enctype);
}
+
+ # Use the existing enctypes and the enctypes we should have to match
+ # against ones that need to be removed, and note those that already
+ # exist.
for my $enctype (@current) {
if ($enctypes{$enctype}) {
delete $enctypes{$enctype};
} else {
- $sql = 'delete from keytab_enctypes where ke_name = ? and
- ke_enctype = ?';
- $self->{dbh}->do ($sql, undef, $name, $enctype);
+ %search = (ke_name => $name,
+ ke_enctype => $enctype);
+ $self->{dbh}->resultset('KeytabEnctype')->find (\%search)
+ ->delete;
$self->log_set ('type_data enctypes', $enctype, undef, @trace);
}
}
@@ -64,21 +72,20 @@ sub enctypes_set {
# doesn't enforce integrity constraints. We do this in sorted order
# to make it easier to test.
for my $enctype (sort keys %enctypes) {
- $sql = 'select en_name from enctypes where en_name = ?';
- my $status = $self->{dbh}->selectrow_array ($sql, undef, $enctype);
- unless ($status) {
+ my %search = (en_name => $enctype);
+ my $enctype_rs = $self->{dbh}->('Enctype')->find (\%search);
+ unless (defined $enctype_rs) {
die "unknown encryption type $enctype\n";
}
- $sql = 'insert into keytab_enctypes (ke_name, ke_enctype) values
- (?, ?)';
- $self->{dbh}->do ($sql, undef, $name, $enctype);
+ my %record = (ke_name => $name,
+ ke_enctype => $enctype);
+ $self->{dbh}->resultset('Enctype')->create (\%record);
$self->log_set ('type_data enctypes', undef, $enctype, @trace);
}
- $self->{dbh}->commit;
+ $guard->commit;
};
if ($@) {
$self->error ($@);
- $self->{dbh}->rollback;
return;
}
return 1;
@@ -92,19 +99,16 @@ sub enctypes_list {
my ($self) = @_;
my @enctypes;
eval {
- my $sql = 'select ke_enctype from keytab_enctypes where ke_name = ?
- order by ke_enctype';
- my $sth = $self->{dbh}->prepare ($sql);
- $sth->execute ($self->{name});
- my $entry;
- while (defined ($entry = $sth->fetchrow_arrayref)) {
- push (@enctypes, @$entry);
+ my %search = (ke_name => $self->{name});
+ my %attrs = (order_by => 'ke_enctype');
+ my @enctypes_rs = $self->{dbh}->resultset('KeytabEnctype')
+ ->search (\%search, \%attrs);
+ for my $enctype_rs (@enctypes_rs) {
+ push (@enctypes, $enctype_rs->ke_enctype);
}
- $self->{dbh}->commit;
};
if ($@) {
$self->error ($@);
- $self->{dbh}->rollback;
return;
}
return @enctypes;
@@ -132,21 +136,21 @@ sub sync_set {
$self->error ("unsupported synchronization target $target");
return;
} else {
+ my $guard = $self->{dbh}->txn_scope_guard;
eval {
- my $sql = 'select ks_target from keytab_sync where ks_name = ?';
- my $dbh = $self->{dbh};
my $name = $self->{name};
- my ($result) = $dbh->selectrow_array ($sql, undef, $name);
- if ($result) {
- my $sql = 'delete from keytab_sync where ks_name = ?';
- $self->{dbh}->do ($sql, undef, $name);
- $self->log_set ('type_data sync', $result, undef, @trace);
+ my %search = (ks_name => $name);
+ my $sync_rs = $self->dbh->resultset('KeytabSync')
+ ->search (\%search);
+ if (defined $sync_rs) {
+ my $target = $sync_rs->ks_target;
+ $sync_rs->delete;
+ $self->log_set ('type_data sync', $target, undef, @trace);
}
- $self->{dbh}->commit;
+ $guard->commit;
};
if ($@) {
$self->error ($@);
- $self->{dbh}->rollback;
return;
}
}
@@ -161,19 +165,16 @@ sub sync_list {
my ($self) = @_;
my @targets;
eval {
- my $sql = 'select ks_target from keytab_sync where ks_name = ?
- order by ks_target';
- my $sth = $self->{dbh}->prepare ($sql);
- $sth->execute ($self->{name});
- my $target;
- while (defined ($target = $sth->fetchrow_array)) {
- push (@targets, $target);
+ my %search = (ks_name => $self->{name});
+ my %attrs = (order_by => 'ks_target');
+ my @syncs = $self->dbh->resultset('KeytabSync')->search (\%search,
+ \%attrs);
+ for my $sync_rs (@syncs) {
+ push (@targets, $sync_rs->ks_target);
}
- $self->{dbh}->commit;
};
if ($@) {
$self->error ($@);
- $self->{dbh}->rollback;
return;
}
return @targets;
@@ -247,11 +248,6 @@ sub new {
my $kadmin = Wallet::Kadmin->new ();
$self->{kadmin} = $kadmin;
- # Set a callback for things to do after a fork, specifically for the MIT
- # kadmin module which forks to kadmin.
- my $callback = sub { $self->{dbh}->{InactiveDestroy} = 1 };
- $kadmin->fork_callback ($callback);
-
$self = $class->SUPER::new ($type, $name, $dbh);
$self->{kadmin} = $kadmin;
return $self;
@@ -271,11 +267,6 @@ sub create {
my $kadmin = Wallet::Kadmin->new ();
$self->{kadmin} = $kadmin;
- # Set a callback for things to do after a fork, specifically for the MIT
- # kadmin module which forks to kadmin.
- my $callback = sub { $self->{dbh}->{InactiveDestroy} = 1 };
- $kadmin->fork_callback ($callback);
-
if (not $kadmin->create ($name)) {
die $kadmin->error, "\n";
}
@@ -292,16 +283,21 @@ sub destroy {
$self->error ("cannot destroy $id: object is locked");
return;
}
+ my $dbh = $self->{dbh};
+ my $guard = $dbh->txn_scope_guard;
eval {
- my $sql = 'delete from keytab_sync where ks_name = ?';
- $self->{dbh}->do ($sql, undef, $self->{name});
- $sql = 'delete from keytab_enctypes where ke_name = ?';
- $self->{dbh}->do ($sql, undef, $self->{name});
- $self->{dbh}->commit;
+ my %search = (ks_name => $self->{name});
+ my $sync_rs = $dbh->resultset('KeytabSync')->search (\%search);
+ $sync_rs->delete_all if defined $sync_rs;
+
+ %search = (ke_name => $self->{name});
+ my $enctype_rs = $dbh->resultset('KeytabEnctype')->search (\%search);
+ $enctype_rs->delete_all if defined $enctype_rs;
+
+ $guard->commit;
};
if ($@) {
$self->error ($@);
- $self->{dbh}->rollback;
return;
}
my $kadmin = $self->{kadmin};
diff --git a/perl/Wallet/Report.pm b/perl/Wallet/Report.pm
index 5a8dc52..ea8cd2f 100644
--- a/perl/Wallet/Report.pm
+++ b/perl/Wallet/Report.pm
@@ -16,12 +16,12 @@ use strict;
use vars qw($VERSION);
use Wallet::ACL;
-use Wallet::Database;
+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.03';
+$VERSION = '0.04';
##############################################################################
# Constructor, destructor, and accessors
@@ -32,7 +32,7 @@ $VERSION = '0.03';
# exception if anything goes wrong.
sub new {
my ($class) = @_;
- my $dbh = Wallet::Database->connect;
+ my $dbh = Wallet::Schema->connect;
my $self = { dbh => $dbh };
bless ($self, $class);
return $self;
@@ -59,7 +59,7 @@ sub error {
# Disconnect the database handle on object destruction to avoid warnings.
sub DESTROY {
my ($self) = @_;
- $self->{dbh}->disconnect unless $self->{dbh}->{InactiveDestroy};
+ $self->{dbh}->storage->dbh->disconnect;
}
##############################################################################
@@ -69,18 +69,26 @@ sub DESTROY {
# Return the SQL statement to find every object in the database.
sub objects_all {
my ($self) = @_;
- my $sql = 'select ob_type, ob_name from objects order by ob_type,
- ob_name';
- return $sql;
+ 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 $sql = 'select ob_type, ob_name from objects where ob_type=? order
- by ob_type, ob_name';
- return ($sql, $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
@@ -89,28 +97,36 @@ sub objects_type {
# match any ACLs, set an error and return undef.
sub objects_owner {
my ($self, $owner) = @_;
- my ($sth);
+ my @objects;
+
+ my %search;
+ my %options = (order_by => [ qw/ob_type ob_name/ ],
+ select => [ qw/ob_type ob_name/ ]);
+
if (lc ($owner) eq 'null') {
- my $sql = 'select ob_type, ob_name from objects where ob_owner is null
- order by objects.ob_type, objects.ob_name';
- return ($sql);
+ %search = (ob_owner => undef);
} else {
my $acl = eval { Wallet::ACL->new ($owner, $self->{dbh}) };
return unless $acl;
- my $sql = 'select ob_type, ob_name from objects where ob_owner = ?
- order by objects.ob_type, objects.ob_name';
- return ($sql, $acl->id);
+ %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 $sql = 'select ob_type, ob_name from objects left join flags on
- (objects.ob_type = flags.fl_type and objects.ob_name = flags.fl_name)
- where flags.fl_flag = ? order by objects.ob_type, objects.ob_name';
- return ($sql, $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
@@ -120,22 +136,35 @@ sub objects_flag {
# set an error and return the empty string.
sub objects_acl {
my ($self, $search) = @_;
- my $acl = eval { Wallet::ACL->new ($search, $self->{dbh}) };
+ my @objects;
+
+ my $dbh = $self->{dbh};
+ my $acl = eval { Wallet::ACL->new ($search, $dbh) };
return unless $acl;
- my $sql = 'select ob_type, ob_name from objects where ob_owner = ? or
- ob_acl_get = ? or ob_acl_store = ? or ob_acl_show = ? or
- ob_acl_destroy = ? or ob_acl_flags = ? order by objects.ob_type,
- objects.ob_name';
- return ($sql, ($acl->id) x 6);
+
+ 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 $sql = 'select ob_type, ob_name from objects where ob_downloaded_on
- is null order by objects.ob_type, objects.ob_name';
- return ($sql);
+ 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
@@ -148,46 +177,44 @@ sub objects {
my ($self, $type, @args) = @_;
undef $self->{error};
- # Find the SQL statement and the arguments to use.
- my $sql = '';
- my @search = ();
+ # Get the search and options array refs from specific functions.
+ my ($search_ref, $options_ref);
if (!defined $type || $type eq '') {
- ($sql) = $self->objects_all;
+ ($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') {
- ($sql, @search) = $self->objects_type (@args);
+ ($search_ref, $options_ref) = $self->objects_type (@args);
} elsif ($type eq 'owner') {
- ($sql, @search) = $self->objects_owner (@args);
+ ($search_ref, $options_ref) = $self->objects_owner (@args);
} elsif ($type eq 'flag') {
- ($sql, @search) = $self->objects_flag (@args);
+ ($search_ref, $options_ref) = $self->objects_flag (@args);
} elsif ($type eq 'acl') {
- ($sql, @search) = $self->objects_acl (@args);
+ ($search_ref, $options_ref) = $self->objects_acl (@args);
} elsif ($type eq 'unused') {
- ($sql) = $self->objects_unused (@args);
+ ($search_ref, $options_ref) = $self->objects_unused (@args);
} else {
$self->error ("do not know search type: $type");
}
- return unless $sql;
+ return unless $search_ref;
}
- # Do the search.
+ # Perform the search and return on any errors.
my @objects;
+ my $dbh = $self->{dbh};
eval {
- my $sth = $self->{dbh}->prepare ($sql);
- $sth->execute (@search);
- my $object;
- while (defined ($object = $sth->fetchrow_arrayref)) {
- push (@objects, [ @$object ]);
+ my @objects_rs = $dbh->resultset('Object')->search ($search_ref,
+ $options_ref);
+ for my $object_rs (@objects_rs) {
+ push (@objects, [ $object_rs->ob_type, $object_rs->ob_name ]);
}
- $self->{dbh}->commit;
};
if ($@) {
$self->error ("cannot list objects: $@");
- $self->{dbh}->rollback;
return;
}
+
return @objects;
}
@@ -199,17 +226,51 @@ sub objects {
# database.
sub acls_all {
my ($self) = @_;
- my $sql = 'select ac_id, ac_name from acls order by ac_id';
- return ($sql);
+ my @acls;
+
+ my $dbh = $self->{dbh};
+ my %search = ();
+ my %options = (order_by => [ qw/ac_id/ ],
+ select => [ qw/ac_id ac_name/ ]);
+
+ eval {
+ my @acls_rs = $dbh->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 $sql = 'select ac_id, ac_name from acls left join acl_entries
- on (acls.ac_id = acl_entries.ae_id) where ae_id is null order by
- ac_id';
- return ($sql);
+ my @acls;
+
+ my $dbh = $self->{dbh};
+ 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 = $dbh->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
@@ -217,22 +278,69 @@ sub acls_empty {
# do a substring search.
sub acls_entry {
my ($self, $type, $identifier) = @_;
- my $sql = 'select distinct ac_id, ac_name from acl_entries left join acls
- on (ae_id = ac_id) where ae_scheme = ? and ae_identifier like ? order
- by ac_id';
- return ($sql, $type, '%' . $identifier . '%');
+ my @acls;
+
+ my $dbh = $self->{dbh};
+ 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 = $dbh->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 $sql = 'select ac_id, ac_name from acls where not ac_id in (select
- ob_owner from objects where ob_owner = ac_id)';
- for my $acl (qw/get store show destroy flags/) {
- $sql .= " and not ac_id in (select ob_acl_$acl from objects where
- ob_acl_$acl = ac_id)";
+ my @acls;
+
+ my $dbh = $self->{dbh};
+ 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 = $dbh->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 ($sql);
+ return (@acls);
}
# Obtain a textual representation of the membership of an ACL, returning undef
@@ -290,11 +398,10 @@ sub acls {
my ($self, $type, @args) = @_;
undef $self->{error};
- # Find the SQL statement and the arguments to use.
- my $sql;
- my @search = ();
+ # Find the ACLs for any given search.
+ my @acls;
if (!defined $type || $type eq '') {
- ($sql) = $self->acls_all;
+ @acls = $self->acls_all;
} else {
if ($type eq 'duplicate') {
return $self->acls_duplicate;
@@ -303,34 +410,17 @@ sub acls {
$self->error ('ACL searches require an argument to search');
return;
} else {
- ($sql, @search) = $self->acls_entry (@args);
+ @acls = $self->acls_entry (@args);
}
} elsif ($type eq 'empty') {
- ($sql) = $self->acls_empty;
+ @acls = $self->acls_empty;
} elsif ($type eq 'unused') {
- ($sql) = $self->acls_unused;
+ @acls = $self->acls_unused;
} else {
$self->error ("unknown search type: $type");
return;
}
}
-
- # Do the search.
- my @acls;
- eval {
- my $sth = $self->{dbh}->prepare ($sql);
- $sth->execute (@search);
- my $object;
- while (defined ($object = $sth->fetchrow_arrayref)) {
- push (@acls, [ @$object ]);
- }
- $self->{dbh}->commit;
- };
- if ($@) {
- $self->error ("cannot list ACLs: $@");
- $self->{dbh}->rollback;
- return;
- }
return @acls;
}
@@ -343,26 +433,32 @@ sub acls {
sub owners {
my ($self, $type, $name) = @_;
undef $self->{error};
- my @lines;
+ my $dbh = $self->{dbh};
+
+ my @owners;
eval {
- my $sql = 'select distinct ae_scheme, ae_identifier from acl_entries,
- acls, objects where ae_id = ac_id and ac_id = ob_owner and
- ob_type like ? and ob_name like ? order by ae_scheme,
- ae_identifier';
- my $sth = $self->{dbh}->prepare ($sql);
- $sth->execute ($type, $name);
- my $object;
- while (defined ($object = $sth->fetchrow_arrayref)) {
- push (@lines, [ @$object ]);
+ 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 = $dbh->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 ]);
}
- $self->{dbh}->commit;
};
if ($@) {
$self->error ("cannot report on owners: $@");
- $self->{dbh}->rollback;
return;
}
- return @lines;
+ return @owners;
}
##############################################################################
diff --git a/perl/Wallet/Schema.pm b/perl/Wallet/Schema.pm
index 9a7fe44..d36b7ac 100644
--- a/perl/Wallet/Schema.pm
+++ b/perl/Wallet/Schema.pm
@@ -1,262 +1,85 @@
-# Wallet::Schema -- Database schema for the wallet system.
-#
-# Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2007, 2008, 2010, 2011
-# The Board of Trustees of the Leland Stanford Junior University
-#
-# See LICENSE for licensing terms.
-
-##############################################################################
-# Modules and declarations
-##############################################################################
-
package Wallet::Schema;
-require 5.006;
use strict;
-use vars qw(@SQL @TABLES $VERSION);
+use warnings;
-use DBI;
+use Wallet::Config;
+
+use base 'DBIx::Class::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.07';
+our $VERSION = '0.08';
+
+__PACKAGE__->load_namespaces;
+__PACKAGE__->load_components (qw/Schema::Versioned/);
##############################################################################
-# Data manipulation
+# Core overrides
##############################################################################
-# Create a new Wallet::Schema object, parse the SQL out of the documentation,
-# and store it in the object. We have to store the SQL in a static variable,
-# since we can't read DATA multiple times.
-sub new {
+# Override DBI::connect to supply our own connect string, username, and
+# password and to set some standard options. Takes no arguments other than
+# the implicit class argument.
+sub connect {
my ($class) = @_;
- unless (@SQL) {
- local $_;
- my $found;
- my $command = '';
- while (<DATA>) {
- if (not $found and /^=head1 SCHEMA/) {
- $found = 1;
- } elsif ($found and /^=head1 /) {
- last;
- } elsif ($found and /^ /) {
- s/^ //;
- $command .= $_;
- if (/;$/) {
- push (@SQL, $command);
- $command = '';
- }
- }
- }
- close DATA;
+ unless ($Wallet::Config::DB_DRIVER
+ and (defined ($Wallet::Config::DB_INFO)
+ or defined ($Wallet::Config::DB_NAME))) {
+ die "database connection information not configured\n";
}
- my $self = { sql => [ @SQL ] };
- bless ($self, $class);
- return $self;
-}
-
-# Returns the SQL as a list of commands.
-sub sql {
- my ($self) = @_;
- return @{ $self->{sql} };
-}
-
-##############################################################################
-# Initialization and cleanup
-##############################################################################
-
-# Run a set of SQL commands, forcing a transaction, rolling back on error, and
-# throwing an exception if anything fails.
-sub _run_sql {
- my ($self, $dbh, @sql) = @_;
- eval {
- $dbh->begin_work if $dbh->{AutoCommit};
- for my $sql (@sql) {
- $dbh->do ($sql, { RaiseError => 1, PrintError => 0 });
- }
- $dbh->commit;
- };
- if ($@) {
- $dbh->rollback;
- die "$@\n";
+ my $dsn = "DBI:$Wallet::Config::DB_DRIVER:";
+ if (defined $Wallet::Config::DB_INFO) {
+ $dsn .= $Wallet::Config::DB_INFO;
+ } else {
+ $dsn .= "database=$Wallet::Config::DB_NAME";
+ $dsn .= ";host=$Wallet::Config::DB_HOST" if $Wallet::Config::DB_HOST;
+ $dsn .= ";port=$Wallet::Config::DB_PORT" if $Wallet::Config::DB_PORT;
}
-}
-
-# Given a database handle, try to create our database by running the SQL. Do
-# this in a transaction regardless of the database settings and throw an
-# exception if this fails. We have to do a bit of fiddling to get syntax that
-# works with both MySQL and SQLite.
-sub create {
- my ($self, $dbh) = @_;
- my $driver = $dbh->{Driver}->{Name};
- my @create = map {
- if ($driver eq 'SQLite') {
- s/auto_increment primary key/primary key autoincrement/;
- } elsif ($driver eq 'mysql' and /^\s*create\s+table\s/) {
- s/;$/ engine=InnoDB;/;
- }
- $_;
- } @{ $self->{sql} };
- $self->_run_sql ($dbh, @create);
-}
-
-# Given a database handle, try to remove the wallet database tables by
-# reversing the SQL. Do this in a transaction regardless of the database
-# settings and throw an exception if this fails.
-sub drop {
- my ($self, $dbh) = @_;
- my @drop = map {
- if (/^\s*create\s+table\s+(\S+)/i) {
- "drop table if exists $1;";
- } else {
- ();
- }
- } reverse @{ $self->{sql} };
- $self->_run_sql ($dbh, @drop);
-}
-
-# Given an open database handle, determine the current database schema
-# version. If we can't read the version number, we currently assume a version
-# 0 database. This will change in the future.
-sub _schema_version {
- my ($self, $dbh) = @_;
- my $version;
- eval {
- my $sql = 'select md_version from metadata';
- my $result = $dbh->selectrow_arrayref ($sql);
- $version = $result->[0];
- };
+ my $user = $Wallet::Config::DB_USER;
+ my $pass = $Wallet::Config::DB_PASSWORD;
+ my %attrs = (PrintError => 0, RaiseError => 1);
+ my $dbh = eval { $class->SUPER::connect ($dsn, $user, $pass, \%attrs) };
if ($@) {
- $version = 0;
+ die "cannot connect to database: $@\n";
}
- return $version;
+ return $dbh;
}
-# Given a database handle, try to upgrade the schema of that database to the
-# current version while preserving all data. Do this in a transaction
-# regardless of the database settings and throw an exception if this fails.
-sub upgrade {
- my ($self, $dbh) = @_;
- my $version = $self->_schema_version ($dbh);
- my @sql;
- if ($version == 1) {
- return;
- } elsif ($version == 0) {
- @sql = ('create table metadata (md_version integer)',
- 'insert into metadata (md_version) values (1)',
- 'alter table objects add ob_comment varchar(255) default null'
- );
- } else {
- die "unknown database version $version\n";
- }
- $self->_run_sql ($dbh, @sql);
-}
+__END__
+
+1;
##############################################################################
-# Schema
+# Documentation
##############################################################################
-# The following POD is also parsed by the code to extract SQL blocks. Don't
-# add any verbatim blocks to this documentation in the SCHEMA section that
-# aren't intended to be SQL.
-
-1;
-__DATA__
-
=head1 NAME
-Wallet::Schema - Database schema for the wallet system
-
-=for stopwords
-SQL ACL API APIs enums Enums Keytab Backend keytab backend enctypes
-enctype Allbery Metadata metadata verifier
+Wallet::Schema - Database schema and connector for the wallet system
=head1 SYNOPSIS
use Wallet::Schema;
- my $schema = Wallet::Schema->new;
- my @sql = $schema->sql;
- $schema->create ($dbh);
+ my $dbh = Wallet::Schema->connect;
=head1 DESCRIPTION
This class encapsulates the database schema for the wallet system. The
-documentation you're reading explains and comments the schema. The Perl
-object extracts the schema from the documentation and can either return it
-as a list of SQL commands to run or run those commands given a connected
-database handle.
+documentation you're reading explains and comments the schema. The
+class runs using the DBIx::Class module.
-This schema attempts to be portable SQL, but it is designed for use with
-MySQL and may require some modifications for other databases.
-
-=head1 METHODS
-
-=over 4
-
-=item new()
-
-Instantiates a new Wallet::Schema object. This parses the documentation
-and extracts the schema, but otherwise doesn't do anything.
-
-=item create(DBH)
-
-Given a connected database handle, runs the SQL commands necessary to
-create the wallet database in an otherwise empty database. This method
-will not drop any existing tables and will therefore fail if a wallet
-database has already been created. On any error, this method will throw a
-database exception.
-
-=item drop(DBH)
-
-Given a connected database handle, drop all of the wallet tables from that
-database if any of those tables exist. This method will only remove
-tables that are part of the current schema or one of the previous known
-schema and won't remove other tables. On any error, this method will
-throw a database exception.
-
-=item sql()
-
-Returns the schema and the population of the normalization tables as a
-list of SQL commands to run to create the wallet database in an otherwise
-empty database.
-
-=item upgrade(DBH)
-
-Given a connected database handle, runs the SQL commands necessary to
-upgrade that database to the current schema version. On any error, this
-method will throw a database exception.
-
-=back
+connect() will obtain the database connection information from the wallet
+configuration; see L<Wallet::Config> for more details. It will also
+automatically set the RaiseError attribute to true and the PrintError and
+AutoCommit attributes to false, matching the assumptions made by the
+wallet database code.
=head1 SCHEMA
-=head2 Metadata Tables
-
-This table is used to store metadata about the wallet database, used for
-upgrades and in similar situations:
-
- create table metadata
- (md_version integer);
- insert into metadata (md_version) values (1);
-
-This table will normally only have one row. md_version holds the version
-number of the schema (which does not necessarily have any relationship to
-the version number of wallet itself).
-
=head2 Normalization Tables
-The following are normalization tables used to constrain the values in
-other tables.
-
-Holds the supported flag names:
-
- create table flag_names
- (fn_name varchar(32) primary key);
- insert into flag_names (fn_name) values ('locked');
- insert into flag_names (fn_name) values ('unchanging');
-
Holds the supported object types and their corresponding Perl classes:
create table types
@@ -390,8 +213,8 @@ object may have zero or more flags associated with it:
not null references objects(ob_type),
fl_name varchar(255)
not null references objects(ob_name),
- fl_flag varchar(32)
- not null references flag_names(fn_name),
+ fl_flag enum('locked', 'unchanging')
+ not null,
primary key (fl_type, fl_name, fl_flag));
create index fl_object on flags (fl_type, fl_name);
@@ -477,9 +300,22 @@ To use this functionality, you will need to populate the enctypes table
with the enctypes that a keytab may be restricted to. Currently, there is
no automated mechanism to do this.
+=head1 CLASS METHODS
+
+=over 4
+
+=item connect()
+
+Opens a new database connection and returns the database object. On any
+failure, throws an exception. Unlike the DBI method, connect() takes no
+arguments; all database connection information is derived from the wallet
+configuration.
+
+=back
+
=head1 SEE ALSO
-wallet-backend(8)
+wallet-backend(8), Wallet::Config(3)
This module is part of the wallet system. The current version is
available from L<http://www.eyrie.org/~eagle/software/wallet/>.
diff --git a/perl/Wallet/Schema/Result/Acl.pm b/perl/Wallet/Schema/Result/Acl.pm
new file mode 100644
index 0000000..60a357b
--- /dev/null
+++ b/perl/Wallet/Schema/Result/Acl.pm
@@ -0,0 +1,99 @@
+package Wallet::Schema::Result::Acl;
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+=head1 NAME
+
+Wallet::Schema::Result::Acl
+
+=head1 DESCRIPTION
+
+=cut
+
+__PACKAGE__->table("acls");
+
+=head1 ACCESSORS
+
+=head2 ac_id
+
+ data_type: 'integer'
+ is_auto_increment: 1
+ is_nullable: 0
+
+=head2 ac_name
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=cut
+
+__PACKAGE__->add_columns(
+ "ac_id",
+ { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+ "ac_name",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+);
+__PACKAGE__->set_primary_key("ac_id");
+__PACKAGE__->add_unique_constraint("ac_name", ["ac_name"]);
+
+__PACKAGE__->has_one(
+ 'acl_entries',
+ 'Wallet::Schema::Result::AclEntry',
+ { 'foreign.ae_id' => 'self.ac_id' },
+ { cascade_copy => 0, cascade_delete => 0 },
+ );
+__PACKAGE__->has_many(
+ 'acl_history',
+ 'Wallet::Schema::Result::AclHistory',
+ { 'foreign.ah_id' => 'self.ac_id' },
+ { cascade_copy => 0, cascade_delete => 0 },
+ );
+
+# References for all of the various potential ACLs in owners.
+__PACKAGE__->has_many(
+ 'acls_owner',
+ 'Wallet::Schema::Result::Object',
+ { 'foreign.ob_owner' => 'self.ac_id' },
+ );
+__PACKAGE__->has_many(
+ 'acls_get',
+ 'Wallet::Schema::Result::Object',
+ { 'foreign.ob_acl_get' => 'self.ac_id' },
+ );
+__PACKAGE__->has_many(
+ 'acls_store',
+ 'Wallet::Schema::Result::Object',
+ { 'foreign.ob_acl_store' => 'self.ac_id' },
+ );
+__PACKAGE__->has_many(
+ 'acls_show',
+ 'Wallet::Schema::Result::Object',
+ { 'foreign.ob_acl_show' => 'self.ac_id' },
+ );
+__PACKAGE__->has_many(
+ 'acls_destroy',
+ 'Wallet::Schema::Result::Object',
+ { 'foreign.ob_acl_destroy' => 'self.ac_id' },
+ );
+__PACKAGE__->has_many(
+ 'acls_flags',
+ 'Wallet::Schema::Result::Object',
+ { 'foreign.ob_acl_flags' => 'self.ac_id' },
+ );
+
+# Override the insert method so that we can automatically create history
+# items.
+#sub insert {
+# my ($self, @args) = @_;
+# my $ret = $self->next::method (@args);
+# print "ID: ".$self->ac_id."\n";
+# use Data::Dumper; print Dumper (@args);
+
+# return $self;
+#}
+
+1;
diff --git a/perl/Wallet/Schema/Result/AclEntry.pm b/perl/Wallet/Schema/Result/AclEntry.pm
new file mode 100644
index 0000000..99105a0
--- /dev/null
+++ b/perl/Wallet/Schema/Result/AclEntry.pm
@@ -0,0 +1,63 @@
+package Wallet::Schema::Result::AclEntry;
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+=head1 NAME
+
+Wallet::Schema::Result::AclEntry
+
+=head1 DESCRIPTION
+
+=cut
+
+__PACKAGE__->table("acl_entries");
+
+=head1 ACCESSORS
+
+=head2 ae_id
+
+ data_type: 'integer'
+ is_nullable: 0
+
+=head2 ae_scheme
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 32
+
+=head2 ae_identifier
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=cut
+
+__PACKAGE__->add_columns(
+ "ae_id",
+ { data_type => "integer", is_nullable => 0 },
+ "ae_scheme",
+ { data_type => "varchar", is_nullable => 0, size => 32 },
+ "ae_identifier",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+);
+__PACKAGE__->set_primary_key("ae_id", "ae_scheme", "ae_identifier");
+
+__PACKAGE__->belongs_to(
+ 'acls',
+ 'Wallet::Schema::Result::Acl',
+ { 'foreign.ac_id' => 'self.ae_id' },
+ { is_deferrable => 1, on_delete => 'CASCADE',
+ on_update => 'CASCADE' },
+ );
+
+__PACKAGE__->has_one(
+ 'acl_scheme',
+ 'Wallet::Schema::Result::AclScheme',
+ { 'foreign.as_name' => 'self.ae_scheme' },
+ { cascade_delete => 0 },
+ );
+1;
diff --git a/perl/Wallet/Schema/Result/AclHistory.pm b/perl/Wallet/Schema/Result/AclHistory.pm
new file mode 100644
index 0000000..2ad56ff
--- /dev/null
+++ b/perl/Wallet/Schema/Result/AclHistory.pm
@@ -0,0 +1,101 @@
+package Wallet::Schema::Result::AclHistory;
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime");
+
+=head1 NAME
+
+Wallet::Schema::Result::AclHistory
+
+=head1 DESCRIPTION
+
+=cut
+
+__PACKAGE__->table("acl_history");
+
+=head1 ACCESSORS
+
+=head2 ah_id
+
+ data_type: 'integer'
+ is_auto_increment: 1
+ is_nullable: 0
+
+=head2 ah_acl
+
+ data_type: 'integer'
+ is_nullable: 0
+
+=head2 ah_action
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 16
+
+=head2 ah_scheme
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 32
+
+=head2 ah_identifier
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 255
+
+=head2 ah_by
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=head2 ah_from
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=head2 ah_on
+
+ data_type: 'datetime'
+ datetime_undef_if_invalid: 1
+ is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+ "ah_id",
+ { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+ "ah_acl",
+ { data_type => "integer", is_nullable => 0 },
+ "ah_action",
+ { data_type => "varchar", is_nullable => 0, size => 16 },
+ "ah_scheme",
+ { data_type => "varchar", is_nullable => 1, size => 32 },
+ "ah_identifier",
+ { data_type => "varchar", is_nullable => 1, size => 255 },
+ "ah_by",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+ "ah_from",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+ "ah_on",
+ {
+ data_type => "datetime",
+ datetime_undef_if_invalid => 1,
+ is_nullable => 0,
+ },
+);
+__PACKAGE__->set_primary_key("ah_id");
+
+__PACKAGE__->might_have(
+ 'acls',
+ 'Wallet::Schema::Result::Acl',
+ { 'foreign.ac_id' => 'self.ah_id' },
+ );
+
+1;
diff --git a/perl/Wallet/Schema/Result/AclScheme.pm b/perl/Wallet/Schema/Result/AclScheme.pm
new file mode 100644
index 0000000..96db79d
--- /dev/null
+++ b/perl/Wallet/Schema/Result/AclScheme.pm
@@ -0,0 +1,73 @@
+package Wallet::Schema::Result::AclScheme;
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+__PACKAGE__->load_components (qw//);
+
+=head1 NAME
+
+Wallet::Schema::Result::AclScheme
+
+=head1 DESCRIPTION
+
+This is a normalization table used to constrain the values in other
+tables. It contains the types of ACL schemes that Wallet will
+recognize, and the modules that govern each of those schemes.
+
+By default it contains the following entries:
+
+ insert into acl_schemes (as_name, as_class)
+ values ('krb5', 'Wallet::ACL::Krb5');
+ insert into acl_schemes (as_name, as_class)
+ values ('krb5-regex', 'Wallet::ACL::Krb5::Regex');
+ insert into acl_schemes (as_name, as_class)
+ values ('ldap-attr', 'Wallet::ACL::LDAP::Attribute');
+ insert into acl_schemes (as_name, as_class)
+ values ('netdb', 'Wallet::ACL::NetDB');
+ insert into acl_schemes (as_name, as_class)
+ values ('netdb-root', 'Wallet::ACL::NetDB::Root');
+
+If you have extended the wallet to support additional ACL schemes, you
+will want to add additional rows to this table mapping those schemes
+to Perl classes that implement the ACL verifier APIs.
+
+=cut
+
+__PACKAGE__->table("acl_schemes");
+
+=head1 ACCESSORS
+
+=head2 as_name
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 32
+
+=head2 as_class
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 64
+
+=cut
+
+__PACKAGE__->add_columns(
+ "as_name",
+ { data_type => "varchar", is_nullable => 0, size => 32 },
+ "as_class",
+ { data_type => "varchar", is_nullable => 1, size => 64 },
+);
+__PACKAGE__->set_primary_key("as_name");
+
+#__PACKAGE__->resultset->populate ([
+# [ qw/as_name as_class/ ],
+# [ 'krb5', 'Wallet::ACL::Krb5' ],
+# [ 'krb5-regex', 'Wallet::ACL::Krb5::Regex' ],
+# [ 'ldap-attr', 'Wallet::ACL::LDAP::Attribute' ],
+# [ 'netdb', 'Wallet::ACL::NetDB' ],
+# [ 'netdb-root', 'Wallet::ACL::NetDB::Root' ],
+# ]);
+
+1;
diff --git a/perl/Wallet/Schema/Result/Enctype.pm b/perl/Wallet/Schema/Result/Enctype.pm
new file mode 100644
index 0000000..be41b84
--- /dev/null
+++ b/perl/Wallet/Schema/Result/Enctype.pm
@@ -0,0 +1,34 @@
+package Wallet::Schema::Result::Enctype;
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+=head1 NAME
+
+Wallet::Schema::Result::Enctype
+
+=head1 DESCRIPTION
+
+=cut
+
+__PACKAGE__->table("enctypes");
+
+=head1 ACCESSORS
+
+=head2 en_name
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=cut
+
+__PACKAGE__->add_columns(
+ "en_name",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+);
+__PACKAGE__->set_primary_key("en_name");
+
+1;
diff --git a/perl/Wallet/Schema/Result/Flag.pm b/perl/Wallet/Schema/Result/Flag.pm
new file mode 100644
index 0000000..b38e85f
--- /dev/null
+++ b/perl/Wallet/Schema/Result/Flag.pm
@@ -0,0 +1,54 @@
+package Wallet::Schema::Result::Flag;
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+=head1 NAME
+
+Wallet::Schema::Result::Flag
+
+=head1 DESCRIPTION
+
+=cut
+
+__PACKAGE__->table("flags");
+
+=head1 ACCESSORS
+
+=head2 fl_type
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 16
+
+=head2 fl_name
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=head2 fl_flag
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 32
+
+=cut
+
+__PACKAGE__->add_columns(
+ "fl_type" =>
+ { data_type => "varchar", is_nullable => 0, size => 16 },
+ "fl_name" =>
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+ "fl_flag" => {
+ data_type => 'enum',
+ is_enum => 1,
+ extra => { list => [qw/locked unchanging/] },
+ },
+);
+__PACKAGE__->set_primary_key("fl_type", "fl_name", "fl_flag");
+
+
+1;
diff --git a/perl/Wallet/Schema/Result/KeytabEnctype.pm b/perl/Wallet/Schema/Result/KeytabEnctype.pm
new file mode 100644
index 0000000..ae40c52
--- /dev/null
+++ b/perl/Wallet/Schema/Result/KeytabEnctype.pm
@@ -0,0 +1,42 @@
+package Wallet::Schema::Result::KeytabEnctype;
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+=head1 NAME
+
+Wallet::Schema::Result::KeytabEnctype
+
+=head1 DESCRIPTION
+
+=cut
+
+__PACKAGE__->table("keytab_enctypes");
+
+=head1 ACCESSORS
+
+=head2 ke_name
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=head2 ke_enctype
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=cut
+
+__PACKAGE__->add_columns(
+ "ke_name",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+ "ke_enctype",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+);
+__PACKAGE__->set_primary_key("ke_name", "ke_enctype");
+
+1;
diff --git a/perl/Wallet/Schema/Result/KeytabSync.pm b/perl/Wallet/Schema/Result/KeytabSync.pm
new file mode 100644
index 0000000..92ab6b8
--- /dev/null
+++ b/perl/Wallet/Schema/Result/KeytabSync.pm
@@ -0,0 +1,42 @@
+package Wallet::Schema::Result::KeytabSync;
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+=head1 NAME
+
+Wallet::Schema::Result::KeytabSync
+
+=head1 DESCRIPTION
+
+=cut
+
+__PACKAGE__->table("keytab_sync");
+
+=head1 ACCESSORS
+
+=head2 ks_name
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=head2 ks_target
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=cut
+
+__PACKAGE__->add_columns(
+ "ks_name",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+ "ks_target",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+);
+__PACKAGE__->set_primary_key("ks_name", "ks_target");
+
+1;
diff --git a/perl/Wallet/Schema/Result/Object.pm b/perl/Wallet/Schema/Result/Object.pm
new file mode 100644
index 0000000..17c51e2
--- /dev/null
+++ b/perl/Wallet/Schema/Result/Object.pm
@@ -0,0 +1,258 @@
+package Wallet::Schema::Result::Object;
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime");
+
+=head1 NAME
+
+Wallet::Schema::Result::Object
+
+=head1 DESCRIPTION
+
+=cut
+
+__PACKAGE__->table("objects");
+
+=head1 ACCESSORS
+
+=head2 ob_type
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 16
+
+=head2 ob_name
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=head2 ob_owner
+
+ data_type: 'integer'
+ is_nullable: 1
+
+=head2 ob_acl_get
+
+ data_type: 'integer'
+ is_nullable: 1
+
+=head2 ob_acl_store
+
+ data_type: 'integer'
+ is_nullable: 1
+
+=head2 ob_acl_show
+
+ data_type: 'integer'
+ is_nullable: 1
+
+=head2 ob_acl_destroy
+
+ data_type: 'integer'
+ is_nullable: 1
+
+=head2 ob_acl_flags
+
+ data_type: 'integer'
+ is_nullable: 1
+
+=head2 ob_expires
+
+ data_type: 'datetime'
+ datetime_undef_if_invalid: 1
+ is_nullable: 1
+
+=head2 ob_created_by
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=head2 ob_created_from
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=head2 ob_created_on
+
+ data_type: 'datetime'
+ datetime_undef_if_invalid: 1
+ is_nullable: 0
+
+=head2 ob_stored_by
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 255
+
+=head2 ob_stored_from
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 255
+
+=head2 ob_stored_on
+
+ data_type: 'datetime'
+ datetime_undef_if_invalid: 1
+ is_nullable: 1
+
+=head2 ob_downloaded_by
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 255
+
+=head2 ob_downloaded_from
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 255
+
+=head2 ob_downloaded_on
+
+ data_type: 'datetime'
+ datetime_undef_if_invalid: 1
+ is_nullable: 1
+
+=head2 ob_comment
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 255
+
+=cut
+
+__PACKAGE__->add_columns(
+ "ob_type",
+ { data_type => "varchar", is_nullable => 0, size => 16 },
+ "ob_name",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+ "ob_owner",
+ { data_type => "integer", is_nullable => 1 },
+ "ob_acl_get",
+ { data_type => "integer", is_nullable => 1 },
+ "ob_acl_store",
+ { data_type => "integer", is_nullable => 1 },
+ "ob_acl_show",
+ { data_type => "integer", is_nullable => 1 },
+ "ob_acl_destroy",
+ { data_type => "integer", is_nullable => 1 },
+ "ob_acl_flags",
+ { data_type => "integer", is_nullable => 1 },
+ "ob_expires",
+ {
+ data_type => "datetime",
+ datetime_undef_if_invalid => 1,
+ is_nullable => 1,
+ },
+ "ob_created_by",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+ "ob_created_from",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+ "ob_created_on",
+ {
+ data_type => "datetime",
+ datetime_undef_if_invalid => 1,
+ is_nullable => 0,
+ },
+ "ob_stored_by",
+ { data_type => "varchar", is_nullable => 1, size => 255 },
+ "ob_stored_from",
+ { data_type => "varchar", is_nullable => 1, size => 255 },
+ "ob_stored_on",
+ {
+ data_type => "datetime",
+ datetime_undef_if_invalid => 1,
+ is_nullable => 1,
+ },
+ "ob_downloaded_by",
+ { data_type => "varchar", is_nullable => 1, size => 255 },
+ "ob_downloaded_from",
+ { data_type => "varchar", is_nullable => 1, size => 255 },
+ "ob_downloaded_on",
+ {
+ data_type => "datetime",
+ datetime_undef_if_invalid => 1,
+ is_nullable => 1,
+ },
+ "ob_comment",
+ { data_type => "varchar", is_nullable => 1, size => 255 },
+);
+__PACKAGE__->set_primary_key("ob_name", "ob_type");
+
+__PACKAGE__->has_one(
+ 'types',
+ 'Wallet::Schema::Result::Type',
+ { 'foreign.ty_name' => 'self.ob_type' },
+ );
+
+__PACKAGE__->has_many(
+ 'flags',
+ 'Wallet::Schema::Result::Flag',
+ { 'foreign.fl_type' => 'self.ob_type',
+ 'foreign.fl_name' => 'self.ob_name' },
+ { cascade_copy => 0, cascade_delete => 0 },
+ );
+
+__PACKAGE__->has_many(
+ 'object_history',
+ 'Wallet::Schema::Result::ObjectHistory',
+ { 'foreign.oh_type' => 'self.ob_type',
+ 'foreign.oh_name' => 'self.ob_name' },
+ { cascade_copy => 0, cascade_delete => 0 },
+ );
+
+__PACKAGE__->has_many(
+ 'keytab_enctypes',
+ 'Wallet::Schema::Result::KeytabEnctype',
+ { 'foreign.ke_name' => 'self.ob_name' },
+ { cascade_copy => 0, cascade_delete => 0 },
+ );
+
+__PACKAGE__->has_many(
+ 'keytab_sync',
+ 'Wallet::Schema::Result::KeytabSync',
+ { 'foreign.ks_name' => 'self.ob_name' },
+ { cascade_copy => 0, cascade_delete => 0 },
+ );
+
+# References for all of the various potential ACLs.
+__PACKAGE__->belongs_to(
+ 'acls_owner',
+ 'Wallet::Schema::Result::Acl',
+ { 'foreign.ac_id' => 'self.ob_owner' },
+ );
+__PACKAGE__->belongs_to(
+ 'acls_get',
+ 'Wallet::Schema::Result::Acl',
+ { 'foreign.ac_id' => 'self.ob_acl_get' },
+ );
+__PACKAGE__->belongs_to(
+ 'acls_store',
+ 'Wallet::Schema::Result::Acl',
+ { 'foreign.ac_id' => 'self.ob_acl_store' },
+ );
+__PACKAGE__->belongs_to(
+ 'acls_show',
+ 'Wallet::Schema::Result::Acl',
+ { 'foreign.ac_id' => 'self.ob_acl_show' },
+ );
+__PACKAGE__->belongs_to(
+ 'acls_destroy',
+ 'Wallet::Schema::Result::Acl',
+ { 'foreign.ac_id' => 'self.ob_acl_destroy' },
+ );
+__PACKAGE__->belongs_to(
+ 'acls_flags',
+ 'Wallet::Schema::Result::Acl',
+ { 'foreign.ac_id' => 'self.ob_acl_flags' },
+ );
+
+1;
diff --git a/perl/Wallet/Schema/Result/ObjectHistory.pm b/perl/Wallet/Schema/Result/ObjectHistory.pm
new file mode 100644
index 0000000..067712f
--- /dev/null
+++ b/perl/Wallet/Schema/Result/ObjectHistory.pm
@@ -0,0 +1,127 @@
+package Wallet::Schema::Result::ObjectHistory;
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime");
+
+=head1 NAME
+
+Wallet::Schema::Result::ObjectHistory
+
+=head1 DESCRIPTION
+
+=cut
+
+__PACKAGE__->table("object_history");
+
+=head1 ACCESSORS
+
+=head2 oh_id
+
+ data_type: 'integer'
+ is_auto_increment: 1
+ is_nullable: 0
+
+=head2 oh_type
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 16
+
+=head2 oh_name
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=head2 oh_action
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 16
+
+=head2 oh_field
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 16
+
+=head2 oh_type_field
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 255
+
+=head2 oh_old
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 255
+
+=head2 oh_new
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 255
+
+=head2 oh_by
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=head2 oh_from
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=head2 oh_on
+
+ data_type: 'datetime'
+ datetime_undef_if_invalid: 1
+ is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+ "oh_id",
+ { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+ "oh_type",
+ { data_type => "varchar", is_nullable => 0, size => 16 },
+ "oh_name",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+ "oh_action",
+ { data_type => "varchar", is_nullable => 0, size => 16 },
+ "oh_field",
+ { data_type => "varchar", is_nullable => 1, size => 16 },
+ "oh_type_field",
+ { data_type => "varchar", is_nullable => 1, size => 255 },
+ "oh_old",
+ { data_type => "varchar", is_nullable => 1, size => 255 },
+ "oh_new",
+ { data_type => "varchar", is_nullable => 1, size => 255 },
+ "oh_by",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+ "oh_from",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+ "oh_on",
+ {
+ data_type => "datetime",
+ datetime_undef_if_invalid => 1,
+ is_nullable => 0,
+ },
+);
+__PACKAGE__->set_primary_key("oh_id");
+
+__PACKAGE__->might_have(
+ 'objects',
+ 'Wallet::Schema::Result::Object',
+ { 'foreign.ob_type' => 'self.oh_type',
+ 'foreign.ob_name' => 'self.oh_name' },
+ );
+
+1;
diff --git a/perl/Wallet/Schema/Result/SyncTarget.pm b/perl/Wallet/Schema/Result/SyncTarget.pm
new file mode 100644
index 0000000..17f4320
--- /dev/null
+++ b/perl/Wallet/Schema/Result/SyncTarget.pm
@@ -0,0 +1,40 @@
+package Wallet::Schema::Result::SyncTarget;
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+=head1 NAME
+
+Wallet::Schema::Result::SyncTarget
+
+=head1 DESCRIPTION
+
+=cut
+
+__PACKAGE__->table("sync_targets");
+
+=head1 ACCESSORS
+
+=head2 st_name
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 255
+
+=cut
+
+__PACKAGE__->add_columns(
+ "st_name",
+ { data_type => "varchar", is_nullable => 0, size => 255 },
+);
+__PACKAGE__->set_primary_key("st_name");
+
+#__PACKAGE__->has_many(
+# 'keytab_sync',
+# 'Wallet::Schema::Result::KeytabSync',
+# { 'foreign.ks_target' => 'self.st_name' },
+# { cascade_copy => 0, cascade_delete => 0 },
+# );
+1;
diff --git a/perl/Wallet/Schema/Result/Type.pm b/perl/Wallet/Schema/Result/Type.pm
new file mode 100644
index 0000000..89fb4c3
--- /dev/null
+++ b/perl/Wallet/Schema/Result/Type.pm
@@ -0,0 +1,64 @@
+package Wallet::Schema::Result::Type;
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+=head1 NAME
+
+Wallet::Schema::Result::Type
+
+=head1 DESCRIPTION
+
+This is a normalization table used to constrain the values in other
+tables. It contains the types of wallet objects that are considered
+valid, and the modules that govern each.
+
+By default it contains the following entries:
+
+ insert into types (ty_name, ty_class)
+ values ('file', 'Wallet::Object::File');
+ insert into types (ty_name, ty_class)
+ values ('keytab', 'Wallet::Object::Keytab');
+
+If you have extended the wallet to support additional object types ,
+you will want to add additional rows to this table mapping those types
+to Perl classes that implement the object APIs.
+
+=cut
+
+__PACKAGE__->table("types");
+
+=head1 ACCESSORS
+
+=head2 ty_name
+
+ data_type: 'varchar'
+ is_nullable: 0
+ size: 16
+
+=head2 ty_class
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 64
+
+=cut
+
+__PACKAGE__->add_columns(
+ "ty_name",
+ { data_type => "varchar", is_nullable => 0, size => 16 },
+ "ty_class",
+ { data_type => "varchar", is_nullable => 1, size => 64 },
+);
+__PACKAGE__->set_primary_key("ty_name");
+
+#__PACKAGE__->has_many(
+# 'objects',
+# 'Wallet::Schema::Result::Object',
+# { 'foreign.ob_type' => 'self.ty_name' },
+# { cascade_copy => 0, cascade_delete => 0 },
+# );
+
+1;
diff --git a/perl/Wallet/Server.pm b/perl/Wallet/Server.pm
index dfb7dbb..402fbe0 100644
--- a/perl/Wallet/Server.pm
+++ b/perl/Wallet/Server.pm
@@ -18,13 +18,12 @@ use vars qw(%MAPPING $VERSION);
use Wallet::ACL;
use Wallet::Config;
-use Wallet::Database;
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.10';
+$VERSION = '0.11';
##############################################################################
# Utility methods
@@ -38,7 +37,7 @@ $VERSION = '0.10';
# for various things. Throw an exception if anything goes wrong.
sub new {
my ($class, $user, $host) = @_;
- my $dbh = Wallet::Database->connect;
+ my $dbh = Wallet::Schema->connect;
my $acl = Wallet::ACL->new ('ADMIN', $dbh);
my $self = {
dbh => $dbh,
@@ -71,8 +70,9 @@ sub error {
# Disconnect the database handle on object destruction to avoid warnings.
sub DESTROY {
my ($self) = @_;
- if ($self->{dbh} and not $self->{dbh}->{InactiveDestroy}) {
- $self->{dbh}->disconnect;
+
+ if ($self->{dbh}) {
+ $self->{dbh}->storage->dbh->disconnect;
}
}
@@ -86,13 +86,14 @@ sub type_mapping {
my ($self, $type) = @_;
my $class;
eval {
- my $sql = 'select ty_class from types where ty_name = ?';
- ($class) = $self->{dbh}->selectrow_array ($sql, undef, $type);
- $self->{dbh}->commit;
+ my $guard = $self->{dbh}->txn_scope_guard;
+ my %search = (ty_name => $type);
+ my $type_rec = $self->{dbh}->resultset('Type')->find (\%search);
+ $class = $type_rec->ty_class;
+ $guard->commit;
};
if ($@) {
$self->error ($@);
- $self->{dbh}->rollback;
return;
}
if (defined $class) {
diff --git a/perl/create-ddl b/perl/create-ddl
new file mode 100755
index 0000000..62deb86
--- /dev/null
+++ b/perl/create-ddl
@@ -0,0 +1,93 @@
+#!/usr/bin/perl -w
+#
+# create-ddl - Create DDL files for Wallet
+#
+# Written by Jon Robertson <jonrober@stanford.edu>
+# Copyright 2012 Board of Trustees, Leland Stanford Jr. University
+
+#############################################################################
+# Modules and declarations
+#############################################################################
+
+use strict;
+use vars qw();
+
+use Getopt::Long;
+use Wallet::Admin;
+
+#############################################################################
+# Main routine
+#############################################################################
+
+# Get errors and output in the same order.
+$| = 0;
+
+# Clean up the path name.
+my $fullpath = $0;
+$0 =~ s%^.*/%%;
+
+# Parse command-line options.
+my ($help);
+my $oldversion = '';
+Getopt::Long::config ('bundling');
+GetOptions ('h|help' => \$help,
+ 'o|oldversion=s' => \$oldversion) or exit 1;
+if ($help) {
+ print "Feeding myself to perldoc, please wait....\n";
+ exec ('perldoc', '-t', $fullpath);
+}
+
+# Default wallet settings, for Wallet::Admin.
+$Wallet::Config::DB_DDL_DIRECTORY = 'sql/';
+$Wallet::Config::DB_DRIVER = 'SQLite';
+$Wallet::Config::DB_INFO = 'wallet-db';
+
+# Create a Wallet::Admin object and run the backup.
+my $admin = Wallet::Admin->new;
+$admin->backup ($oldversion);
+
+exit(0);
+
+##############################################################################
+# Documentation
+##############################################################################
+
+=head1 NAME
+
+create-ddl - Create DDL files for Wallet
+
+=head1 SYNOPSIS
+
+create-ddl [B<--help>] [B<--oldversion>]
+
+=head1 DESCRIPTION
+
+create-ddl is used to create DDL files for the various DBIx::Class
+Wallet::Schema modules. It simply is an interface for the backup command
+in Wallet::Admin, which does the work via DBIx::Class. The end result
+is a number of files that can be used to load the database for each
+supported database server.
+
+These files can be modified after creation to customize the database
+load, though should only be done when necessary to prevent confusion
+for the schema modules not matching the actual table definitions. This
+is currently only done in the case of SQLite databases, due to the
+SQLite parser creating keys without AUTOINCREMENT.
+
+=head1 OPTIONS
+
+B<--help>
+
+Prints the perldoc information (this document) for the script.
+
+B<--oldversion>=<version>
+
+The version number of the previous version. If there are existing DDL
+files for this version, then we will also create diff files to upgrade
+a database from the old version to the current.
+
+=head1 AUTHORS
+
+Jon Robertson <jonrober@stanford.edu>
+
+=cut
diff --git a/perl/sql/Wallet-Schema-0.07-0.08-MySQL.sql b/perl/sql/Wallet-Schema-0.07-0.08-MySQL.sql
new file mode 100644
index 0000000..ed0bde1
--- /dev/null
+++ b/perl/sql/Wallet-Schema-0.07-0.08-MySQL.sql
@@ -0,0 +1,7 @@
+BEGIN;
+ALTER TABLE flags MODIFY `fl_flag` enum('locked', 'unchanging') NOT NULL;
+DROP TABLE IF EXISTS flag_names;
+DROP TABLE IF EXISTS metadata;
+ALTER TABLE objects ADD ob_comment varchar(255) default null;
+COMMIT;
+
diff --git a/perl/sql/Wallet-Schema-0.07-0.08-SQLite.sql b/perl/sql/Wallet-Schema-0.07-0.08-SQLite.sql
new file mode 100644
index 0000000..3e600b0
--- /dev/null
+++ b/perl/sql/Wallet-Schema-0.07-0.08-SQLite.sql
@@ -0,0 +1,6 @@
+BEGIN;
+DROP TABLE IF EXISTS flag_names;
+DROP TABLE IF EXISTS metadata;
+ALTER TABLE objects ADD ob_comment varchar(255) default null;
+COMMIT;
+
diff --git a/perl/sql/Wallet-Schema-0.07-MySQL.sql b/perl/sql/Wallet-Schema-0.07-MySQL.sql
new file mode 100644
index 0000000..1bd38b3
--- /dev/null
+++ b/perl/sql/Wallet-Schema-0.07-MySQL.sql
@@ -0,0 +1,211 @@
+--
+-- Created by SQL::Translator::Producer::MySQL
+-- Created on Fri Jan 25 14:12:02 2013
+--
+SET foreign_key_checks=0;
+
+DROP TABLE IF EXISTS `acl_history`;
+
+--
+-- Table: `acl_history`
+--
+CREATE TABLE `acl_history` (
+ `ah_id` integer NOT NULL auto_increment,
+ `ah_acl` integer NOT NULL,
+ `ah_action` varchar(16) NOT NULL,
+ `ah_scheme` varchar(32),
+ `ah_identifier` varchar(255),
+ `ah_by` varchar(255) NOT NULL,
+ `ah_from` varchar(255) NOT NULL,
+ `ah_on` datetime NOT NULL,
+ PRIMARY KEY (`ah_id`)
+);
+
+DROP TABLE IF EXISTS `acl_schemes`;
+
+--
+-- Table: `acl_schemes`
+--
+CREATE TABLE `acl_schemes` (
+ `as_name` varchar(32) NOT NULL,
+ `as_class` varchar(64),
+ PRIMARY KEY (`as_name`)
+) ENGINE=InnoDB;
+
+DROP TABLE IF EXISTS `acls`;
+
+--
+-- Table: `acls`
+--
+CREATE TABLE `acls` (
+ `ac_id` integer NOT NULL auto_increment,
+ `ac_name` varchar(255) NOT NULL,
+ PRIMARY KEY (`ac_id`),
+ UNIQUE `ac_name` (`ac_name`)
+) ENGINE=InnoDB;
+
+DROP TABLE IF EXISTS `enctypes`;
+
+--
+-- Table: `enctypes`
+--
+CREATE TABLE `enctypes` (
+ `en_name` varchar(255) NOT NULL,
+ PRIMARY KEY (`en_name`)
+);
+
+DROP TABLE IF EXISTS `flags`;
+
+--
+-- Table: `flags`
+--
+CREATE TABLE `flag_names` (
+ `fn_name` varchar(32) NOT NULL,
+ PRIMARY KEY (`fn_name`)
+);
+
+DROP TABLE IF EXISTS `flags`;
+
+--
+-- Table: `flags`
+--
+CREATE TABLE `flags` (
+ `fl_type` varchar(16) NOT NULL,
+ `fl_name` varchar(255) NOT NULL,
+ `fl_flag` varchar(32) NOT NULL,
+ PRIMARY KEY (`fl_type`, `fl_name`, `fl_flag`)
+);
+
+DROP TABLE IF EXISTS `keytab_enctypes`;
+
+--
+-- Table: `keytab_enctypes`
+--
+CREATE TABLE `keytab_enctypes` (
+ `ke_name` varchar(255) NOT NULL,
+ `ke_enctype` varchar(255) NOT NULL,
+ PRIMARY KEY (`ke_name`, `ke_enctype`)
+);
+
+DROP TABLE IF EXISTS `keytab_sync`;
+
+--
+-- Table: `keytab_sync`
+--
+CREATE TABLE `keytab_sync` (
+ `ks_name` varchar(255) NOT NULL,
+ `ks_target` varchar(255) NOT NULL,
+ PRIMARY KEY (`ks_name`, `ks_target`)
+);
+
+DROP TABLE IF EXISTS `metadata`;
+
+--
+-- Table: `metadata`
+--
+CREATE TABLE `metadata` (
+ `md_version` integer
+);
+
+DROP TABLE IF EXISTS `sync_targets`;
+
+--
+-- Table: `sync_targets`
+--
+CREATE TABLE `sync_targets` (
+ `st_name` varchar(255) NOT NULL,
+ PRIMARY KEY (`st_name`)
+);
+
+DROP TABLE IF EXISTS `types`;
+
+--
+-- Table: `types`
+--
+CREATE TABLE `types` (
+ `ty_name` varchar(16) NOT NULL,
+ `ty_class` varchar(64),
+ PRIMARY KEY (`ty_name`)
+) ENGINE=InnoDB;
+
+DROP TABLE IF EXISTS `acl_entries`;
+
+--
+-- Table: `acl_entries`
+--
+CREATE TABLE `acl_entries` (
+ `ae_id` integer NOT NULL,
+ `ae_scheme` varchar(32) NOT NULL,
+ `ae_identifier` varchar(255) NOT NULL,
+ INDEX `acl_entries_idx_ae_scheme` (`ae_scheme`),
+ INDEX `acl_entries_idx_ae_id` (`ae_id`),
+ PRIMARY KEY (`ae_id`, `ae_scheme`, `ae_identifier`),
+ CONSTRAINT `acl_entries_fk_ae_scheme` FOREIGN KEY (`ae_scheme`) REFERENCES `acl_schemes` (`as_name`),
+ CONSTRAINT `acl_entries_fk_ae_id` FOREIGN KEY (`ae_id`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+
+DROP TABLE IF EXISTS `objects`;
+
+--
+-- Table: `objects`
+--
+CREATE TABLE `objects` (
+ `ob_type` varchar(16) NOT NULL,
+ `ob_name` varchar(255) NOT NULL,
+ `ob_owner` integer,
+ `ob_acl_get` integer,
+ `ob_acl_store` integer,
+ `ob_acl_show` integer,
+ `ob_acl_destroy` integer,
+ `ob_acl_flags` integer,
+ `ob_expires` datetime,
+ `ob_created_by` varchar(255) NOT NULL,
+ `ob_created_from` varchar(255) NOT NULL,
+ `ob_created_on` datetime NOT NULL,
+ `ob_stored_by` varchar(255),
+ `ob_stored_from` varchar(255),
+ `ob_stored_on` datetime,
+ `ob_downloaded_by` varchar(255),
+ `ob_downloaded_from` varchar(255),
+ `ob_downloaded_on` datetime,
+ INDEX `objects_idx_ob_acl_destroy` (`ob_acl_destroy`),
+ INDEX `objects_idx_ob_acl_flags` (`ob_acl_flags`),
+ INDEX `objects_idx_ob_acl_get` (`ob_acl_get`),
+ INDEX `objects_idx_ob_owner` (`ob_owner`),
+ INDEX `objects_idx_ob_acl_show` (`ob_acl_show`),
+ INDEX `objects_idx_ob_acl_store` (`ob_acl_store`),
+ INDEX `objects_idx_ob_type` (`ob_type`),
+ PRIMARY KEY (`ob_name`, `ob_type`),
+ CONSTRAINT `objects_fk_ob_acl_destroy` FOREIGN KEY (`ob_acl_destroy`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `objects_fk_ob_acl_flags` FOREIGN KEY (`ob_acl_flags`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `objects_fk_ob_acl_get` FOREIGN KEY (`ob_acl_get`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `objects_fk_ob_owner` FOREIGN KEY (`ob_owner`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `objects_fk_ob_acl_show` FOREIGN KEY (`ob_acl_show`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `objects_fk_ob_acl_store` FOREIGN KEY (`ob_acl_store`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `objects_fk_ob_type` FOREIGN KEY (`ob_type`) REFERENCES `types` (`ty_name`)
+) ENGINE=InnoDB;
+
+DROP TABLE IF EXISTS `object_history`;
+
+--
+-- Table: `object_history`
+--
+CREATE TABLE `object_history` (
+ `oh_id` integer NOT NULL auto_increment,
+ `oh_type` varchar(16) NOT NULL,
+ `oh_name` varchar(255) NOT NULL,
+ `oh_action` varchar(16) NOT NULL,
+ `oh_field` varchar(16),
+ `oh_type_field` varchar(255),
+ `oh_old` varchar(255),
+ `oh_new` varchar(255),
+ `oh_by` varchar(255) NOT NULL,
+ `oh_from` varchar(255) NOT NULL,
+ `oh_on` datetime NOT NULL,
+ INDEX `object_history_idx_oh_type_oh_name` (`oh_type`, `oh_name`),
+ PRIMARY KEY (`oh_id`),
+ CONSTRAINT `object_history_fk_oh_type_oh_name` FOREIGN KEY (`oh_type`, `oh_name`) REFERENCES `objects` (`ob_type`, `ob_name`)
+) ENGINE=InnoDB;
+
+SET foreign_key_checks=1;
+
diff --git a/perl/sql/Wallet-Schema-0.07-SQLite.sql b/perl/sql/Wallet-Schema-0.07-SQLite.sql
new file mode 100644
index 0000000..e24ea15
--- /dev/null
+++ b/perl/sql/Wallet-Schema-0.07-SQLite.sql
@@ -0,0 +1,219 @@
+--
+-- Created by SQL::Translator::Producer::SQLite
+-- Created on Fri Jan 25 14:12:02 2013
+--
+
+BEGIN TRANSACTION;
+
+--
+-- Table: acl_history
+--
+DROP TABLE IF EXISTS acl_history;
+
+CREATE TABLE acl_history (
+ ah_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ ah_acl integer NOT NULL,
+ ah_action varchar(16) NOT NULL,
+ ah_scheme varchar(32),
+ ah_identifier varchar(255),
+ ah_by varchar(255) NOT NULL,
+ ah_from varchar(255) NOT NULL,
+ ah_on datetime NOT NULL
+);
+
+--
+-- Table: acl_schemes
+--
+DROP TABLE IF EXISTS acl_schemes;
+
+CREATE TABLE acl_schemes (
+ as_name varchar(32) NOT NULL,
+ as_class varchar(64),
+ PRIMARY KEY (as_name)
+);
+
+--
+-- Table: acls
+--
+DROP TABLE IF EXISTS acls;
+
+CREATE TABLE acls (
+ ac_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ ac_name varchar(255) NOT NULL
+);
+
+CREATE UNIQUE INDEX ac_name ON acls (ac_name);
+
+--
+-- Table: enctypes
+--
+DROP TABLE IF EXISTS enctypes;
+
+CREATE TABLE enctypes (
+ en_name varchar(255) NOT NULL,
+ PRIMARY KEY (en_name)
+);
+
+--
+-- Table: flags
+--
+DROP TABLE IF EXISTS flag_names;
+
+CREATE TABLE flag_names (
+ fn_name varchar(32) NOT NULL,
+ PRIMARY KEY (fn_name)
+);
+
+--
+-- Table: flags
+--
+DROP TABLE IF EXISTS flags;
+
+CREATE TABLE flags (
+ fl_type varchar(16) NOT NULL,
+ fl_name varchar(255) NOT NULL,
+ fl_flag varchar(32) NOT NULL,
+ PRIMARY KEY (fl_type, fl_name, fl_flag)
+);
+
+--
+-- Table: keytab_enctypes
+--
+DROP TABLE IF EXISTS keytab_enctypes;
+
+CREATE TABLE keytab_enctypes (
+ ke_name varchar(255) NOT NULL,
+ ke_enctype varchar(255) NOT NULL,
+ PRIMARY KEY (ke_name, ke_enctype)
+);
+
+--
+-- Table: keytab_sync
+--
+DROP TABLE IF EXISTS keytab_sync;
+
+CREATE TABLE keytab_sync (
+ ks_name varchar(255) NOT NULL,
+ ks_target varchar(255) NOT NULL,
+ PRIMARY KEY (ks_name, ks_target)
+);
+
+--
+-- Table: metadata
+--
+DROP TABLE IF EXISTS metadata;
+
+CREATE TABLE metadata (
+ md_version integer
+);
+
+--
+-- Table: sync_targets
+--
+DROP TABLE IF EXISTS sync_targets;
+
+CREATE TABLE sync_targets (
+ st_name varchar(255) NOT NULL,
+ PRIMARY KEY (st_name)
+);
+
+--
+-- Table: types
+--
+DROP TABLE IF EXISTS types;
+
+CREATE TABLE types (
+ ty_name varchar(16) NOT NULL,
+ ty_class varchar(64),
+ PRIMARY KEY (ty_name)
+);
+
+--
+-- Table: acl_entries
+--
+DROP TABLE IF EXISTS acl_entries;
+
+CREATE TABLE acl_entries (
+ ae_id integer NOT NULL,
+ ae_scheme varchar(32) NOT NULL,
+ ae_identifier varchar(255) NOT NULL,
+ PRIMARY KEY (ae_id, ae_scheme, ae_identifier),
+ FOREIGN KEY(ae_scheme) REFERENCES acl_schemes(as_name),
+ FOREIGN KEY(ae_id) REFERENCES acls(ac_id)
+);
+
+CREATE INDEX acl_entries_idx_ae_scheme ON acl_entries (ae_scheme);
+
+CREATE INDEX acl_entries_idx_ae_id ON acl_entries (ae_id);
+
+--
+-- Table: objects
+--
+DROP TABLE IF EXISTS objects;
+
+CREATE TABLE objects (
+ ob_type varchar(16) NOT NULL,
+ ob_name varchar(255) NOT NULL,
+ ob_owner integer,
+ ob_acl_get integer,
+ ob_acl_store integer,
+ ob_acl_show integer,
+ ob_acl_destroy integer,
+ ob_acl_flags integer,
+ ob_expires datetime,
+ ob_created_by varchar(255) NOT NULL,
+ ob_created_from varchar(255) NOT NULL,
+ ob_created_on datetime NOT NULL,
+ ob_stored_by varchar(255),
+ ob_stored_from varchar(255),
+ ob_stored_on datetime,
+ ob_downloaded_by varchar(255),
+ ob_downloaded_from varchar(255),
+ ob_downloaded_on datetime,
+ PRIMARY KEY (ob_name, ob_type),
+ FOREIGN KEY(ob_acl_destroy) REFERENCES acls(ac_id),
+ FOREIGN KEY(ob_acl_flags) REFERENCES acls(ac_id),
+ FOREIGN KEY(ob_acl_get) REFERENCES acls(ac_id),
+ FOREIGN KEY(ob_owner) REFERENCES acls(ac_id),
+ FOREIGN KEY(ob_acl_show) REFERENCES acls(ac_id),
+ FOREIGN KEY(ob_acl_store) REFERENCES acls(ac_id),
+ FOREIGN KEY(ob_type) REFERENCES types(ty_name)
+);
+
+CREATE INDEX objects_idx_ob_acl_destroy ON objects (ob_acl_destroy);
+
+CREATE INDEX objects_idx_ob_acl_flags ON objects (ob_acl_flags);
+
+CREATE INDEX objects_idx_ob_acl_get ON objects (ob_acl_get);
+
+CREATE INDEX objects_idx_ob_owner ON objects (ob_owner);
+
+CREATE INDEX objects_idx_ob_acl_show ON objects (ob_acl_show);
+
+CREATE INDEX objects_idx_ob_acl_store ON objects (ob_acl_store);
+
+CREATE INDEX objects_idx_ob_type ON objects (ob_type);
+
+--
+-- Table: object_history
+--
+DROP TABLE IF EXISTS object_history;
+
+CREATE TABLE object_history (
+ oh_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ oh_type varchar(16) NOT NULL,
+ oh_name varchar(255) NOT NULL,
+ oh_action varchar(16) NOT NULL,
+ oh_field varchar(16),
+ oh_type_field varchar(255),
+ oh_old varchar(255),
+ oh_new varchar(255),
+ oh_by varchar(255) NOT NULL,
+ oh_from varchar(255) NOT NULL,
+ oh_on datetime NOT NULL,
+ FOREIGN KEY(oh_type) REFERENCES objects(ob_type)
+);
+
+CREATE INDEX object_history_idx_oh_type_oh_name ON object_history (oh_type, oh_name);
+
+COMMIT;
diff --git a/perl/sql/Wallet-Schema-0.08-MySQL.sql b/perl/sql/Wallet-Schema-0.08-MySQL.sql
new file mode 100644
index 0000000..44b6475
--- /dev/null
+++ b/perl/sql/Wallet-Schema-0.08-MySQL.sql
@@ -0,0 +1,193 @@
+--
+-- Created by SQL::Translator::Producer::MySQL
+-- Created on Fri Jan 25 14:12:02 2013
+--
+SET foreign_key_checks=0;
+
+DROP TABLE IF EXISTS `acl_history`;
+
+--
+-- Table: `acl_history`
+--
+CREATE TABLE `acl_history` (
+ `ah_id` integer NOT NULL auto_increment,
+ `ah_acl` integer NOT NULL,
+ `ah_action` varchar(16) NOT NULL,
+ `ah_scheme` varchar(32),
+ `ah_identifier` varchar(255),
+ `ah_by` varchar(255) NOT NULL,
+ `ah_from` varchar(255) NOT NULL,
+ `ah_on` datetime NOT NULL,
+ PRIMARY KEY (`ah_id`)
+);
+
+DROP TABLE IF EXISTS `acl_schemes`;
+
+--
+-- Table: `acl_schemes`
+--
+CREATE TABLE `acl_schemes` (
+ `as_name` varchar(32) NOT NULL,
+ `as_class` varchar(64),
+ PRIMARY KEY (`as_name`)
+) ENGINE=InnoDB;
+
+DROP TABLE IF EXISTS `acls`;
+
+--
+-- Table: `acls`
+--
+CREATE TABLE `acls` (
+ `ac_id` integer NOT NULL auto_increment,
+ `ac_name` varchar(255) NOT NULL,
+ PRIMARY KEY (`ac_id`),
+ UNIQUE `ac_name` (`ac_name`)
+) ENGINE=InnoDB;
+
+DROP TABLE IF EXISTS `enctypes`;
+
+--
+-- Table: `enctypes`
+--
+CREATE TABLE `enctypes` (
+ `en_name` varchar(255) NOT NULL,
+ PRIMARY KEY (`en_name`)
+);
+
+DROP TABLE IF EXISTS `flags`;
+
+--
+-- Table: `flags`
+--
+CREATE TABLE `flags` (
+ `fl_type` varchar(16) NOT NULL,
+ `fl_name` varchar(255) NOT NULL,
+ `fl_flag` enum('locked', 'unchanging') NOT NULL,
+ PRIMARY KEY (`fl_type`, `fl_name`, `fl_flag`)
+);
+
+DROP TABLE IF EXISTS `keytab_enctypes`;
+
+--
+-- Table: `keytab_enctypes`
+--
+CREATE TABLE `keytab_enctypes` (
+ `ke_name` varchar(255) NOT NULL,
+ `ke_enctype` varchar(255) NOT NULL,
+ PRIMARY KEY (`ke_name`, `ke_enctype`)
+);
+
+DROP TABLE IF EXISTS `keytab_sync`;
+
+--
+-- Table: `keytab_sync`
+--
+CREATE TABLE `keytab_sync` (
+ `ks_name` varchar(255) NOT NULL,
+ `ks_target` varchar(255) NOT NULL,
+ PRIMARY KEY (`ks_name`, `ks_target`)
+);
+
+DROP TABLE IF EXISTS `sync_targets`;
+
+--
+-- Table: `sync_targets`
+--
+CREATE TABLE `sync_targets` (
+ `st_name` varchar(255) NOT NULL,
+ PRIMARY KEY (`st_name`)
+);
+
+DROP TABLE IF EXISTS `types`;
+
+--
+-- Table: `types`
+--
+CREATE TABLE `types` (
+ `ty_name` varchar(16) NOT NULL,
+ `ty_class` varchar(64),
+ PRIMARY KEY (`ty_name`)
+) ENGINE=InnoDB;
+
+DROP TABLE IF EXISTS `acl_entries`;
+
+--
+-- Table: `acl_entries`
+--
+CREATE TABLE `acl_entries` (
+ `ae_id` integer NOT NULL,
+ `ae_scheme` varchar(32) NOT NULL,
+ `ae_identifier` varchar(255) NOT NULL,
+ INDEX `acl_entries_idx_ae_scheme` (`ae_scheme`),
+ INDEX `acl_entries_idx_ae_id` (`ae_id`),
+ PRIMARY KEY (`ae_id`, `ae_scheme`, `ae_identifier`),
+ CONSTRAINT `acl_entries_fk_ae_scheme` FOREIGN KEY (`ae_scheme`) REFERENCES `acl_schemes` (`as_name`),
+ CONSTRAINT `acl_entries_fk_ae_id` FOREIGN KEY (`ae_id`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+
+DROP TABLE IF EXISTS `objects`;
+
+--
+-- Table: `objects`
+--
+CREATE TABLE `objects` (
+ `ob_type` varchar(16) NOT NULL,
+ `ob_name` varchar(255) NOT NULL,
+ `ob_owner` integer,
+ `ob_acl_get` integer,
+ `ob_acl_store` integer,
+ `ob_acl_show` integer,
+ `ob_acl_destroy` integer,
+ `ob_acl_flags` integer,
+ `ob_expires` datetime,
+ `ob_created_by` varchar(255) NOT NULL,
+ `ob_created_from` varchar(255) NOT NULL,
+ `ob_created_on` datetime NOT NULL,
+ `ob_stored_by` varchar(255),
+ `ob_stored_from` varchar(255),
+ `ob_stored_on` datetime,
+ `ob_downloaded_by` varchar(255),
+ `ob_downloaded_from` varchar(255),
+ `ob_downloaded_on` datetime,
+ `ob_comment` varchar(255),
+ INDEX `objects_idx_ob_acl_destroy` (`ob_acl_destroy`),
+ INDEX `objects_idx_ob_acl_flags` (`ob_acl_flags`),
+ INDEX `objects_idx_ob_acl_get` (`ob_acl_get`),
+ INDEX `objects_idx_ob_owner` (`ob_owner`),
+ INDEX `objects_idx_ob_acl_show` (`ob_acl_show`),
+ INDEX `objects_idx_ob_acl_store` (`ob_acl_store`),
+ INDEX `objects_idx_ob_type` (`ob_type`),
+ PRIMARY KEY (`ob_name`, `ob_type`),
+ CONSTRAINT `objects_fk_ob_acl_destroy` FOREIGN KEY (`ob_acl_destroy`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `objects_fk_ob_acl_flags` FOREIGN KEY (`ob_acl_flags`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `objects_fk_ob_acl_get` FOREIGN KEY (`ob_acl_get`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `objects_fk_ob_owner` FOREIGN KEY (`ob_owner`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `objects_fk_ob_acl_show` FOREIGN KEY (`ob_acl_show`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `objects_fk_ob_acl_store` FOREIGN KEY (`ob_acl_store`) REFERENCES `acls` (`ac_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `objects_fk_ob_type` FOREIGN KEY (`ob_type`) REFERENCES `types` (`ty_name`)
+) ENGINE=InnoDB;
+
+DROP TABLE IF EXISTS `object_history`;
+
+--
+-- Table: `object_history`
+--
+CREATE TABLE `object_history` (
+ `oh_id` integer NOT NULL auto_increment,
+ `oh_type` varchar(16) NOT NULL,
+ `oh_name` varchar(255) NOT NULL,
+ `oh_action` varchar(16) NOT NULL,
+ `oh_field` varchar(16),
+ `oh_type_field` varchar(255),
+ `oh_old` varchar(255),
+ `oh_new` varchar(255),
+ `oh_by` varchar(255) NOT NULL,
+ `oh_from` varchar(255) NOT NULL,
+ `oh_on` datetime NOT NULL,
+ INDEX `object_history_idx_oh_type_oh_name` (`oh_type`, `oh_name`),
+ PRIMARY KEY (`oh_id`),
+ CONSTRAINT `object_history_fk_oh_type_oh_name` FOREIGN KEY (`oh_type`, `oh_name`) REFERENCES `objects` (`ob_type`, `ob_name`)
+) ENGINE=InnoDB;
+
+SET foreign_key_checks=1;
+
diff --git a/perl/sql/Wallet-Schema-0.08-PostgreSQL.sql b/perl/sql/Wallet-Schema-0.08-PostgreSQL.sql
new file mode 100644
index 0000000..2f79147
--- /dev/null
+++ b/perl/sql/Wallet-Schema-0.08-PostgreSQL.sql
@@ -0,0 +1,201 @@
+--
+-- Created by SQL::Translator::Producer::PostgreSQL
+-- Created on Fri Jan 25 14:12:02 2013
+--
+--
+-- Table: acl_history
+--
+DROP TABLE "acl_history" CASCADE;
+CREATE TABLE "acl_history" (
+ "ah_id" serial NOT NULL,
+ "ah_acl" integer NOT NULL,
+ "ah_action" character varying(16) NOT NULL,
+ "ah_scheme" character varying(32),
+ "ah_identifier" character varying(255),
+ "ah_by" character varying(255) NOT NULL,
+ "ah_from" character varying(255) NOT NULL,
+ "ah_on" timestamp NOT NULL,
+ PRIMARY KEY ("ah_id")
+);
+
+--
+-- Table: acl_schemes
+--
+DROP TABLE "acl_schemes" CASCADE;
+CREATE TABLE "acl_schemes" (
+ "as_name" character varying(32) NOT NULL,
+ "as_class" character varying(64),
+ PRIMARY KEY ("as_name")
+);
+
+--
+-- Table: acls
+--
+DROP TABLE "acls" CASCADE;
+CREATE TABLE "acls" (
+ "ac_id" serial NOT NULL,
+ "ac_name" character varying(255) NOT NULL,
+ PRIMARY KEY ("ac_id"),
+ CONSTRAINT "ac_name" UNIQUE ("ac_name")
+);
+
+--
+-- Table: enctypes
+--
+DROP TABLE "enctypes" CASCADE;
+CREATE TABLE "enctypes" (
+ "en_name" character varying(255) NOT NULL,
+ PRIMARY KEY ("en_name")
+);
+
+--
+-- Table: flags
+--
+DROP TABLE "flags" CASCADE;
+CREATE TABLE "flags" (
+ "fl_type" character varying(16) NOT NULL,
+ "fl_name" character varying(255) NOT NULL,
+ "fl_flag" character varying NOT NULL,
+ PRIMARY KEY ("fl_type", "fl_name", "fl_flag")
+);
+
+--
+-- Table: keytab_enctypes
+--
+DROP TABLE "keytab_enctypes" CASCADE;
+CREATE TABLE "keytab_enctypes" (
+ "ke_name" character varying(255) NOT NULL,
+ "ke_enctype" character varying(255) NOT NULL,
+ PRIMARY KEY ("ke_name", "ke_enctype")
+);
+
+--
+-- Table: keytab_sync
+--
+DROP TABLE "keytab_sync" CASCADE;
+CREATE TABLE "keytab_sync" (
+ "ks_name" character varying(255) NOT NULL,
+ "ks_target" character varying(255) NOT NULL,
+ PRIMARY KEY ("ks_name", "ks_target")
+);
+
+--
+-- Table: sync_targets
+--
+DROP TABLE "sync_targets" CASCADE;
+CREATE TABLE "sync_targets" (
+ "st_name" character varying(255) NOT NULL,
+ PRIMARY KEY ("st_name")
+);
+
+--
+-- Table: types
+--
+DROP TABLE "types" CASCADE;
+CREATE TABLE "types" (
+ "ty_name" character varying(16) NOT NULL,
+ "ty_class" character varying(64),
+ PRIMARY KEY ("ty_name")
+);
+
+--
+-- Table: acl_entries
+--
+DROP TABLE "acl_entries" CASCADE;
+CREATE TABLE "acl_entries" (
+ "ae_id" integer NOT NULL,
+ "ae_scheme" character varying(32) NOT NULL,
+ "ae_identifier" character varying(255) NOT NULL,
+ PRIMARY KEY ("ae_id", "ae_scheme", "ae_identifier")
+);
+CREATE INDEX "acl_entries_idx_ae_scheme" on "acl_entries" ("ae_scheme");
+CREATE INDEX "acl_entries_idx_ae_id" on "acl_entries" ("ae_id");
+
+--
+-- Table: objects
+--
+DROP TABLE "objects" CASCADE;
+CREATE TABLE "objects" (
+ "ob_type" character varying(16) NOT NULL,
+ "ob_name" character varying(255) NOT NULL,
+ "ob_owner" integer,
+ "ob_acl_get" integer,
+ "ob_acl_store" integer,
+ "ob_acl_show" integer,
+ "ob_acl_destroy" integer,
+ "ob_acl_flags" integer,
+ "ob_expires" timestamp,
+ "ob_created_by" character varying(255) NOT NULL,
+ "ob_created_from" character varying(255) NOT NULL,
+ "ob_created_on" timestamp NOT NULL,
+ "ob_stored_by" character varying(255),
+ "ob_stored_from" character varying(255),
+ "ob_stored_on" timestamp,
+ "ob_downloaded_by" character varying(255),
+ "ob_downloaded_from" character varying(255),
+ "ob_downloaded_on" timestamp,
+ "ob_comment" character varying(255),
+ PRIMARY KEY ("ob_name", "ob_type")
+);
+CREATE INDEX "objects_idx_ob_acl_destroy" on "objects" ("ob_acl_destroy");
+CREATE INDEX "objects_idx_ob_acl_flags" on "objects" ("ob_acl_flags");
+CREATE INDEX "objects_idx_ob_acl_get" on "objects" ("ob_acl_get");
+CREATE INDEX "objects_idx_ob_owner" on "objects" ("ob_owner");
+CREATE INDEX "objects_idx_ob_acl_show" on "objects" ("ob_acl_show");
+CREATE INDEX "objects_idx_ob_acl_store" on "objects" ("ob_acl_store");
+CREATE INDEX "objects_idx_ob_type" on "objects" ("ob_type");
+
+--
+-- Table: object_history
+--
+DROP TABLE "object_history" CASCADE;
+CREATE TABLE "object_history" (
+ "oh_id" serial NOT NULL,
+ "oh_type" character varying(16) NOT NULL,
+ "oh_name" character varying(255) NOT NULL,
+ "oh_action" character varying(16) NOT NULL,
+ "oh_field" character varying(16),
+ "oh_type_field" character varying(255),
+ "oh_old" character varying(255),
+ "oh_new" character varying(255),
+ "oh_by" character varying(255) NOT NULL,
+ "oh_from" character varying(255) NOT NULL,
+ "oh_on" timestamp NOT NULL,
+ PRIMARY KEY ("oh_id")
+);
+CREATE INDEX "object_history_idx_oh_type_oh_name" on "object_history" ("oh_type", "oh_name");
+
+--
+-- Foreign Key Definitions
+--
+
+ALTER TABLE "acl_entries" ADD FOREIGN KEY ("ae_scheme")
+ REFERENCES "acl_schemes" ("as_name") DEFERRABLE;
+
+ALTER TABLE "acl_entries" ADD FOREIGN KEY ("ae_id")
+ REFERENCES "acls" ("ac_id") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+ALTER TABLE "objects" ADD FOREIGN KEY ("ob_acl_destroy")
+ REFERENCES "acls" ("ac_id") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+ALTER TABLE "objects" ADD FOREIGN KEY ("ob_acl_flags")
+ REFERENCES "acls" ("ac_id") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+ALTER TABLE "objects" ADD FOREIGN KEY ("ob_acl_get")
+ REFERENCES "acls" ("ac_id") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+ALTER TABLE "objects" ADD FOREIGN KEY ("ob_owner")
+ REFERENCES "acls" ("ac_id") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+ALTER TABLE "objects" ADD FOREIGN KEY ("ob_acl_show")
+ REFERENCES "acls" ("ac_id") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+ALTER TABLE "objects" ADD FOREIGN KEY ("ob_acl_store")
+ REFERENCES "acls" ("ac_id") ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE;
+
+ALTER TABLE "objects" ADD FOREIGN KEY ("ob_type")
+ REFERENCES "types" ("ty_name") DEFERRABLE;
+
+ALTER TABLE "object_history" ADD FOREIGN KEY ("oh_type", "oh_name")
+ REFERENCES "objects" ("ob_type", "ob_name") DEFERRABLE;
+
diff --git a/perl/sql/Wallet-Schema-0.08-SQLite.sql b/perl/sql/Wallet-Schema-0.08-SQLite.sql
new file mode 100644
index 0000000..9936c20
--- /dev/null
+++ b/perl/sql/Wallet-Schema-0.08-SQLite.sql
@@ -0,0 +1,201 @@
+--
+-- Created by SQL::Translator::Producer::SQLite
+-- Created on Fri Jan 25 14:12:02 2013
+--
+
+BEGIN TRANSACTION;
+
+--
+-- Table: acl_history
+--
+DROP TABLE IF EXISTS acl_history;
+
+CREATE TABLE acl_history (
+ ah_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ ah_acl integer NOT NULL,
+ ah_action varchar(16) NOT NULL,
+ ah_scheme varchar(32),
+ ah_identifier varchar(255),
+ ah_by varchar(255) NOT NULL,
+ ah_from varchar(255) NOT NULL,
+ ah_on datetime NOT NULL
+);
+
+--
+-- Table: acl_schemes
+--
+DROP TABLE IF EXISTS acl_schemes;
+
+CREATE TABLE acl_schemes (
+ as_name varchar(32) NOT NULL,
+ as_class varchar(64),
+ PRIMARY KEY (as_name)
+);
+
+--
+-- Table: acls
+--
+DROP TABLE IF EXISTS acls;
+
+CREATE TABLE acls (
+ ac_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ ac_name varchar(255) NOT NULL
+);
+
+CREATE UNIQUE INDEX ac_name ON acls (ac_name);
+
+--
+-- Table: enctypes
+--
+DROP TABLE IF EXISTS enctypes;
+
+CREATE TABLE enctypes (
+ en_name varchar(255) NOT NULL,
+ PRIMARY KEY (en_name)
+);
+
+--
+-- Table: flags
+--
+DROP TABLE IF EXISTS flags;
+
+CREATE TABLE flags (
+ fl_type varchar(16) NOT NULL,
+ fl_name varchar(255) NOT NULL,
+ fl_flag varchar(32) NOT NULL,
+ PRIMARY KEY (fl_type, fl_name, fl_flag)
+);
+
+--
+-- Table: keytab_enctypes
+--
+DROP TABLE IF EXISTS keytab_enctypes;
+
+CREATE TABLE keytab_enctypes (
+ ke_name varchar(255) NOT NULL,
+ ke_enctype varchar(255) NOT NULL,
+ PRIMARY KEY (ke_name, ke_enctype)
+);
+
+--
+-- Table: keytab_sync
+--
+DROP TABLE IF EXISTS keytab_sync;
+
+CREATE TABLE keytab_sync (
+ ks_name varchar(255) NOT NULL,
+ ks_target varchar(255) NOT NULL,
+ PRIMARY KEY (ks_name, ks_target)
+);
+
+--
+-- Table: sync_targets
+--
+DROP TABLE IF EXISTS sync_targets;
+
+CREATE TABLE sync_targets (
+ st_name varchar(255) NOT NULL,
+ PRIMARY KEY (st_name)
+);
+
+--
+-- Table: types
+--
+DROP TABLE IF EXISTS types;
+
+CREATE TABLE types (
+ ty_name varchar(16) NOT NULL,
+ ty_class varchar(64),
+ PRIMARY KEY (ty_name)
+);
+
+--
+-- Table: acl_entries
+--
+DROP TABLE IF EXISTS acl_entries;
+
+CREATE TABLE acl_entries (
+ ae_id integer NOT NULL,
+ ae_scheme varchar(32) NOT NULL,
+ ae_identifier varchar(255) NOT NULL,
+ PRIMARY KEY (ae_id, ae_scheme, ae_identifier),
+ FOREIGN KEY(ae_scheme) REFERENCES acl_schemes(as_name),
+ FOREIGN KEY(ae_id) REFERENCES acls(ac_id)
+);
+
+CREATE INDEX acl_entries_idx_ae_scheme ON acl_entries (ae_scheme);
+
+CREATE INDEX acl_entries_idx_ae_id ON acl_entries (ae_id);
+
+--
+-- Table: objects
+--
+DROP TABLE IF EXISTS objects;
+
+CREATE TABLE objects (
+ ob_type varchar(16) NOT NULL,
+ ob_name varchar(255) NOT NULL,
+ ob_owner integer,
+ ob_acl_get integer,
+ ob_acl_store integer,
+ ob_acl_show integer,
+ ob_acl_destroy integer,
+ ob_acl_flags integer,
+ ob_expires datetime,
+ ob_created_by varchar(255) NOT NULL,
+ ob_created_from varchar(255) NOT NULL,
+ ob_created_on datetime NOT NULL,
+ ob_stored_by varchar(255),
+ ob_stored_from varchar(255),
+ ob_stored_on datetime,
+ ob_downloaded_by varchar(255),
+ ob_downloaded_from varchar(255),
+ ob_downloaded_on datetime,
+ ob_comment varchar(255),
+ PRIMARY KEY (ob_name, ob_type),
+ FOREIGN KEY(ob_acl_destroy) REFERENCES acls(ac_id),
+ FOREIGN KEY(ob_acl_flags) REFERENCES acls(ac_id),
+ FOREIGN KEY(ob_acl_get) REFERENCES acls(ac_id),
+ FOREIGN KEY(ob_owner) REFERENCES acls(ac_id),
+ FOREIGN KEY(ob_acl_show) REFERENCES acls(ac_id),
+ FOREIGN KEY(ob_acl_store) REFERENCES acls(ac_id),
+ FOREIGN KEY(ob_type) REFERENCES types(ty_name)
+);
+
+CREATE INDEX objects_idx_ob_acl_destroy ON objects (ob_acl_destroy);
+
+CREATE INDEX objects_idx_ob_acl_flags ON objects (ob_acl_flags);
+
+CREATE INDEX objects_idx_ob_acl_get ON objects (ob_acl_get);
+
+CREATE INDEX objects_idx_ob_owner ON objects (ob_owner);
+
+CREATE INDEX objects_idx_ob_acl_show ON objects (ob_acl_show);
+
+CREATE INDEX objects_idx_ob_acl_store ON objects (ob_acl_store);
+
+CREATE INDEX objects_idx_ob_type ON objects (ob_type);
+
+--
+-- Table: object_history
+--
+DROP TABLE IF EXISTS object_history;
+
+CREATE TABLE object_history (
+ oh_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ oh_type varchar(16) NOT NULL,
+ oh_name varchar(255) NOT NULL,
+ oh_action varchar(16) NOT NULL,
+ oh_field varchar(16),
+ oh_type_field varchar(255),
+ oh_old varchar(255),
+ oh_new varchar(255),
+ oh_by varchar(255) NOT NULL,
+ oh_from varchar(255) NOT NULL,
+ oh_on datetime NOT NULL,
+ FOREIGN KEY(oh_type) REFERENCES objects(ob_type)
+);
+
+CREATE INDEX object_history_idx_oh_type_oh_name ON object_history (oh_type, oh_name);
+
+COMMIT;
diff --git a/perl/t/admin.t b/perl/t/admin.t
index 6250f8e..cf6a637 100755
--- a/perl/t/admin.t
+++ b/perl/t/admin.t
@@ -8,12 +8,13 @@
#
# See LICENSE for licensing terms.
-use Test::More tests => 18;
+use Test::More tests => 23;
use Wallet::Admin;
use Wallet::Report;
use Wallet::Schema;
use Wallet::Server;
+use DBI;
use lib 't/lib';
use Util;
@@ -56,6 +57,24 @@ is ($admin->register_verifier ('base', 'Wallet::ACL::Base'), undef,
is ($server->acl_add ('ADMIN', 'base', 'foo'), 1,
' and adding a base ACL now works');
+# Test an upgrade. Reinitialize to an older version, then test upgrade to
+# the current version.
+$Wallet::Schema::VERSION = '0.07';
+is ($admin->reinitialize ('admin@EXAMPLE.COM'), 1,
+ ' and re-initialization succeeds');
+$Wallet::Schema::VERSION = '0.08';
+my $schema = $admin->dbh;
+$schema->upgrade_directory ('sql/');
+my $retval = $admin->upgrade;
+is ($retval, 1, 'Performing an upgrade succeeds');
+my $dbh = $schema->storage->dbh;
+my $sql = "select version from dbix_class_schema_versions order by version "
+ ."DESC";
+$version = $dbh->selectall_arrayref ($sql);
+is (@$version, 2, ' and versions table has correct number of rows');
+is (@{ $version->[0] }, 1, ' and correct number of columns');
+is ($version->[0][0], '0.08', ' and the schema version is correct');
+
# Clean up.
is ($admin->destroy, 1, 'Destruction succeeds');
unlink 'wallet-db';
diff --git a/perl/t/lib/Util.pm b/perl/t/lib/Util.pm
index 8bbefc4..c15ccfe 100644
--- a/perl/t/lib/Util.pm
+++ b/perl/t/lib/Util.pm
@@ -45,6 +45,7 @@ sub contents {
# for testing by default, but support t/data/test.database as a configuration
# file to use another database backend.
sub db_setup {
+ $Wallet::Config::DB_DDL_DIRECTORY = 'sql/';
if (-f 't/data/test.database') {
open (DB, '<', 't/data/test.database')
or die "cannot open t/data/test.database: $!";
@@ -60,6 +61,10 @@ sub db_setup {
$Wallet::Config::DB_USER = $user if $user;
$Wallet::Config::DB_PASSWORD = $password if $password;
} else {
+
+ # If we have a new SQLite db by default, disable version checking.
+ $ENV{DBIC_NO_VERSION_CHECK} = 1;
+
$Wallet::Config::DB_DRIVER = 'SQLite';
$Wallet::Config::DB_INFO = 'wallet-db';
unlink 'wallet-db';
diff --git a/perl/t/report.t b/perl/t/report.t
index 363db20..13ef7b6 100755
--- a/perl/t/report.t
+++ b/perl/t/report.t
@@ -145,7 +145,7 @@ is (scalar (@lines), 1, 'Searching for objects owned by ADMIN finds one');
is ($lines[0][0], 'base', ' and it has the right type');
is ($lines[0][1], 'service/admin', ' and the right name');
@lines = $report->objects ('owner', 'null');
-is (scalar (@lines), 1, 'Searching for objects with no set ownerfinds one');
+is (scalar (@lines), 1, 'Searching for objects with no set owner finds one');
is ($lines[0][0], 'base', ' and it has the right type');
is ($lines[0][1], 'service/null', ' and the right name');
@lines = $report->objects ('acl', 'ADMIN');
diff --git a/perl/t/schema.t b/perl/t/schema.t
deleted file mode 100755
index 5dd90d1..0000000
--- a/perl/t/schema.t
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Tests for the wallet schema class.
-#
-# Written by Russ Allbery <rra@stanford.edu>
-# Copyright 2007, 2008, 2011
-# The Board of Trustees of the Leland Stanford Junior University
-#
-# See LICENSE for licensing terms.
-
-use Test::More tests => 16;
-
-use DBI ();
-use POSIX qw(strftime);
-use Wallet::Config ();
-use Wallet::Schema ();
-
-use lib 't/lib';
-use Util;
-
-my $schema = Wallet::Schema->new;
-ok (defined $schema, 'Wallet::Schema creation');
-ok ($schema->isa ('Wallet::Schema'), ' and class verification');
-my @sql = $schema->sql;
-ok (@sql > 0, 'sql() returns something');
-is (scalar (@sql), 32, ' and returns the right number of statements');
-
-# Connect to a database and test create.
-db_setup;
-my $connect = "DBI:${Wallet::Config::DB_DRIVER}:${Wallet::Config::DB_INFO}";
-my $user = $Wallet::Config::DB_USER;
-my $password = $Wallet::Config::DB_PASSWORD;
-$dbh = DBI->connect ($connect, $user, $password);
-if (not defined $dbh) {
- die "cannot connect to database $connect: $DBI::errstr\n";
-}
-$dbh->{RaiseError} = 1;
-$dbh->{PrintError} = 0;
-eval { $schema->create ($dbh) };
-is ($@, '', "create() doesn't die");
-
-# Check that the version number is correct.
-my $sql = "select md_version from metadata";
-my $version = $dbh->selectall_arrayref ($sql);
-is (@$version, 1, 'metadata has correct number of rows');
-is (@{ $version->[0] }, 1, ' and correct number of columns');
-is ($version->[0][0], 1, ' and the schema version is correct');
-
-# Test upgrading the database from version 0. SQLite cannot drop table
-# columns, so we have to kill the table and then recreate it.
-$dbh->do ("drop table metadata");
-if (lc ($Wallet::Config::DB_DRIVER) eq 'sqlite') {
- ($sql) = grep { /create table objects/ } $schema->sql;
- $sql =~ s/ob_comment .*,//;
- $dbh->do ("drop table objects")
- or die "cannot drop objects table: $DBI::errstr\n";
- $dbh->do ($sql)
- or die "cannot recreate objects table: $DBI::errstr\n";
-} else {
- $dbh->do ("alter table objects drop column ob_comment")
- or die "cannot drop ob_comment column: $DBI::errstr\n";
-}
-eval { $schema->upgrade ($dbh) };
-is ($@, '', "upgrade() doesn't die");
-$sql = "select md_version from metadata";
-$version = $dbh->selectall_arrayref ($sql);
-is (@$version, 1, ' and metadata has correct number of rows');
-is (@{ $version->[0] }, 1, ' and correct number of columns');
-is ($version->[0][0], 1, ' and the schema version is correct');
-$sql = "insert into objects (ob_type, ob_name, ob_created_by, ob_created_from,
- ob_created_on, ob_comment) values ('file', 'test', 'test',
- 'test.example.org', ?, 'a test comment')";
-$dbh->do ($sql, undef, strftime ('%Y-%m-%d %T', localtime time));
-$sql = "select ob_comment from objects where ob_name = 'test'";
-my ($comment) = $dbh->selectrow_array ($sql);
-is ($comment, 'a test comment', ' and ob_comment was added to objects');
-
-# Test dropping the database.
-eval { $schema->drop ($dbh) };
-is ($@, '', "drop() doesn't die");
-
-# Make sure all the tables are gone.
-SKIP: {
- if (lc ($Wallet::Config::DB_DRIVER) eq 'sqlite') {
- my $sql = "select name from sqlite_master where type = 'table'";
- my $sth = $dbh->prepare ($sql);
- $sth->execute;
- my ($table, @tables);
- while (defined ($table = $sth->fetchrow_array)) {
- push (@tables, $table) unless $table =~ /^sqlite_/;
- }
- is ("@tables", '', ' and there are no tables in the database');
- } elsif (lc ($Wallet::Config::DB_DRIVER) eq 'mysql') {
- my $sql = "show tables";
- my $sth = $dbh->prepare ($sql);
- $sth->execute;
- my ($table, @tables);
- while (defined ($table = $sth->fetchrow_array)) {
- push (@tables, $table);
- }
- is ("@tables", '', ' and there are no tables in the database');
- } else {
- skip 1;
- }
-}
-eval { $schema->create ($dbh) };
-is ($@, '', ' and we can run create again');
-
-# Clean up.
-eval { $schema->drop ($dbh) };
-unlink 'wallet-db';
diff --git a/perl/t/server.t b/perl/t/server.t
index 8e0a30d..63f2e76 100755
--- a/perl/t/server.t
+++ b/perl/t/server.t
@@ -1030,5 +1030,5 @@ is ($@, "database connection information not configured\n",
' or if DB_INFO is not set');
$Wallet::Config::DB_INFO = 't';
$server = eval { Wallet::Server->new ($user2, $host) };
-like ($@, qr/^cannot connect to database: /,
+like ($@, qr/unable to open database file/,
' or if the database connection fails');
diff --git a/server/wallet-admin b/server/wallet-admin
index 94d62c7..7e5a402 100755
--- a/server/wallet-admin
+++ b/server/wallet-admin
@@ -15,6 +15,13 @@
use strict;
use Wallet::Admin;
+# The last non-DBIx::Class version. If a database has no DBIx::Class
+# versioning, we want to set it to this so that upgrades can begin.
+our $BASE_VERSION = '0.07';
+
+# Directory that contains the wallet SQL files for upgrades.
+our $SQL_DIR = '/usr/share/wallet/sql/';
+
##############################################################################
# Implementation
##############################################################################
@@ -41,6 +48,9 @@ sub command {
die "too few arguments to initialize\n" if @args < 1;
die "invalid admin principal $args[0]\n"
unless $args[0] =~ /^[^\@\s]+\@\S+$/;
+
+ my $schema = $admin->{dbh};
+ $schema->upgrade_directory ($SQL_DIR);
$admin->initialize (@args) or die $admin->error, "\n";
} elsif ($command eq 'register') {
die "too many arguments to register\n" if @args > 3;
@@ -59,7 +69,20 @@ sub command {
}
} elsif ($command eq 'upgrade') {
die "too many arguments to upgrade\n" if @args;
+
+ my $schema = $admin->{dbh};
+ $schema->upgrade_directory ($SQL_DIR);
+
+ # Check to see if the database is versioned. If not, install the
+ # versioning table and default version.
+ if (!$schema->get_db_version) {
+ print "Versioning database.\n";
+ $schema->install ($BASE_VERSION);
+ }
+
+ # Actually upgrade.
$admin->upgrade or die $admin->error, "\n";
+
} else {
die "unknown command $command\n";
}