diff --git a/lib/WebGUI/DatabaseLink.pm b/lib/WebGUI/DatabaseLink.pm index d61ce6e21..6eea9272e 100644 --- a/lib/WebGUI/DatabaseLink.pm +++ b/lib/WebGUI/DatabaseLink.pm @@ -17,7 +17,9 @@ package WebGUI::DatabaseLink; use strict; use Tie::CPHash; +use WebGUI::SQL; use WebGUI::International; +use WebGUI::Utility; =head1 NAME @@ -46,6 +48,75 @@ These subroutines are available from this package: #------------------------------------------------------------------- +=head2 checkPrivileges ( $requestedPrivileges, [$overrideGrants] ) + +Checks that the database has the requested set of privileges and returns true if it +does. + +=head3 requestedPrivileges + +An array reference containing the list of privileges to check for this database. + +=head3 overrideGrants + +An array reference that allows for testing of arbitrary grants. This argument is +solely for testing, as it bypasses querying the database for grants. + +=cut + +sub checkPrivileges { + my $self = shift; + my $requestedPrivileges = shift; + my $overrideGrants = shift; + + ##Get the grants for the database, respecting the override if it has + ##been passed. + my @grants; + if (defined $overrideGrants) { + @grants = @{ $overrideGrants }; + } + else { + @grants = $self->db->buildArray('show grants for current_user'); + } + + ##Parse through the grants, building both the list of grants and the + ##database which they belong to. + my @privileges; + foreach (@grants) { + ##Checks for grants on all databases '*' or grants on a specific database + if (m/GRANT ([\w\s\d,]*?) ON ([^.]+)/) { + my ($privileges, $database) = ($1, $2); + $database =~ tr/`//d; + $database =~ s/[%*]/.*/g; + if ($self->databaseName() =~ /$database/) { + push(@privileges, (split(/, /,$privileges))); + } + } + } + + # Check if we found any privileges at all + if (! scalar @privileges) { + $self->session->errorHandler->warn( + sprintf( "DatabaseLink: Could not find SQL privileges or no privileges on database '%s' for user '%s' with database link ID '%s' using DSN '%s'", + $self->databaseName, $self->get->{username}, + $self->getId, $self->get->{DSN}, + ) + ); + return 0; + } + + # Check if all required privs are present. + return 1 if (isIn('ALL PRIVILEGES', @privileges)); + + foreach (@{ $requestedPrivileges }) { + return 0 unless (isIn(uc($_), @privileges)); + } + + return 1; +} + +#------------------------------------------------------------------- + =head2 copy ( ) Returns a new database link id, after copying the properties of this database link to a new entry. diff --git a/t/DatabaseLink.t b/t/DatabaseLink.t index 43e5b98ad..52487a6d8 100644 --- a/t/DatabaseLink.t +++ b/t/DatabaseLink.t @@ -55,7 +55,75 @@ my $DSNs = [ }, ]; -plan tests => 2 + scalar @{ $DSNs }; +#Grants for parsing tests, particularly the database name +my $grants = [ + { + dsn => 'DBI:mysql:myDb:myHost:8008', + privileges => [qw/ALTER CREATE INSERT DELETE/], + grants => [ + 'GRANT ALTER, CREATE, INSERT, DELETE ON *.* to user@localhost', + ], + privileged => 1, + comment => 'ACID on *.*, privileged', + }, + { + dsn => 'DBI:mysql:myDb:myHost:8008', + privileges => [qw/ALTER CREATE INSERT DELETE/], + grants => [ + 'GRANT ALL PRIVILEGES ON *.* to user@localhost', + ], + privileged => 1, + comment => 'ALL PRIVILEGES on *.*, privileged', + }, + { + dsn => 'DBI:mysql:myDb:myHost:8008', + privileges => [qw/ALTER CREATE INSERT DELETE/], + grants => [ + 'GRANT ALTER, CREATE, INSERT ON *.* to user@localhost', + ], + privileged => 0, + comment => 'Missing DELETE on *.*, unprivileged', + }, + { + dsn => 'DBI:mysql:myDb:myHost:8008', + privileges => [qw/ALTER CREATE INSERT DELETE/], + grants => [ + 'GRANT ALL PRIVILEGES ON myDb.* to user@localhost', + ], + privileged => 1, + comment => 'ALL PRIVILEGES on explicit db name, privileged', + }, + { + dsn => 'DBI:mysql:myDb:myHost:8008', + privileges => [qw/ALTER CREATE INSERT DELETE/], + grants => [ + 'GRANT ALL PRIVILEGES ON `myDb`.* to user@localhost', + ], + privileged => 1, + comment => 'ALL PRIVILEGES on quoted, explicit db name, privileged', + }, + { + dsn => 'DBI:mysql:myDb:myHost:8008', + privileges => [qw/ALTER CREATE INSERT DELETE/], + grants => [ + 'GRANT ALL PRIVILEGES ON `my%`.* to user@localhost', + ], + privileged => 1, + comment => 'ALL PRIVILEGES on quoted, wildcard name, privileged', + }, + { + dsn => 'DBI:mysql:yourDb:myHost:8008', + privileges => [qw/ALTER CREATE INSERT DELETE/], + grants => [ + 'GRANT ALL PRIVILEGES ON `my%`.* to user@localhost', + ], + privileged => 0, + comment => 'ALL PRIVILEGES on wrong db, unprivileged', + }, +]; + + +plan tests => 2 + scalar @{ $DSNs } + scalar @{ $grants }; my $dbLink = WebGUI::DatabaseLink->new($session, 0); is($dbLink->get->{DSN}, $session->config->get('dsn'), 'DSN set correctly for default database link'); @@ -68,6 +136,15 @@ foreach my $dsn (@{ $DSNs }) { $dbl->delete; } +foreach my $grant (@{ $grants }) { + my $dbl = WebGUI::DatabaseLink->create($session, { DSN => $grant->{dsn} }); + is( + $dbl->checkPrivileges($grant->{privileges}, $grant->{grants}), + $grant->{privileged}, + $grant->{comment} + ); + $dbl->delete; +} END { foreach my $link ($dbLink, ) {