Provide a framework for CSRF protection, with tests.
Add CSRF protection to Asset editSave, AssetManager, VersionTags and Group operations.
This commit is contained in:
parent
4664ab7035
commit
5e4db3adb4
19 changed files with 362 additions and 62 deletions
|
|
@ -2,6 +2,7 @@
|
|||
- fixed #10606: shelf selector
|
||||
- fixed: Have just 1 list of protected groups. Use that everywhere.
|
||||
- fixed #10488: Map.gif is missing for Map wobject
|
||||
- fixed #10553: WebGUI vulnerable to CSRF attacks
|
||||
|
||||
7.7.13
|
||||
- fixed #10574: Creating Calendar Entry
|
||||
|
|
|
|||
|
|
@ -875,6 +875,7 @@ sub getEditForm {
|
|||
name=>"func",
|
||||
value=>"editSave"
|
||||
});
|
||||
$tabform->csrfToken();
|
||||
my $assetId;
|
||||
my $class;
|
||||
if ($self->getId eq "new") {
|
||||
|
|
@ -2707,19 +2708,20 @@ sub www_edit {
|
|||
|
||||
=head2 www_editSave ( )
|
||||
|
||||
Saves and updates history. If canEdit, returns www_manageAssets() if a new Asset is created, otherwise returns www_view(). Will return an insufficient Privilege if canEdit returns False.
|
||||
Saves and updates history. If canEdit, returns www_manageAssets() if a new Asset is created, otherwise returns www_view(). Will return an insufficient Privilege if canEdit returns False, or if the submitted form does not pass the C<$session->form->validToken> check.
|
||||
|
||||
NOTE: Don't try to override or overload this method. It won't work. What you are looking for is processPropertiesFromFormPost().
|
||||
|
||||
=cut
|
||||
|
||||
sub www_editSave {
|
||||
my $self = shift;
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
|
||||
##If this is a new asset (www_add), the parent may be locked. We should still be able to add a new asset.
|
||||
my $isNewAsset = $self->session->form->process("assetId") eq "new" ? 1 : 0;
|
||||
return $self->session->privilege->locked() if (!$self->canEditIfLocked and !$isNewAsset);
|
||||
return $self->session->privilege->insufficient() unless $self->canEdit;
|
||||
my $isNewAsset = $session->form->process("assetId") eq "new" ? 1 : 0;
|
||||
return $session->privilege->locked() if (!$self->canEditIfLocked and !$isNewAsset);
|
||||
return $session->privilege->insufficient() unless $self->canEdit && $session->form->validToken;
|
||||
if ($self->session->config("maximumAssets")) {
|
||||
my ($count) = $self->session->db->quickArray("select count(*) from asset");
|
||||
my $i18n = WebGUI::International->new($self->session, "Asset");
|
||||
|
|
|
|||
|
|
@ -254,15 +254,24 @@ sub www_copy {
|
|||
|
||||
=head2 www_copyList ( )
|
||||
|
||||
Copies to clipboard assets in a list, then returns self calling method www_manageAssets(), if canEdit. Otherwise returns AdminConsole rendered insufficient privilege.
|
||||
|
||||
Checks to see if the current user canEdit the parent containting the assets that
|
||||
are being copied. If that's not true, or if the CSRF token is missing, then
|
||||
return insufficient privileges.
|
||||
|
||||
Copies the list of assets in the C<assetId> form variable, checking each one for edit privileges.
|
||||
|
||||
Returns the user to either the screen set by the C<proceed> form variable, or to
|
||||
the Asset Manager.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_copyList {
|
||||
my $self = shift;
|
||||
return $self->session->privilege->insufficient() unless $self->canEdit;
|
||||
foreach my $assetId ($self->session->form->param("assetId")) {
|
||||
my $asset = WebGUI::Asset->newByDynamicClass($self->session,$assetId);
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
return $self->session->privilege->insufficient() unless $self->canEdit && $session->form->validToken;
|
||||
foreach my $assetId ($session->form->param("assetId")) {
|
||||
my $asset = WebGUI::Asset->newByDynamicClass($session,$assetId);
|
||||
if ($asset->canEdit) {
|
||||
my $newAsset = $asset->duplicate({skipAutoCommitWorkflows => 1});
|
||||
$newAsset->update({ title=>$newAsset->getTitle.' (copy)'});
|
||||
|
|
@ -270,7 +279,7 @@ sub www_copyList {
|
|||
}
|
||||
}
|
||||
if ($self->session->form->process("proceed") ne "") {
|
||||
my $method = "www_".$self->session->form->process("proceed");
|
||||
my $method = "www_".$session->form->process("proceed");
|
||||
return $self->$method();
|
||||
}
|
||||
return $self->www_manageAssets();
|
||||
|
|
@ -344,21 +353,30 @@ sub www_cut {
|
|||
|
||||
=head2 www_cutList ( )
|
||||
|
||||
Cuts assets in a list (removes to clipboard), then returns self calling method www_manageAssets(), if canEdit. Otherwise returns AdminConsole rendered insufficient privilege.
|
||||
Checks to see if the current user canEdit the parent containting the assets that
|
||||
are being cut. If that's not true, or if the CSRF token is missing, then
|
||||
return insufficient privileges.
|
||||
|
||||
Cuts the list of assets in the C<assetId> form variable, checking each one for edit privileges
|
||||
and to see if it's a system asset.
|
||||
|
||||
Returns the user to either the screen set by the C<proceed> form variable, or to
|
||||
the Asset Manager.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_cutList {
|
||||
my $self = shift;
|
||||
return $self->session->privilege->insufficient() unless $self->canEdit;
|
||||
foreach my $assetId ($self->session->form->param("assetId")) {
|
||||
my $asset = WebGUI::Asset->newByDynamicClass($self->session,$assetId);
|
||||
my $session = $self->session;
|
||||
return $session->privilege->insufficient() unless $self->canEdit && $session->form->validToken;
|
||||
foreach my $assetId ($session->form->param("assetId")) {
|
||||
my $asset = WebGUI::Asset->newByDynamicClass($session,$assetId);
|
||||
if ($asset->canEdit && !$asset->get('isSystem')) {
|
||||
$asset->cut;
|
||||
}
|
||||
}
|
||||
if ($self->session->form->process("proceed") ne "") {
|
||||
my $method = "www_".$self->session->form->process("proceed");
|
||||
if ($session->form->process("proceed") ne "") {
|
||||
my $method = "www_".$session->form->process("proceed");
|
||||
return $self->$method();
|
||||
}
|
||||
return $self->www_manageAssets();
|
||||
|
|
@ -368,22 +386,31 @@ sub www_cutList {
|
|||
|
||||
=head2 www_duplicateList ( )
|
||||
|
||||
Creates a bunch of duplicate assets under the same parent.
|
||||
Checks to see if the current user canEdit the parent containting the assets that
|
||||
are being duplicated. If that's not true, or if the CSRF token is missing, then
|
||||
return insufficient privileges.
|
||||
|
||||
Duplicates (copy and paste immediately) the list of assets in the C<assetId>
|
||||
form variable, checking each one for edit privileges.
|
||||
|
||||
Returns the user to either the screen set by the C<proceed> form variable, or to
|
||||
the Asset Manager.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_duplicateList {
|
||||
my $self = shift;
|
||||
return $self->session->privilege->insufficient() unless $self->canEdit;
|
||||
foreach my $assetId ($self->session->form->param("assetId")) {
|
||||
my $asset = WebGUI::Asset->newByDynamicClass($self->session,$assetId);
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
return $session->privilege->insufficient() unless $self->canEdit && $session->form->validToken;
|
||||
foreach my $assetId ($session->form->param("assetId")) {
|
||||
my $asset = WebGUI::Asset->newByDynamicClass($session,$assetId);
|
||||
if ($asset->canEdit) {
|
||||
my $newAsset = $asset->duplicate;
|
||||
$newAsset->update({ title=>$newAsset->getTitle.' (copy)'});
|
||||
}
|
||||
}
|
||||
if ($self->session->form->process("proceed") ne "") {
|
||||
my $method = "www_".$self->session->form->process("proceed");
|
||||
if ($session->form->process("proceed") ne "") {
|
||||
my $method = "www_".$session->form->process("proceed");
|
||||
return $self->$method();
|
||||
}
|
||||
return $self->www_manageAssets();
|
||||
|
|
@ -506,15 +533,21 @@ sub www_paste {
|
|||
|
||||
=head2 www_pasteList ( )
|
||||
|
||||
Pastes a selection of assets. If canEdit is False, returns an insufficient privileges page.
|
||||
Returns the user to the manageAssets screen.
|
||||
Checks to see if the current user canEdit the parent containting the assets that
|
||||
are being pasted. If that's not true, or if the CSRF token is missing, then
|
||||
return insufficient privileges.
|
||||
|
||||
Pastes the list of assets in the C<assetId> form variable, checking each one for edit privileges.
|
||||
|
||||
Returns the user to either the screen set by the C<proceed> form variable, or to
|
||||
the Asset Manager.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_pasteList {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
return $session->privilege->insufficient() unless $self->canEdit;
|
||||
return $session->privilege->insufficient() unless $self->canEdit && $session->form->validToken;
|
||||
my $form = $session->form;
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
##Need to store the list of assetIds for the status subroutine
|
||||
|
|
|
|||
|
|
@ -1020,8 +1020,10 @@ sub www_setRank {
|
|||
=head2 www_setRanks ( )
|
||||
|
||||
Utility method for the AssetManager. Reorders 1 pagefull of assets via rank.
|
||||
AssetIds are passed in via the C<assetId> form variable.
|
||||
|
||||
If the current user cannot edit the current asset, it returns the insufficient privileges page.
|
||||
If the current user cannot edit the current asset, or if a valid CSRF token
|
||||
is not submitted with the form, it returns the insufficient privileges page.
|
||||
|
||||
Returns the user to the manage assets screen.
|
||||
|
||||
|
|
@ -1030,7 +1032,7 @@ Returns the user to the manage assets screen.
|
|||
sub www_setRanks {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
return $session->privilege->insufficient() unless $session->asset->canEdit;
|
||||
return $session->privilege->insufficient() unless $session->asset->canEdit && $session->form->validToken;
|
||||
my $i18n = WebGUI::International->new($session, 'Asset');
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
my $form = $session->form;
|
||||
|
|
|
|||
|
|
@ -321,7 +321,11 @@ sub www_delete {
|
|||
|
||||
=head2 www_deleteList
|
||||
|
||||
Moves list of assets to trash, returns www_manageAssets() method of self if canEdit. Otherwise returns AdminConsole rendered insufficient privilege.
|
||||
Checks to see if a valid CSRF token was received. If not, then it returns insufficient privilege.
|
||||
|
||||
Moves list of assets to trash, checking each to see if the user canEdit,
|
||||
and canEditIfLocked. Returns the user to manageTrash, or to the screen set
|
||||
by the form variable C<proceeed>.
|
||||
|
||||
=cut
|
||||
|
||||
|
|
@ -333,6 +337,7 @@ sub www_deleteList {
|
|||
my $form = $session->form;
|
||||
my @assetIds = $form->param('assetId');
|
||||
$pb->start($i18n->get('Delete Assets'), $session->url->extras('adminConsole/assets.gif'));
|
||||
return $self->session->privilege->insufficient() unless $session->form->validToken;
|
||||
ASSETID: foreach my $assetId (@assetIds) {
|
||||
my $asset = eval { WebGUI::Asset->newPending($session,$assetId); };
|
||||
if ($@) {
|
||||
|
|
@ -404,6 +409,7 @@ sub www_manageTrash {
|
|||
$output .= '
|
||||
assetManager.AddButton("'.$i18n->get("restore").'","restoreList","manageTrash");
|
||||
assetManager.AddButton("'.$i18n->get("purge").'","purgeList","manageTrash");
|
||||
assetManager.AddFormHidden({ name:"webguiCsrfToken", value:"'.$self->session->scratch->get('webguiCsrfToken').'"});
|
||||
assetManager.Write();
|
||||
var assetListSelectAllToggle = false;
|
||||
function toggleAssetListSelectAll(form) {
|
||||
|
|
@ -427,11 +433,14 @@ sub www_manageTrash {
|
|||
|
||||
Purges a piece of content, including all it's revisions, from the system permanently.
|
||||
|
||||
Returns insufficient privileges unless the submitted form passes the validToken check.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_purgeList {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
return $session->privilege->insufficient() unless $session->form->validToken;
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
my $i18n = WebGUI::International->new($session, 'Asset');
|
||||
$pb->start($i18n->get('purge'), $session->url->extras('adminConsole/assets.gif'));
|
||||
|
|
|
|||
|
|
@ -397,9 +397,10 @@ ENDHTML
|
|||
$output .= '</ol>';
|
||||
|
||||
### The page of assets
|
||||
$output .= sprintf <<EOHTML, $session->asset->getUrl, $i18n->get( 'with selected' ), $i18n->get( "update" ), $i18n->get( "delete" ), $i18n->get( '43' ), $i18n->get( 'cut' ), $i18n->get( "Copy" ), $i18n->get( "duplicate" );
|
||||
$output .= sprintf <<EOHTML, $session->asset->getUrl, WebGUI::Form::CsrfToken->new($session)->toHtml, $i18n->get( 'with selected' ), $i18n->get( "update" ), $i18n->get( "delete" ), $i18n->get( '43' ), $i18n->get( 'cut' ), $i18n->get( "Copy" ), $i18n->get( "duplicate" );
|
||||
<div>
|
||||
<form method="post" enctype="multipart/form-data" action="%s">
|
||||
%s
|
||||
<input type="hidden" name="func" value="manageAssets" />
|
||||
<input type="hidden" name="proceed" value="manageAssets" />
|
||||
<div id="dataTableContainer">
|
||||
|
|
|
|||
70
lib/WebGUI/Form/CsrfToken.pm
Normal file
70
lib/WebGUI/Form/CsrfToken.pm
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package WebGUI::Form::CsrfToken;
|
||||
|
||||
=head1 LEGAL
|
||||
|
||||
-------------------------------------------------------------------
|
||||
WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
-------------------------------------------------------------------
|
||||
Please read the legal notices (docs/legal.txt) and the license
|
||||
(docs/license.txt) that came with this distribution before using
|
||||
this software.
|
||||
-------------------------------------------------------------------
|
||||
http://www.plainblack.com info@plainblack.com
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use base 'WebGUI::Form::Hidden';
|
||||
use WebGUI::International;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Package WebGUI::Form::CsrfToken
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Creates a hidden field to use for CSRF prevention..
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
This is a subclass of WebGUI::Form::Hidden.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
The following methods are specifically available from this class. Check the superclass for additional methods.
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getName ( session )
|
||||
|
||||
Returns the human readable name of this control.
|
||||
|
||||
=cut
|
||||
|
||||
sub getName {
|
||||
my ($self, $session) = @_;
|
||||
return WebGUI::International->new($session, 'WebGUI')->get('csrfToken');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 toHtmlAsHidden ( )
|
||||
|
||||
Renders an input tag of type hidden.
|
||||
|
||||
=cut
|
||||
|
||||
sub toHtmlAsHidden {
|
||||
my $self = shift;
|
||||
$self->set('name', 'webguiCsrfToken');
|
||||
$self->set('value', $self->session->scratch->get('webguiCsrfToken'));
|
||||
return $self->SUPER::toHtmlAsHidden();
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
|
|
@ -290,7 +290,7 @@ A WebGUI::Session object
|
|||
|
||||
sub www_addGroupsToGroupSave {
|
||||
my $session = shift;
|
||||
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")));
|
||||
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")) && $session->form->validToken);
|
||||
my $group = WebGUI::Group->new($session,$session->form->process("gid"));
|
||||
my @groups = $session->form->group('groups');
|
||||
$group->addGroups(\@groups);
|
||||
|
|
@ -314,7 +314,7 @@ A WebGUI::Session object
|
|||
|
||||
sub www_addUsersToGroupSave {
|
||||
my $session = shift;
|
||||
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")));
|
||||
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")) && $session->form->validToken);
|
||||
my @users = $session->form->selectList('users');
|
||||
my $group = WebGUI::Group->new($session,$session->form->process("gid"));
|
||||
$group->addUsers(\@users);
|
||||
|
|
@ -419,10 +419,9 @@ sub www_deleteGroupGrouping {
|
|||
|
||||
=head2 www_deleteGrouping ( )
|
||||
|
||||
Deletes a set of users from a set of groups.
|
||||
The user and group lists are expected to
|
||||
be found in form fields names uid and gid, respectively. Visitors are not allowed to
|
||||
perform this operation.
|
||||
Deletes a set of users from a set of groups. The user and group lists are expected
|
||||
to be found in form fields names uid and gid, respectively. Visitors are not
|
||||
allowed to perform this operation.
|
||||
|
||||
=head3 $session
|
||||
|
||||
|
|
@ -432,7 +431,7 @@ A WebGUI::Session object
|
|||
|
||||
sub www_deleteGrouping {
|
||||
my $session = shift;
|
||||
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")));
|
||||
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")) && $session->form->validToken);
|
||||
if (($session->user->userId eq $session->form->process("uid") || $session->form->process("uid") eq '3') && $session->form->process("gid") eq '3') {
|
||||
return $session->privilege->vitalComponent();
|
||||
}
|
||||
|
|
@ -476,6 +475,7 @@ sub www_editGroup {
|
|||
-name => "op",
|
||||
-value => "editGroupSave",
|
||||
);
|
||||
$f->csrfToken();
|
||||
$f->hidden(
|
||||
-name => "gid",
|
||||
-value => $session->form->process("gid")
|
||||
|
|
@ -657,7 +657,7 @@ sub www_editGroupSave {
|
|||
my $session = shift;
|
||||
my $gid = $session->form->process("gid");
|
||||
return $session->privilege->adminOnly
|
||||
unless canEditGroup($session, $gid);
|
||||
unless canEditGroup($session, $gid) && $session->form->validToken;
|
||||
my $g = WebGUI::Group->new($session, $gid);
|
||||
# We don't want them to use an existing name. If needed, we'll add a number to the name to keep it unique.
|
||||
my $groupName = $session->form->process("groupName");
|
||||
|
|
@ -718,6 +718,7 @@ sub www_editGrouping {
|
|||
my $i18n = WebGUI::International->new($session);
|
||||
my $f = WebGUI::HTMLForm->new($session);
|
||||
$f->submit;
|
||||
$f->csrfToken();
|
||||
$f->hidden(
|
||||
-name => "op",
|
||||
-value => "editGroupingSave"
|
||||
|
|
@ -774,7 +775,7 @@ A WebGUI::Session object
|
|||
|
||||
sub www_editGroupingSave {
|
||||
my $session = shift;
|
||||
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")));
|
||||
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")) && $session->form->validToken);
|
||||
my $group = WebGUI::Group->new($session,$session->form->process("gid"));
|
||||
$group->userGroupExpireDate($session->form->process("uid"),$session->datetime->setToEpoch($session->form->process("expireDate")));
|
||||
$group->userIsAdmin($session->form->process("uid"),$session->form->process("groupAdmin"));
|
||||
|
|
@ -805,6 +806,7 @@ sub www_emailGroup {
|
|||
-name => "op",
|
||||
-value => "emailGroupSend"
|
||||
);
|
||||
$f->csrfToken();
|
||||
$f->hidden(
|
||||
-name => "gid",
|
||||
-value => $session->form->process("gid")
|
||||
|
|
@ -853,7 +855,7 @@ A WebGUI::Session object
|
|||
|
||||
sub www_emailGroupSend {
|
||||
my $session = shift;
|
||||
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")));
|
||||
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")) && $session->form->validToken);
|
||||
my $mail = WebGUI::Mail::Send->create($session, {toGroup=>$session->form->process("gid"),subject=>$session->form->process("subject"),from=>$session->form->process("from")});
|
||||
$mail->addHtml($session->form->process("message","HTMLArea"));
|
||||
$mail->addFooter;
|
||||
|
|
@ -958,6 +960,7 @@ sub www_manageGroupsInGroup {
|
|||
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")));
|
||||
|
||||
my $f = WebGUI::HTMLForm->new($session);
|
||||
$f->csrfToken();
|
||||
$f->submit;
|
||||
$f->hidden(
|
||||
-name => "op",
|
||||
|
|
@ -1014,6 +1017,7 @@ sub www_manageUsersInGroup {
|
|||
return $session->privilege->adminOnly() unless (canEditGroup($session,$session->form->process("gid")));
|
||||
my $i18n = WebGUI::International->new($session);
|
||||
my $output = WebGUI::Form::formHeader($session,)
|
||||
.WebGUI::Form::csrfToken($session,{})
|
||||
.WebGUI::Form::hidden($session,{
|
||||
name=>"gid",
|
||||
value=>$session->form->process("gid")
|
||||
|
|
@ -1036,7 +1040,6 @@ sub www_manageUsersInGroup {
|
|||
name=>"uid",
|
||||
value=>$row->{userId}
|
||||
})
|
||||
.$session->icon->delete('op=deleteGrouping;uid='.$row->{userId}.';gid='.$session->form->process("gid"))
|
||||
.$session->icon->edit('op=editGrouping;uid='.$row->{userId}.';gid='.$session->form->process("gid"))
|
||||
.'</td>';
|
||||
$output .= '<td class="tableData"><a href="'.$session->url->page('op=editUser;uid='.$row->{userId}).'">'.$row->{username}.'</a></td>';
|
||||
|
|
@ -1050,6 +1053,7 @@ sub www_manageUsersInGroup {
|
|||
return _submenu($session,$output) unless ($session->form->process("doit") || $userCount < 250 || $session->form->process("pn") > 1);
|
||||
my $f = WebGUI::HTMLForm->new($session);
|
||||
$f->submit;
|
||||
$f->csrfToken();
|
||||
$f->hidden(
|
||||
-name => "gid",
|
||||
-value => $session->form->process("gid")
|
||||
|
|
|
|||
|
|
@ -607,6 +607,7 @@ sub www_editSettings {
|
|||
name => "op",
|
||||
value => "saveSettings"
|
||||
});
|
||||
$tabform->csrfToken();
|
||||
|
||||
my $definitions = definition($session, $i18n);
|
||||
foreach my $definition (@{$definitions}) {
|
||||
|
|
@ -671,7 +672,7 @@ is in group Admin (3). Returns the user to the Edit Settings screen, www_editSe
|
|||
|
||||
sub www_saveSettings {
|
||||
my $session = shift;
|
||||
return $session->privilege->adminOnly() unless ($session->user->isAdmin);
|
||||
return $session->privilege->adminOnly() unless ($session->user->isAdmin && $session->form->validToken);
|
||||
my $i18n = WebGUI::International->new($session, "WebGUI");
|
||||
my $setting = $session->setting;
|
||||
my $form = $session->form;
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ sub www_approveVersionTag {
|
|||
my $tag = WebGUI::VersionTag->new( $session, $session->form->param("tagId") );
|
||||
|
||||
return $session->privilege->insufficient
|
||||
unless canApproveVersionTag( $session, $tag );
|
||||
unless canApproveVersionTag( $session, $tag ) && $session->form->validToken;
|
||||
|
||||
my $instance = $tag->getWorkflowInstance;
|
||||
my $activity = $instance->getNextActivity;
|
||||
|
|
@ -217,6 +217,7 @@ sub www_editVersionTag {
|
|||
-value=>"editVersionTagSave"
|
||||
);
|
||||
my $value = $tag->getId if defined $tag;
|
||||
$f->csrfToken();
|
||||
$f->hidden(
|
||||
-name=>"tagId",
|
||||
-value=>$value,
|
||||
|
|
@ -271,7 +272,7 @@ A reference to the current session.
|
|||
|
||||
sub www_editVersionTagSave {
|
||||
my $session = shift;
|
||||
return $session->session->privilege->insufficient() unless canView($session);
|
||||
return $session->session->privilege->insufficient() unless canView($session) && $session->form->validToken;
|
||||
if ($session->form->param("tagId") eq "new") {
|
||||
my $tag = WebGUI::VersionTag->create($session, {
|
||||
name=>$session->form->process("name","text", "Untitled"),
|
||||
|
|
@ -323,6 +324,7 @@ sub www_commitVersionTag {
|
|||
# Commit comments form
|
||||
my $f = WebGUI::HTMLForm->new($session);
|
||||
$f->submit;
|
||||
$f->csrfToken();
|
||||
$f->readOnly(
|
||||
label => $i18n->get("version tag name"),
|
||||
hoverHelp => $i18n->get("version tag name description commit"),
|
||||
|
|
@ -419,7 +421,7 @@ sub www_commitVersionTagConfirm {
|
|||
my $tagId = $session->form->param("tagId");
|
||||
if ($tagId) {
|
||||
my $tag = WebGUI::VersionTag->new($session, $tagId);
|
||||
if (defined $tag && $session->user->isInGroup($tag->get("groupToUse"))) {
|
||||
if (defined $tag && $session->user->isInGroup($tag->get("groupToUse")) && $session->form->validToken) {
|
||||
my $i18n = WebGUI::International->new($session, "VersionTag");
|
||||
|
||||
my $startTime = WebGUI::DateTime->new($session,$session->form->process("startTime","dateTime"))->toDatabase;
|
||||
|
|
@ -653,8 +655,9 @@ sub www_manageRevisionsInTag {
|
|||
$ac->addSubmenuItem($session->url->page('op=manageVersions'), $i18n->get("manage versions"));
|
||||
|
||||
# Process any actions
|
||||
my $action = lc $session->form->get('action');
|
||||
if ( $action eq "purge" ) {
|
||||
my $action = lc $session->form->get('action');
|
||||
my $validToken = $session->form->validToken;
|
||||
if ( $action eq "purge" && $validToken) {
|
||||
# Purge these revisions
|
||||
my @assetInfo = $session->form->get('assetInfo');
|
||||
for my $assetInfo ( @assetInfo ) {
|
||||
|
|
@ -669,7 +672,7 @@ sub www_manageRevisionsInTag {
|
|||
return www_manageVersions( $session );
|
||||
}
|
||||
}
|
||||
elsif ( $action eq "move to:" ) {
|
||||
elsif ( $action eq "move to:" && $validToken) {
|
||||
# Get the new version tag
|
||||
my $moveToTagId = $session->form->get('moveToTagId');
|
||||
my $moveToTag;
|
||||
|
|
@ -697,7 +700,7 @@ sub www_manageRevisionsInTag {
|
|||
return www_manageVersions( $session );
|
||||
}
|
||||
}
|
||||
elsif ( $action eq "update version tag" ) {
|
||||
elsif ( $action eq "update version tag" && $validToken) {
|
||||
my $startTime = WebGUI::DateTime->new($session,$session->form->process("startTime","dateTime"))->toDatabase;
|
||||
my $endTime = WebGUI::DateTime->new($session,$session->form->process("endTime","dateTime"))->toDatabase;
|
||||
|
||||
|
|
@ -716,6 +719,7 @@ sub www_manageRevisionsInTag {
|
|||
if (defined $instance) {
|
||||
my $form = WebGUI::HTMLForm->new($session);
|
||||
$form->submit;
|
||||
$form->csrfToken;
|
||||
$form->hidden(
|
||||
name=>"tagId",
|
||||
value=>$tagId
|
||||
|
|
@ -769,6 +773,7 @@ sub www_manageRevisionsInTag {
|
|||
.= WebGUI::Form::formHeader( $session, {} )
|
||||
. WebGUI::Form::hidden( $session, { name => 'op', value=> 'manageRevisionsInTag' } )
|
||||
. WebGUI::Form::hidden( $session, { name => 'tagId', value => $tag->getId } )
|
||||
. WebGUI::Form::csrfToken( $session )
|
||||
. '<table width="100%" class="content">'
|
||||
. '<tr>'
|
||||
. '<td colspan="5">'
|
||||
|
|
|
|||
|
|
@ -174,5 +174,24 @@ sub process {
|
|||
});
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 validToken ( )
|
||||
|
||||
Checks that the current form has a method=POST, and that it has a CSRF token matching
|
||||
the one in this user's current session.
|
||||
|
||||
=cut
|
||||
|
||||
sub validToken {
|
||||
my ($self) = @_;
|
||||
my $session = $self->session;
|
||||
$session->log->warn('method: '. $session->request->method);
|
||||
$session->log->warn('token: '. $session->scratch->get('webguiCsrfToken'));
|
||||
return 0 unless $session->request->method eq 'POST';
|
||||
return 0 unless $self->param('webguiCsrfToken') eq $session->scratch->get('webguiCsrfToken');
|
||||
return 1;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ sub new {
|
|||
$self->{_var}{expires} = $session->datetime->time() + $session->setting->get("sessionTimeout");
|
||||
$self->session->{_sessionId} = $self->{_var}{sessionId};
|
||||
$session->db->setRow("userSession","sessionId",$self->{_var});
|
||||
return $self;
|
||||
}
|
||||
else { ##Start a new default session with the requested, non-existant id.
|
||||
$self->start(1,$sessionId);
|
||||
|
|
@ -222,7 +223,7 @@ sub session {
|
|||
=head2 start ( [ userId, sessionId ] )
|
||||
|
||||
Start a new user session. Returns the user session id. The session variable's sessionId
|
||||
is set to the var object's session id.
|
||||
is set to the var object's session id. Also sets the user's CSRF token.
|
||||
|
||||
=head3 userId
|
||||
|
||||
|
|
@ -251,6 +252,7 @@ sub start {
|
|||
$self->{_var}{sessionId} = $sessionId;
|
||||
$self->session->db->setRow("userSession","sessionId",$self->{_var},$sessionId);
|
||||
$self->session->{_sessionId} = $sessionId;
|
||||
$self->session->scratch->set('webguiCsrfToken', $self->session->id->generate);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -102,6 +102,20 @@ sub addTab {
|
|||
return $self->{_tab}{$name}{form};
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 csrfToken ( )
|
||||
|
||||
Adds the WebGUI CSRF token to the form. Really a wrapper for WebGUI::Form::CsrfToken.
|
||||
|
||||
=cut
|
||||
|
||||
sub csrfToken {
|
||||
my $self = shift;
|
||||
$self->{_hidden} .= WebGUI::Form::CsrfToken($self->session);
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 formHeader ( hashRef )
|
||||
|
|
|
|||
|
|
@ -4475,6 +4475,12 @@ Users may override this setting in their profile.
|
|||
lastUpdated => 0,
|
||||
},
|
||||
|
||||
'csrfToken' => {
|
||||
message => 'CSRF Token',
|
||||
lastUpdated => 0,
|
||||
context => 'CSRF = Cross Site Request Forgery, token is a piece of identification',
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
58
t/Form/CsrfToken.t
Normal file
58
t/Form/CsrfToken.t
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#-------------------------------------------------------------------
|
||||
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
#-------------------------------------------------------------------
|
||||
# Please read the legal notices (docs/legal.txt) and the license
|
||||
# (docs/license.txt) that came with this distribution before using
|
||||
# this software.
|
||||
#-------------------------------------------------------------------
|
||||
# http://www.plainblack.com info@plainblack.com
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
use FindBin;
|
||||
use strict;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
|
||||
use WebGUI::Test;
|
||||
use WebGUI::Form;
|
||||
use WebGUI::Form::CsrfToken;
|
||||
use WebGUI::Session;
|
||||
use HTML::Form;
|
||||
use WebGUI::Form_Checking;
|
||||
|
||||
#The goal of this test is to verify that Zipcode form elements work
|
||||
|
||||
use Test::More; # increment this value for each test you create
|
||||
|
||||
my $session = WebGUI::Test->session;
|
||||
|
||||
# put your tests here
|
||||
|
||||
my $formClass = 'WebGUI::Form::CsrfToken';
|
||||
|
||||
my $numTests = 5;
|
||||
|
||||
plan tests => $numTests;
|
||||
|
||||
my ($header, $footer) = (WebGUI::Form::formHeader($session), WebGUI::Form::formFooter($session));
|
||||
|
||||
my $html = join "\n",
|
||||
$header,
|
||||
$formClass->new($session, {})->toHtml,
|
||||
$footer;
|
||||
|
||||
my @forms = HTML::Form->parse($html, 'http://www.webgui.org');
|
||||
|
||||
##Test Form Generation
|
||||
|
||||
is(scalar @forms, 1, '1 form was parsed');
|
||||
|
||||
my @inputs = $forms[0]->inputs;
|
||||
is(scalar @inputs, 1, 'The form has 1 input');
|
||||
|
||||
#Basic tests
|
||||
|
||||
my $input = $inputs[0];
|
||||
is($input->name, 'webguiCsrfToken', 'Checking input name');
|
||||
is($input->type, 'hidden', 'Checking input type');
|
||||
is($input->value, $session->scratch->get('webguiCsrfToken'), 'Checking token value');
|
||||
|
||||
37
t/Session/Form.t
Normal file
37
t/Session/Form.t
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#-------------------------------------------------------------------
|
||||
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
#-------------------------------------------------------------------
|
||||
# Please read the legal notices (docs/legal.txt) and the license
|
||||
# (docs/license.txt) that came with this distribution before using
|
||||
# this software.
|
||||
#-------------------------------------------------------------------
|
||||
# http://www.plainblack.com info@plainblack.com
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
use FindBin;
|
||||
use strict;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
|
||||
use WebGUI::Test;
|
||||
use WebGUI::Session;
|
||||
|
||||
use Test::More;
|
||||
|
||||
plan tests => 4;
|
||||
|
||||
my $session = WebGUI::Test->session;
|
||||
my $token = $session->scratch->get('webguiCsrfToken');
|
||||
|
||||
$session->request->method('POST');
|
||||
$session->request->setup_param({ webguiCsrfToken => $token, });
|
||||
ok($session->form->validToken, 'validToken: right method and form value');
|
||||
|
||||
$session->request->method('GET');
|
||||
ok(! $session->form->validToken, '... wrong method, right form value');
|
||||
|
||||
$session->request->method('POST');
|
||||
$session->request->setup_param({ webguiCsrfToken => 'bad token', });
|
||||
ok(! $session->form->validToken, 'validToken: right method and wrong form value');
|
||||
|
||||
$session->request->method('GET');
|
||||
ok(! $session->form->validToken, 'validToken: wrong method and form value');
|
||||
|
|
@ -16,7 +16,7 @@ use WebGUI::Test;
|
|||
use WebGUI::Session;
|
||||
use WebGUI::Session::Var;
|
||||
|
||||
use Test::More tests => 40; # increment this value for each test you create
|
||||
use Test::More tests => 44; # increment this value for each test you create
|
||||
use Test::Deep;
|
||||
|
||||
my $session = WebGUI::Test->session;
|
||||
|
|
@ -29,9 +29,13 @@ is($session->var->isAdminOn, 1, "switchAdminOn()");
|
|||
$session->var->switchAdminOff;
|
||||
is($session->var->isAdminOn, 0, "switchAdminOff()");
|
||||
|
||||
my $token = $session->scratch->get('webguiCsrfToken');
|
||||
ok( $token, 'CSRF token set');
|
||||
ok( $session->id->valid($token), '...is a valid GUID');
|
||||
|
||||
my $id = $session->var->getId;
|
||||
my ($count) = $session->db->quickArray("select count(*) from userSession where sessionId=?",[$id]);
|
||||
is($count, 1,"created an user session entry in the database");
|
||||
is($count, 1, "created an user session entry in the database");
|
||||
|
||||
my %newEnvHash = ( REMOTE_ADDR => '192.168.0.34');
|
||||
my $origEnv = $session->env->{_env};
|
||||
|
|
@ -41,6 +45,8 @@ my $var = WebGUI::Session::Var->new($session);
|
|||
my $varTime = time();
|
||||
my $varExpires = $varTime + $session->setting->get('sessionTimeout');
|
||||
isa_ok($var, 'WebGUI::Session::Var', 'new returns Var object');
|
||||
isnt($session->scratch->get('webguiCsrfToken'), $token, '... calling new without sessionId creates a new token');
|
||||
$token = $session->scratch->get('webguiCsrfToken');
|
||||
|
||||
cmp_ok(abs($var->get('lastPageView') - $varTime), '<=', 1, 'lastPageView set correctly');
|
||||
cmp_ok(abs($var->get('expires') - $varExpires), '<=', 1, 'expires set correctly');
|
||||
|
|
@ -66,6 +72,7 @@ $newEnvHash{REMOTE_ADDR} = '10.0.5.5';
|
|||
$varTime = time();
|
||||
my $var2 = WebGUI::Session::Var->new($session, $session->getId);
|
||||
$varExpires = $varTime + $session->setting->get('sessionTimeout');
|
||||
is($var2->session->scratch->get('webguiCsrfToken'), $token, 'opening a new user session did not change the CSRF token');
|
||||
|
||||
cmp_deeply(
|
||||
$var2,
|
||||
|
|
|
|||
|
|
@ -378,6 +378,23 @@ sub get_output {
|
|||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
=head2 method ( [ $method ] )
|
||||
|
||||
Getter/setter for the HTTP request method.
|
||||
|
||||
=cut
|
||||
|
||||
sub method {
|
||||
my ($self, $newMethod) = @_;
|
||||
my $method = $self->{method};
|
||||
if (defined $newMethod) {
|
||||
$self->{method} = $newMethod;
|
||||
}
|
||||
return $method;
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
=head2 print ( @values )
|
||||
|
||||
Fake print method for the PseudoRequest object. It caches everything printed
|
||||
|
|
|
|||
|
|
@ -16,16 +16,18 @@ function AssetManager() {
|
|||
this.dosort=true;
|
||||
this.tablecontainsforms=false;
|
||||
// Methods
|
||||
this.AddLine = AssetManager_AddLine;
|
||||
this.AddColumn = AssetManager_AddColumn;
|
||||
this.AddButton = AssetManager_AddButton;
|
||||
this.Write = AssetManager_Write;
|
||||
this.SortRows = AssetManager_SortRows;
|
||||
this.AddLine = AssetManager_AddLine;
|
||||
this.AddColumn = AssetManager_AddColumn;
|
||||
this.AddButton = AssetManager_AddButton;
|
||||
this.Write = AssetManager_Write;
|
||||
this.SortRows = AssetManager_SortRows;
|
||||
this.AddLineSortData = AssetManager_AddLineSortData;
|
||||
this.AddFormHidden = AssetManager_AddFormHidden;
|
||||
// Structure
|
||||
this.Columns = new Array();
|
||||
this.Lines = new Array();
|
||||
this.Buttons = new Array();
|
||||
this.Columns = new Array();
|
||||
this.Lines = new Array();
|
||||
this.Buttons = new Array();
|
||||
this.FormHidden = new Array();
|
||||
|
||||
|
||||
//***************Properties used for dragging
|
||||
|
|
@ -62,6 +64,12 @@ function AssetManager() {
|
|||
|
||||
}
|
||||
|
||||
// Add a hidden input field to the form
|
||||
function AssetManager_AddFormHidden( hiddenObj ) {
|
||||
var index = this.FormHidden.length;
|
||||
this.FormHidden[index] = hiddenObj;
|
||||
}
|
||||
|
||||
// Add a line to the grid
|
||||
function AssetManager_AddLine() {
|
||||
var index = this.Lines.length;
|
||||
|
|
@ -71,7 +79,7 @@ function AssetManager_AddLine() {
|
|||
this.Lines[index][i].text = arguments[i];
|
||||
this.Lines[index][i].data = arguments[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a button to the form
|
||||
function AssetManager_AddButton(label,func,proceed) {
|
||||
|
|
@ -161,6 +169,10 @@ function AssetManager_Write() {
|
|||
for (var j=0; j<this.Buttons.length; j++) {
|
||||
document.write('<input type="button" onclick="this.form.func.value=\''+this.Buttons[j].func+'\';this.form.proceed.value=\''+this.Buttons[j].proceed+'\';this.form.submit();" value="'+this.Buttons[j].label+'" />');
|
||||
}
|
||||
for (var j=0; j < this.FormHidden.length; j++) {
|
||||
var myHidden = this.FormHidden[j];
|
||||
document.write('<input type="hidden" name="'+ myHidden.name+'" value="'+ myHidden.value+'" />');
|
||||
}
|
||||
document.write('</form>');
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue