diff --git a/docs/changelog/6.x.x.txt b/docs/changelog/6.x.x.txt
index e93110d9d..d358895cb 100644
--- a/docs/changelog/6.x.x.txt
+++ b/docs/changelog/6.x.x.txt
@@ -14,6 +14,8 @@
sending attachments, HTML messages, and more. This will introduce many new
options for developers.
- The group mail screen now allows sending of HTML messages.
+ - Added prequery statements to the SQLReport and configurable allowed statements
+ to the databas link properties. (Martin Kamerbeek / Procolix)
6.8.6
- Added logic to deal with case sensitivity and whitespace problems in LDAP
diff --git a/docs/upgrades/upgrade_6.8.5-6.9.0.pl b/docs/upgrades/upgrade_6.8.5-6.9.0.pl
index 247eb0a99..1a7d24eea 100644
--- a/docs/upgrades/upgrade_6.8.5-6.9.0.pl
+++ b/docs/upgrades/upgrade_6.8.5-6.9.0.pl
@@ -26,9 +26,22 @@ addSearchEngine();
addEMSTemplates();
addEMSTables();
updateTemplates();
+updateDatabaseLinksAndSQLReport();
finish($session); # this line required
+#-------------------------------------------------
+sub updateDatabaseLinksAndSQLReport {
+ print "\tUpdating the Database link and SQLReport Tables.\n";
+ $session->db->write('alter table databaseLink add column allowedKeywords text');
+ $session->db->write('update databaseLink set allowedKeywords="select\ndecsribe\nshow"');
+ $session->db->write('alter table SQLReport add column prequeryStatements1 text');
+ $session->db->write('alter table SQLReport add column prequeryStatements2 text');
+ $session->db->write('alter table SQLReport add column prequeryStatements3 text');
+ $session->db->write('alter table SQLReport add column prequeryStatements4 text');
+ $session->db->write('alter table SQLReport add column prequeryStatements5 text');
+}
+
#-------------------------------------------------
sub updateTemplates {
print "\tUpdating base templates for XHTML compliance, and a cleaner look.\n" unless ($quiet);
diff --git a/lib/WebGUI/Asset/Wobject/SQLReport.pm b/lib/WebGUI/Asset/Wobject/SQLReport.pm
index 694dada5d..2fd47139d 100644
--- a/lib/WebGUI/Asset/Wobject/SQLReport.pm
+++ b/lib/WebGUI/Asset/Wobject/SQLReport.pm
@@ -47,7 +47,11 @@ sub definition {
dbQuery1=>{
fieldType=>"codearea",
defaultValue=>undef
- },
+ },
+ prequeryStatements1=>{
+ fieldType=>"codearea",
+ defaultValue=>undef
+ },
preprocessMacros1=>{
fieldType=>"yesNo",
defaultValue=>0
@@ -64,6 +68,10 @@ sub definition {
fieldType=>"codearea",
defaultValue=>undef
},
+ prequeryStatements2=>{
+ fieldType=>"codearea",
+ defaultValue=>undef
+ },
preprocessMacros2=>{
fieldType=>"yesNo",
defaultValue=>0
@@ -80,6 +88,10 @@ sub definition {
fieldType=>"codearea",
defaultValue=>undef
},
+ prequeryStatements3=>{
+ fieldType=>"codearea",
+ defaultValue=>undef
+ },
preprocessMacros3=>{
fieldType=>"yesNo",
defaultValue=>0
@@ -96,6 +108,10 @@ sub definition {
fieldType=>"codearea",
defaultValue=>undef
},
+ prequeryStatements4=>{
+ fieldType=>"codearea",
+ defaultValue=>undef
+ },
preprocessMacros4=>{
fieldType=>"yesNo",
defaultValue=>0
@@ -112,6 +128,10 @@ sub definition {
fieldType=>"codearea",
defaultValue=>undef
},
+ prequeryStatements5=>{
+ fieldType=>"codearea",
+ defaultValue=>undef
+ },
preprocessMacros5=>{
fieldType=>"yesNo",
defaultValue=>0
@@ -172,55 +192,62 @@ sub getEditForm {
|);
for my $nr (1..5) {
- # Set TR class for this query properties
- $tabform->getTab("properties")->trClass("query".$nr);
+ # Set TR class for this query properties
+ $tabform->getTab("properties")->trClass("query".$nr);
- $tabform->getTab("properties")->readOnly(
- -value=>"
",
- -label=>join '', "", $i18n->get('4'), $nr,":",
- );
- $tabform->getTab("properties")->yesNo(
- -name=>"preprocessMacros".$nr,
- -label=>$i18n->get(15),
- -hoverHelp=>$i18n->get('15 description'),
- -value=>$self->getValue("preprocessMacros".$nr)
- );
- $tabform->getTab("properties")->textarea(
- -name=>"placeholderParams".$nr,
- -label=>$i18n->get('Placeholder Parameters'),
- -hoverHelp=>$i18n->get('Placeholder Parameters description'),
- -value=>$self->getValue("placeholderParams".$nr)
- );
- $tabform->getTab("properties")->codearea(
- -name=>"dbQuery".$nr,
- -label=>$i18n->get(4),
- -hoverHelp=>$i18n->get('4 description'),
- -value=>$self->getValue("dbQuery".$nr)
- );
- $tabform->getTab("properties")->databaseLink(
- -name=>"databaseLinkId".$nr,
- -value=>$self->getValue("databaseLinkId".$nr)
- );
+ $tabform->getTab("properties")->readOnly(
+ -value=>"
",
+ -label=>join '', "", $i18n->get('4'), $nr,":",
+ );
+ $tabform->getTab("properties")->yesNo(
+ -name=>"preprocessMacros".$nr,
+ -label=>$i18n->get(15),
+ -hoverHelp=>$i18n->get('15 description'),
+ -value=>$self->getValue("preprocessMacros".$nr)
+ );
+ $tabform->getTab("properties")->textarea(
+ -name=>"placeholderParams".$nr,
+ -label=>$i18n->get('Placeholder Parameters'),
+ -hoverHelp=>$i18n->get('Placeholder Parameters description'),
+ -value=>$self->getValue("placeholderParams".$nr)
+ );
+ $tabform->getTab("properties")->codearea(
+ -name => "prequeryStatements".$nr,
+ -label => $i18n->get('Prequery statements'),
+ -hoverHelp => $i18n->get('Prequery statements description'),
+ -value => $self->getValue("prequeryStatements".$nr),
+ );
+ $tabform->getTab("properties")->codearea(
+ -name=>"dbQuery".$nr,
+ -label=>$i18n->get(4),
+ -hoverHelp=>$i18n->get('4 description'),
+ -value=>$self->getValue("dbQuery".$nr)
+ );
+ $tabform->getTab("properties")->databaseLink(
+ -name=>"databaseLinkId".$nr,
+ -value=>$self->getValue("databaseLinkId".$nr)
+ );
- # Add a "Add another query" button
- if ($nr < 5 and ($self->get("dbQuery".($nr+1)) eq "" || ($self->get("dbQuery".($nr)) eq "" and $self->get("dbQuery".($nr+1)) ne ""))) {
- $tabform->getTab("properties")->button(
- -value=>$i18n->get('Add another query'),
- -extras=>'onclick="toggleQuery(\''.($nr+1).'\'); this.style.display=\'none\';"',
- -noWait=>1
- );
- }
+ # Add a "Add another query" button
+ if ($nr < 5 and ($self->get("dbQuery".($nr+1)) eq "" || ($self->get("dbQuery".($nr)) eq "" and $self->get("dbQuery".($nr+1)) ne ""))) {
+ $tabform->getTab("properties")->button(
+ -value=>$i18n->get('Add another query'),
+ -extras=>'onclick="toggleQuery(\''.($nr+1).'\'); this.style.display=\'none\';"',
+ -noWait=>1
+ );
+ }
- # Make empty query blocks invisible
- if ($nr > 1 && ($self->get("dbQuery".$nr) eq "" || $self->get("dbQuery".($nr-1)) eq "")) {
- $tabform->getTab("properties")->raw(qq|
-
- |);
- }
+ # Make empty query blocks invisible
+ if ($nr > 1 && ($self->get("dbQuery".$nr) eq "" || $self->get("dbQuery".($nr-1)) eq "")) {
+ $tabform->getTab("properties")->raw(qq|
+
+ |);
+ }
}
+
# Undefine TR class
$tabform->getTab("properties")->trClass();
@@ -229,7 +256,8 @@ sub getEditForm {
-label=>$i18n->get(14),
-hoverHelp=>$i18n->get('14 description'),
-value=>$self->getValue("paginateAfter")
- );
+ );
+
return $tabform;
}
@@ -308,7 +336,6 @@ sub _parsePlaceholderParams {
#-------------------------------------------------------------------
-
sub _processQuery {
my $self = shift;
my $nr = shift || 1;
@@ -328,14 +355,38 @@ sub _processQuery {
} else {
$query = $self->{_query}{$nr}{dbQuery};
}
-
+
my $i18n = WebGUI::International->new($self->session,"Asset_SQLReport");
push(@{$self->{_debug_loop}},{'debug.output'=>$i18n->get(17).$query});
push(@{$self->{_debug_loop}},{'debug.output'=>$i18n->get('debug placeholder parameters').join(",",@$placeholderParams)});
my $dbLink = WebGUI::DatabaseLink->new($self->session,$self->{_query}{$nr}{databaseLinkId});
- my $dbh = $dbLink->db;
+
+ my $dbh = $dbLink->db;
if (defined $dbh) {
- if ($query =~ /^select/i || $query =~ /^show/i || $query =~ /^describe/i) {
+ if ($dbLink->queryIsAllowed($query)) {
+# if ($query =~ /^select/i || $query =~ /^show/i || $query =~ /^describe/i) {
+ # Check and execute prequery statements first
+ foreach (split(/\n/, $self->getValue("prequeryStatements".$nr))) {
+ my $prequeryStatement = $_;
+ WebGUI::Macro::process($self->session, \$prequeryStatement) if ($self->{_query}{$nr}{preprocessMacros});
+
+ if ($dbLink->queryIsAllowed($prequeryStatement)) {
+ my $sth = $dbh->unconditionalRead($prequeryStatement);
+ if ($sth->errorCode > 0) {
+ push(@{$self->{_debug_loop}},{
+ 'debug.output' => $i18n->get('Prequery error').' "'.$prequeryStatement.'": '.$sth->errorMessage
+ });
+ } else {
+ push(@{$self->{_debug_loop}},{
+ 'debug.output' => "Prequery: $prequeryStatement"
+ });
+ }
+ $sth->finish;
+ } else {
+ push(@{$self->{_debug_loop}},{'debug.output'=>$i18n->get("Prequery not allowed").$prequeryStatement});
+ }
+ }
+
my $url = $self->getUrl('func=view');
foreach ($self->session->form->param) {
unless ($_ eq "pn" || $_ eq "func" || $_ =~ /identifier/i || $_ =~ /password/i) {
diff --git a/lib/WebGUI/DatabaseLink.pm b/lib/WebGUI/DatabaseLink.pm
index ec7c3cbae..e90e94296 100644
--- a/lib/WebGUI/DatabaseLink.pm
+++ b/lib/WebGUI/DatabaseLink.pm
@@ -230,18 +230,43 @@ sub new {
DSN=>$session->config->get("dsn"),
username=>$session->config->get("dbuser"),
identifier=>$session->config->get("dbpass"),
- title=>"WebGUI Database"
+ title=>"WebGUI Database",
+ allowedKeywords=>"select\ndescribe\ndesc\nshow",
);
} else {
%databaseLink = $session->db->quickHash("select * from databaseLink where databaseLinkId=".$session->db->quote($databaseLinkId));
}
}
- return undef unless $databaseLink{databaseLinkId};
+
+ return undef unless defined($databaseLink{databaseLinkId});
bless {_session=>$session, _databaseLink => \%databaseLink }, $class;
}
#-------------------------------------------------------------------
+=head2 queryIsAllowed ( query )
+
+Returns a boolean indicating is the supplied query is allowed for this database link.
+
+=head3 query
+
+The SQL query which is to be investigated.
+
+=cut
+
+sub queryIsAllowed {
+ my $self = shift;
+ my $query = shift;
+
+ foreach (split(/\s+/, $self->{_databaseLink}{allowedKeywords})) {
+ return 1 if ($query =~ m/^$_/i);
+ }
+
+ return 0;
+}
+
+#-------------------------------------------------------------------
+
=head2 session
Returns a reference to the current session.
diff --git a/lib/WebGUI/Form/DatabaseLink.pm b/lib/WebGUI/Form/DatabaseLink.pm
index 9732f5793..b9a115526 100644
--- a/lib/WebGUI/Form/DatabaseLink.pm
+++ b/lib/WebGUI/Form/DatabaseLink.pm
@@ -127,7 +127,8 @@ sub toHtmlWithWrapper {
$subtext = $self->session->icon->edit("op=editDatabaseLink;lid=".$self->get("value").";afterEdit=".$self->session->url->escape($self->get("afterEdit")));
}
$subtext .= $self->session->icon->manage("op=listDatabaseLinks");
- $self->get("subtext") = $subtext . $self->get("subtext");
+ $self->set("subtext", $subtext . $self->get("subtext"));
+# $self->get("subtext") = $subtext . $self->get("subtext");
}
return $self->SUPER::toHtmlWithWrapper;
}
diff --git a/lib/WebGUI/Help/Asset_SQLReport.pm b/lib/WebGUI/Help/Asset_SQLReport.pm
index 0cc55e2bd..e83d9256d 100644
--- a/lib/WebGUI/Help/Asset_SQLReport.pm
+++ b/lib/WebGUI/Help/Asset_SQLReport.pm
@@ -25,6 +25,11 @@ our $HELP = {
description => '15 description',
namespace => 'Asset_SQLReport',
},
+ {
+ title => 'Prequery statements',
+ description => 'Prequery statements description',
+ namespace => 'Asset_SQLReport',
+ },
{
title => '4',
description => '4 description',
@@ -44,7 +49,11 @@ our $HELP = {
{
tag => 'wobjects using',
namespace => 'Wobject'
- }
+ },
+ {
+ tag => 'database links manage',
+ namespace => 'WebGUI',
+ },
]
},
'sql report template' => {
diff --git a/lib/WebGUI/Help/WebGUI.pm b/lib/WebGUI/Help/WebGUI.pm
index 1cab5aeac..ff620dab1 100644
--- a/lib/WebGUI/Help/WebGUI.pm
+++ b/lib/WebGUI/Help/WebGUI.pm
@@ -665,6 +665,11 @@ our $HELP = {
description => '995 description',
namespace => 'WebGUI',
},
+ {
+ title => 'allowed keywords',
+ description => 'allowed keywords description',
+ namespace => 'WebGUI',
+ },
],
related => [
{
diff --git a/lib/WebGUI/Operation/DatabaseLink.pm b/lib/WebGUI/Operation/DatabaseLink.pm
index 7721ceb60..90d9b7755 100644
--- a/lib/WebGUI/Operation/DatabaseLink.pm
+++ b/lib/WebGUI/Operation/DatabaseLink.pm
@@ -65,8 +65,10 @@ sub www_deleteDatabaseLink {
sub www_deleteDatabaseLinkConfirm {
my $session = shift;
return $session->privilege->insufficient unless ($session->user->isInGroup(3));
+ return $session->privilege->vitalComponent if ($session->form->process("dlid") == 0);
+
WebGUI::DatabaseLink->new($session,$session->form->process("dlid"))->delete;
- return www_listDatabaseLinks();
+ return www_listDatabaseLinks($session);
}
#-------------------------------------------------------------------
@@ -76,7 +78,8 @@ sub www_editDatabaseLink {
my ($output, %db, $f);
tie %db, 'Tie::CPHash';
if ($session->form->process("dlid") eq "new") {
-
+ # Default values are SELECT, DESCRIBE and SHOW
+ $db{allowedKeywords} = "select\ndescribe\nshow";
} elsif ($session->form->process("dlid") eq "0") {
} else {
@@ -123,6 +126,12 @@ sub www_editDatabaseLink {
-hoverHelp => $i18n->get('995 description'),
-value => $db{identifier},
);
+ $f->textarea(
+ -name => "allowedKeywords",
+ -label => $i18n->get('allowed keywords'),
+ -hoverHelp => $i18n->get('allowed keywords description'),
+ -value => $db{allowedKeywords},
+ );
$f->submit;
$output .= $f->print;
return _submenu($session,$output,"990","database link add/edit");
@@ -130,20 +139,25 @@ sub www_editDatabaseLink {
#-------------------------------------------------------------------
sub www_editDatabaseLinkSave {
+ my ($allowedKeywords);
my $session = shift;
return $session->privilege->insufficient unless ($session->user->isInGroup(3));
+
+ # Convert enters to a single \n.
+ ($allowedKeywords = $session->form->process("allowedKeywords")) =~ s/\s+/\n/g;
my $params = {
title=>$session->form->process("title"),
username=>$session->form->process("dbusername"),
identifier=>$session->form->process("dbidentifier"),
- DSN=>$session->form->process("DSN")
+ DSN=>$session->form->process("DSN"),
+ allowedKeywords=>$allowedKeywords,
};
if ($session->form->process("dlid") eq "new") {
WebGUI::DatabaseLink->create($session,$params);
} else {
WebGUI::DatabaseLink->new($session,$session->form->process("dlid"))->set($params);
}
- return www_listDatabaseLinks();
+ return www_listDatabaseLinks($session);
}
#-------------------------------------------------------------------
@@ -154,12 +168,14 @@ sub www_listDatabaseLinks {
my $output = '';
my $i18n = WebGUI::International->new($session);
foreach my $id (keys %{$links}) {
- $output .= ' | '.$i18n->get(1076).' |
';
- $output = '| '
- .$session->icon->delete('op=deleteDatabaseLink;dlid='.$id)
- .$session->icon->edit('op=editDatabaseLink;dlid='.$id)
- .$session->icon->copy('op=copyDatabaseLink;dlid='.$id)
- .' | ';
+# $output .= '
| '.$i18n->get(1076).' |
';
+ $output .= '| ';
+ if ($id ne '0') {
+ $output .= $session->icon->delete('op=deleteDatabaseLink;dlid='.$id)
+ .$session->icon->edit('op=editDatabaseLink;dlid='.$id)
+ .$session->icon->copy('op=copyDatabaseLink;dlid='.$id);
+ }
+ $output .= ' | ';
$output .= ''.$links->{$id}.' |
';
}
$output .= '
';
diff --git a/lib/WebGUI/i18n/English/Asset_SQLReport.pm b/lib/WebGUI/i18n/English/Asset_SQLReport.pm
index ea3729b99..34a1a163b 100644
--- a/lib/WebGUI/i18n/English/Asset_SQLReport.pm
+++ b/lib/WebGUI/i18n/English/Asset_SQLReport.pm
@@ -54,9 +54,10 @@ while the second question mark will contain the form variable "field1".
},
'4 description' => {
- message => q|This is a standard SQL query. If you are unfamiliar with SQL then you'll likely not want to use this wobject.
-A question mark ? in the query represents a placeholder. Note that the ? is not enclosed in quotation marks, even when the placeholder represents a string. |,
- lastUpdated => 1119841649,
+ message => q|This is a standard SQL query. If you are unfamiliar with SQL then you'll likely not want to use this wobject.
+A question mark ? in the query represents a placeholder. Note that the ? is not enclosed in quotation marks, even when the placeholder represents a string.
+The keywords that are allowed are defined in the database link properties. The allowed keywords for the WebGUI database are SELECT, DESCRIBE and SHOW.
|,
+ lastUpdated => 1119841650,
},
'14 description' => {
@@ -277,7 +278,24 @@ will always be false for query5.
message => q|Add another query|,
lastUpdated => 1031514049
},
-
+ 'Prequery not allowed' => {
+ message => q|Debug: Prequery statement is not allowed: |,
+ lastUpdated => 0,
+ },
+ 'Prequery error' => {
+ message => q|Debug: An error occured in prequery|,
+ lastUpdated => 0,
+ },
+ 'Prequery statements' => {
+ message => q|Prequery statements|,
+ lastUpdated => 0,
+ },
+ 'Prequery statements description' => {
+ message => q|Prequery statements are sql statements executed before the real query. You can use prequery statements for instance to set variables that you want to use in the real query. For example:
+ set @myVariable := 1
+The prequery statements are seperated from each other by returns and cannot use placeholders. You can use macro's within the prequery statements, however. Please note that prequery statements are only visible in the query they belong to and that you can only use statements that are allowed by the database link.
|,
+ lastUpdated => 0,
+ },
};
1;
diff --git a/lib/WebGUI/i18n/English/WebGUI.pm b/lib/WebGUI/i18n/English/WebGUI.pm
index 083b6d3c8..b344f7d27 100644
--- a/lib/WebGUI/i18n/English/WebGUI.pm
+++ b/lib/WebGUI/i18n/English/WebGUI.pm
@@ -3849,6 +3849,16 @@ Message Boards hold forums for users. There are many different Wobjects in WebG
message => q|unknown user|,
lastUpdated => 1135205716,
},
+
+ 'allowed keywords' => {
+ message => q|Allowed keywords|,
+ lastUpdated => 0,
+ },
+
+ 'allowed keywords description' => {
+ message => q|You can enter the statements that are allowed for this databaselink. A safe (read-only) choice is SELECT, DESCRIBE and SHOW. The different keywords should be seperated from each other by whitespace.|,
+ lastUpdated => 0,
+ },
};