diff options
Diffstat (limited to 'perl/lib/Wallet/Schema.pm')
-rw-r--r-- | perl/lib/Wallet/Schema.pm | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/perl/lib/Wallet/Schema.pm b/perl/lib/Wallet/Schema.pm new file mode 100644 index 0000000..cb4c93e --- /dev/null +++ b/perl/lib/Wallet/Schema.pm @@ -0,0 +1,354 @@ +# Database schema and connector for the wallet system. +# +# Written by Jon Robertson <jonrober@stanford.edu> +# Copyright 2012, 2013, 2014 +# The Board of Trustees of the Leland Stanford Junior University +# +# See LICENSE for licensing terms. + +package Wallet::Schema; + +use strict; +use warnings; + +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. +our $VERSION = '0.09'; + +__PACKAGE__->load_namespaces; +__PACKAGE__->load_components (qw/Schema::Versioned/); + +############################################################################## +# Core overrides +############################################################################## + +# 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 ($Wallet::Config::DB_DRIVER + and (defined ($Wallet::Config::DB_INFO) + or defined ($Wallet::Config::DB_NAME))) { + die "database connection information not configured\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; + } + my $user = $Wallet::Config::DB_USER; + my $pass = $Wallet::Config::DB_PASSWORD; + my %attrs = (PrintError => 0, RaiseError => 1); + my $schema = eval { $class->SUPER::connect ($dsn, $user, $pass, \%attrs) }; + if ($@) { + die "cannot connect to database: $@\n"; + } + return $schema; +} + +1; + +__END__ + +############################################################################## +# Documentation +############################################################################## + +=for stopwords +RaiseError PrintError AutoCommit ACL verifier API APIs enums keytab backend +enctypes DBI Allbery + +=head1 NAME + +Wallet::Schema - Database schema and connector for the wallet system + +=head1 SYNOPSIS + + use Wallet::Schema; + my $schema = 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 +class runs using the DBIx::Class module. + +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 Normalization Tables + +Holds the supported object types and their corresponding Perl classes: + + create table types + (ty_name varchar(16) primary key, + ty_class varchar(64)); + insert into types (ty_name, ty_class) + values ('file', 'Wallet::Object::File'); + insert into types (ty_name, ty_class) + values ('keytab', 'Wallet::Object::Keytab'); + +Holds the supported ACL schemes and their corresponding Perl classes: + + create table acl_schemes + (as_name varchar(32) primary key, + as_class varchar(64)); + 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 object types or +additional ACL schemes, you will want to add additional rows to these +tables mapping those types or schemes to Perl classes that implement the +object or ACL verifier APIs. + +=head2 ACL Tables + +A wallet ACL consists of zero or more ACL entries, each of which is a +scheme and an identifier. The scheme identifies the check that should be +performed and the identifier is additional scheme-specific information. +Each ACL references entries in the following table: + + create table acls + (ac_id integer auto_increment primary key, + ac_name varchar(255) not null, + unique (ac_name)); + +This just keeps track of unique ACL identifiers. The data is then stored +in: + + create table acl_entries + (ae_id integer not null references acls(ac_id), + ae_scheme varchar(32) + not null references acl_schemes(as_name), + ae_identifier varchar(255) not null, + primary key (ae_id, ae_scheme, ae_identifier)); + create index ae_id on acl_entries (ae_id); + +ACLs may be referred to in the API via either the numeric ID or the +human-readable name, but internally ACLs are always referenced by numeric +ID so that they can be renamed without requiring complex data +modifications. + +Currently, the ACL named C<ADMIN> (case-sensitive) is special-cased in the +Wallet::Server code and granted global access. + +Every change made to any ACL in the database will be recorded in this +table. + + create table acl_history + (ah_id integer auto_increment primary key, + ah_acl integer not null, + ah_name varchar(255) default null, + ah_action varchar(16) not null, + ah_scheme varchar(32) default null, + ah_identifier varchar(255) default null, + ah_by varchar(255) not null, + ah_from varchar(255) not null, + ah_on datetime not null); + create index ah_acl on acl_history (ah_acl); + +ah_action must be one of C<create>, C<destroy>, C<add>, C<remove>, or +C<rename> (enums aren't used for compatibility with databases other than +MySQL). For a change of type create, destroy, or rename, only the action, +the ACL name (in the case of rename, the old ACL name prior to the +rename), and the trace records (by, from, and on) are stored. For a +change to the lines of an ACL, the scheme and identifier of the line that +was added or removed are included. + +ah_by stores the authenticated identity that made the change, ah_from +stores the host from which they made the change, and ah_on stores the time +the change was made. + +=head2 Object Tables + +Each object stored in the wallet is represented by an entry in the objects +table: + + create table objects + (ob_type varchar(16) + not null references types(ty_name), + ob_name varchar(255) not null, + ob_owner integer default null references acls(ac_id), + ob_acl_get integer default null references acls(ac_id), + ob_acl_store integer default null references acls(ac_id), + ob_acl_show integer default null references acls(ac_id), + ob_acl_destroy integer default null references acls(ac_id), + ob_acl_flags integer default null references acls(ac_id), + ob_expires datetime default null, + 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) default null, + ob_stored_from varchar(255) default null, + ob_stored_on datetime default null, + ob_downloaded_by varchar(255) default null, + ob_downloaded_from varchar(255) default null, + ob_downloaded_on datetime default null, + ob_comment varchar(255) default null, + primary key (ob_name, ob_type)); + create index ob_owner on objects (ob_owner); + create index ob_expires on objects (ob_expires); + +Object names are not globally unique but only unique within their type, so +the table has a joint primary key. Each object has an owner and then up +to five more specific ACLs. The owner provides permission for get, store, +and show operations if no more specific ACL is set. It does not provide +permission for destroy or flags. + +The ob_acl_flags ACL controls who can set flags on this object. Each +object may have zero or more flags associated with it: + + create table flags + (fl_type varchar(16) + not null references objects(ob_type), + fl_name varchar(255) + not null references objects(ob_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); + +Every change made to any object in the wallet database will be recorded in +this table: + + create table object_history + (oh_id integer auto_increment primary key, + oh_type varchar(16) + not null references objects(ob_type), + oh_name varchar(255) + not null references objects(ob_name), + oh_action varchar(16) not null, + oh_field varchar(16) default null, + oh_type_field varchar(255) default null, + oh_old varchar(255) default null, + oh_new varchar(255) default null, + oh_by varchar(255) not null, + oh_from varchar(255) not null, + oh_on datetime not null); + create index oh_object on object_history (oh_type, oh_name); + +oh_action must be one of C<create>, C<destroy>, C<get>, C<store>, or +C<set>. oh_field must be one of C<owner>, C<acl_get>, C<acl_store>, +C<acl_show>, C<acl_destroy>, C<acl_flags>, C<expires>, C<flags>, or +C<type_data>. Enums aren't used for compatibility with databases other +than MySQL. + +For a change of type create, get, store, or destroy, only the action and +the trace records (by, from, and on) are stored. For changes to columns +or to the flags table, oh_field takes what attribute is changed, oh_from +takes the previous value converted to a string and oh_to takes the next +value similarly converted to a string. The special field value +"type_data" is used when type-specific data is changed, and in that case +(and only that case) some type-specific name for the data being changed is +stored in oh_type_field. + +When clearing a flag, oh_old will have the name of the flag and oh_new +will be null. When setting a flag, oh_old will be null and oh_new will +have the name of the flag. + +oh_by stores the authenticated identity that made the change, oh_from +stores the host from which they made the change, and oh_on stores the time +the change was made. + +=head2 Duo Backend Data + +Duo integration objects store some additional metadata about the +integration to aid in synchronization with Duo. + + create table duo + (du_name varchar(255) + not null references objects(ob_name), + du_key varchar(255) not null); + create index du_key on duo (du_key); + +du_key holds the Duo integration key, which is the unique name of the +integration within Duo. Additional data may be added later to represent +the other possible settings within Duo. + +=head2 Keytab Backend Data + +The keytab backend has stub support for synchronizing keys with an +external system, although no external systems are currently supported. +The permitted external systems are listed in a normalization table: + + create table sync_targets + (st_name varchar(255) primary key); + +and then the synchronization targets for a given keytab are stored in this +table: + + create table keytab_sync + (ks_name varchar(255) + not null references objects(ob_name), + ks_target varchar(255) + not null references sync_targets(st_name), + primary key (ks_name, ks_target)); + create index ks_name on keytab_sync (ks_name); + +The keytab backend supports restricting the allowable enctypes for a given +keytab. The permitted enctypes are listed in a normalization table: + + create table enctypes + (en_name varchar(255) primary key); + +and then the restrictions for a given keytab are stored in this table: + + create table keytab_enctypes + (ke_name varchar(255) + not null references objects(ob_name), + ke_enctype varchar(255) + not null references enctypes(en_name), + primary key (ke_name, ke_enctype)); + create index ke_name on keytab_enctypes (ke_name); + +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::Config(3) + +This module is part of the wallet system. The current version is +available from L<http://www.eyrie.org/~eagle/software/wallet/>. + +=head1 AUTHOR + +Russ Allbery <eagle@eyrie.org> + +=cut |