diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index 1c9156900..73fc75713 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -36,6 +36,12 @@ addons/ConfigAssistant.pack/static/colorpicker/js/jquery.js tools/report-slow-request lib/MT/Util/LogProcessor +# MT::App::NotifyList pulled in MT 4.361 +# Fogbugz case: http://bugs.movabletype.org/?106276 +# Git commits: 75333270 99cacda7 +lib/MT/App/NotifyList.pm +mt-add-notify.cgi + ^schemas ^t/ .*/t/ diff --git a/lib/MT/App.pm b/lib/MT/App.pm index 8cc4df068..05ff4e4c5 100644 --- a/lib/MT/App.pm +++ b/lib/MT/App.pm @@ -2445,6 +2445,19 @@ sub validate_upload { my $app = shift; my $args = shift; + my $INVALID = sub { + my $desc = $app->translate( @_ ? @_ : 'Reason unspecified' ); + require MT::Log; + $app->log({ + level => MT::Log::SECURITY(), + class => 'asset', + category => 'invalid', + message + => $app->translate('Blocked invalid upload: [_1]', $desc), + }); + return $app->errtrans('Invalid upload file'); + }; + ### # Check filename for validity and allowed upload file extensions # The file need not exist for these checks. @@ -2454,11 +2467,13 @@ sub validate_upload { my ($file) = File::Basename::fileparse( $args->{filename} ); my $cfg = $app->config(); - return $app->errtrans( 'Invalid upload file' ) - if - ! defined $file - or - $file =~ m{ + defined $file + or return $INVALID->( 'Could not parse filepath [_1]', + $args->{filename} ); + + return $INVALID->( + 'Invalid characters in filename: [_1]', $args->{filename}) + if $file =~ m{ / | # Filename shouldn't have slash; indicates directory \.\. | # No upward traversal allowed \0 | # No NULL bytes allowed @@ -2466,16 +2481,25 @@ sub validate_upload { ^$ # Empty filename }x; - if ( my $deny_exts = $cfg->DeniedAssetFileExtensions ) { - # Return error IF file extension matches - MT::Util::match_file_extension( $file, $deny_exts ) - and return $app->errtrans( 'Invalid upload file' ); + my $deny_exts = $cfg->DeniedAssetFileExtensions || []; + if ( @$deny_exts ) { + my $match = MT::Util::match_file_extension( $file, $deny_exts ); + if ( defined $match and $match ne '' ) { + return $INVALID->( + 'Blacklisted file extension ([_1]) found for file [_2]', + $match, $file + ); + } } - if ( my $allow_exts = $cfg->AssetFileExtensions ) { - # Return error UNLESS file extension matches - MT::Util::match_file_extension( $file, $allow_exts ) - or return $app->errtrans( 'Invalid upload file' ); + my $allow_exts = $cfg->AssetFileExtensions || []; + if ( @$allow_exts ) { + my $match = MT::Util::match_file_extension( $file, $allow_exts ); + unless ( defined $match and $match ne '' ) { + return $INVALID->( + 'File does not have whitelisted extension: [_1]', $file + ); + } } } @@ -2485,10 +2509,17 @@ sub validate_upload { # files (in particular) that contain embedded HTML or JavaScript are # a known vector for an IE 6 and 7 content-sniffing vulnerability. ### - if ( my $data = $args->{data} ) { + if ( defined( my $data = $args->{data} )) { require MT::Image; - MT::Image->has_html_signature( data => $data ) - and return $app->errtrans( 'Invalid upload file' ); + my $has_html = MT::Image->has_html_signature( data => $data ); + + defined $has_html + or return $INVALID->( + 'Error reading image [_1]: [_2]', 'data', MT::Image->errstr ); + + $has_html + or return $INVALID->( + 'Image file contains suspicious filetype signature'); } 1; @@ -3658,15 +3689,16 @@ sub query { } sub blog { - my $app = shift; - $app->{_blog} = shift if @_; - return $app->{_blog} if $app->{_blog}; - return undef unless $app->query; - my $blog_id = $app->query->param('blog_id'); - if ($blog_id) { - $app->{_blog} = MT->model('blog')->load($blog_id); - } - return $app->{_blog}; + my $app = shift; + my $blog = shift || $app->{_blog}; + $blog ||= eval { + no warnings; + my $blog_id + = int( $app->query->param('blog_id') || 0 ); + return $blog_id ? $app->model('blog')->load( $blog_id ) + : undef; + }; + return $app->{_blog} = $blog; } ## Logging/tracing diff --git a/lib/MT/App/Comments.pm b/lib/MT/App/Comments.pm index 9987d7c99..e6149a5c5 100644 --- a/lib/MT/App/Comments.pm +++ b/lib/MT/App/Comments.pm @@ -349,22 +349,33 @@ sub do_signup { my $app = shift; my $q = $app->query; - return $app->error( $app->translate("Invalid request") ) - if $app->request_method() ne 'POST'; + return $app->errtrans("Invalid request") + if $app->request_method() ne 'POST'; my $param = {}; $param->{$_} = $q->param($_) foreach qw(blog_id entry_id static email url username nickname email return_url ); + return $app->errtrans("Invalid request") + unless int( $param->{blog_id} ); + + my $blog = $app->model('blog')->load( $param->{blog_id} || 0 ) + or return $app->error( + $app->translate( 'Can\'t load blog #[_1].', $param->{blog_id} ) ); + + my $cfg = $app->config; + if ( my $registration = $cfg->CommenterRegistration ) { + return $app->handle_error( + $app->translate('Registration is not allowed.') ) + unless $registration->{Allow} && $blog->allow_commenter_regist; + } + my $filter_result = $app->run_callbacks( 'api_save_filter.author', $app ); my $user; $user = $app->create_user_pending($param) if $filter_result; unless ($user) { - my $blog = $app->model('blog')->load( $param->{blog_id} ) - or return $app->error( - $app->translate( 'Can\'t load blog #[_1].', $param->{blog_id} ) ); if ( my $provider = MT->effective_captcha_provider( $blog->captcha_provider ) ) { diff --git a/lib/MT/AtomServer.pm b/lib/MT/AtomServer.pm index 8f9f7056e..a47c05fd7 100644 --- a/lib/MT/AtomServer.pm +++ b/lib/MT/AtomServer.pm @@ -315,7 +315,7 @@ use MT::Entry; use MT::Util qw( encode_xml format_ts ); use MT::Permission; use File::Spec; -use File::Basename; +use File::Basename qw( basename dirname ); use constant NS_APP => 'http://www.w3.org/2007/app'; use constant NS_DC => 'http://purl.org/dc/elements/1.1/'; @@ -926,158 +926,119 @@ sub delete_post { ''; } ## end sub delete_post -sub _upload_to_asset { - my $app = shift; - my $atom = $app->atom_body or return; - my $blog = $app->{blog}; - my $user = $app->{user}; - my %MIME2EXT = ( - 'text/plain' => '.txt', - 'image/jpeg' => '.jpg', - 'video/3gpp' => '.3gp', - 'application/x-mpeg' => '.mpg', - 'video/mp4' => '.mp4', - 'video/quicktime' => '.mov', - 'audio/mpeg' => '.mp3', - 'audio/x-wav' => '.wav', - 'audio/ogg' => '.ogg', - 'audio/ogg-vorbis' => '.ogg', - ); +sub validate_upload { + my $app = shift; - return $app->error( 403, "Access denied" ) - unless $app->{perms}->can_upload; - my $content = $atom->content; - my $type = $content->type - or return $app->error( 400, "content \@type is required" ); - my $fname = $atom->title - or return $app->error( 400, "title is required" ); - $fname = basename($fname); - return $app->error( 400, "Invalid or empty filename" ) - if $fname =~ m!/|\.\.|\0|\|!; - - # Copy the filename and extract the extension. - - my $ext = $fname; - $ext =~ m!.*\.(.*)$! - ; ## Extract the characters to the right of the last dot delimiter / period - $ext = $1; ## Those characters are the file extension - - ### - # - # Look at new Movable Type configuration parameter AssetFileExtensions to - # see if the file that is being uploaded has a filename extension that is - # explicitly permitted. - # - # This code is very similar to the AssetFileExtensions check in XMLRPCServer.pm. - # - ### - - if ( my $allow_exts = $app->config('AssetFileExtensions') ) { - - # Split the parameters of the AssetFileExtensions configuration directive into items in an array - my @allowed = map { - if ( $_ =~ m/^\./ ) {qr/$_/i} - else {qr/\.$_/i} - } split '\s?,\s?', $allow_exts; - - # Find the extension in the array - my @found = grep( /\b$ext\b/, @allowed ); - - # If there is no extension or the extension wasn't found in the array - if ( ( length($ext) == 0 ) || ( !@found ) ) { - return - $app->error( - 500, - $app->translate( - 'The file ([_1]) you uploaded is not allowed.', - $fname - ) - ); - } - } ## end if ( my $allow_exts = ...) - - my $local_relative = File::Spec->catfile( '%r', $fname ); - my $local = File::Spec->catfile( $blog->site_path, $fname ); - my $fmgr = $blog->file_mgr; - - ### - # - # Had to extract the declaration of $base and $path from the succeeding line - # because $ext is now declared in the code section above this comment. - # - ### - - my ( $base, $path ); - ( $base, $path, $ext ) = File::Basename::fileparse( $local, '\.[^\.]*' ); - $ext = $MIME2EXT{$type} unless $ext; - my $base_copy = $base; - my $ext_copy = $ext; - $ext_copy =~ s/\.//; - my $i = 1; - while ( $fmgr->exists( $path . $base . $ext ) ) { - $base = $base_copy . '_' . $i++; + if ( @_ ) { + $app->SUPER::validate_upload( @_ ) + or return $app->error( 500, $app->errstr ); } + else { # Check Atom payload - $local = $path . $base . $ext; - my $local_basename = $base . $ext; - my $data = $content->body; + return $app->error( 403, "Access denied" ) + unless $app->{perms}->can_upload; - ### - # - # Function to evaluate the first 1k of content in an image file to see if it contains HTML or JavaScript - # content in the body. Image files that contain embedded HTML or JavaScript are - # prohibited in order to prevent a known IE 6 and 7 content-sniffing vulnerability. - # - # This code based on the ImageValidate plugin written by Six Apart. - # - ### + my $atom = $app->atom_body + or return $app->error( 400, "atom body is required" ); - ## Make a copy of the body that only contains the first 1k bytes. - my $html_test_string = substr( $data, 0, 1024 ); + my $content = $atom->content; + my $data = $content->body; - ## Using an error message format that already exists in all localizations of Movable Type 4. - return - $app->error( - 500, - MT->translate( - "Saving [_1] failed: [_2]", - $local_basename, - "Invalid image file format." - ) - ) - if ( $html_test_string =~ m/^\s*<[!?]/ ) - || ( $html_test_string - =~ m/<(HTML|SCRIPT|TITLE|BODY|HEAD|PLAINTEXT|TABLE|IMG|PRE|A)/i ) - || ( $html_test_string =~ m/text\/html/i ) - || ( $html_test_string - =~ m/^\s*<(FRAMESET|IFRAME|LINK|BASE|STYLE|DIV|P|FONT|APPLET)/i ) - || ( $html_test_string - =~ m/^\s*<(APPLET|META|CENTER|FORM|ISINDEX|H[123456]|B|BR)/i ); + my $type = $content->type + or return $app->error( 400, "content \@type is required" ); + + my $fname = $atom->title + or return $app->error( 400, "title is required" ); + + # Call self to have $app->SUPER check the filename and data + $app->validate_upload({ filename => basename($fname), data => $data }) + or return $app->error( 500, $app->errstr ); + } + + 1; # Looks good +} + +sub _upload_to_asset { + my $app = shift; + + return unless $app->validate_upload(); + + my $blog = $app->{blog}; + my $user = $app->{user}; + my $fmgr = $blog->file_mgr; + my $atom = $app->atom_body or return; + my $fname = basename( $atom->title ); + my $content = $atom->content; + my $type = $content->type; + my $data = $content->body; + + my $local + = File::Spec->catfile( $blog->site_path, $fname ); + + my ( $base, $path, $ext ) + = File::Basename::fileparse( $local, '\.[^\.]*' ); + + # FIXME Unnecessarily hard-coded hash of a handful of MIME types + # If there's no extension, look it up against a hash of arbitrarily + # chosen, sadly non-comprehensive list of popular MIME types. + if ( ! defined $ext or $ext eq '' ) { + my %MIME2EXT = ( + 'text/plain' => '.txt', + 'image/jpeg' => '.jpg', + 'video/3gpp' => '.3gp', + 'application/x-mpeg' => '.mpg', + 'video/mp4' => '.mp4', + 'video/quicktime' => '.mov', + 'audio/mpeg' => '.mp3', + 'audio/x-wav' => '.wav', + 'audio/ogg' => '.ogg', + 'audio/ogg-vorbis' => '.ogg', + ); + ($ext) = $MIME2EXT{$type} + || MT::Util::mime_type_extension( $type ); + } + + my $i = 1; + my $base_copy = $base; + while ( $fmgr->exists( join('', $path, $base, $ext) ) ) { + $base = join( '_', $base_copy, $i++ ); + } + my $local_basename = join('', $base, $ext); + $local = join('', $path, $local_basename); + + # Re-check in case extension was added + $app->validate_upload({ filename => $local_basename }) + or return $app->error( 500, $app->errstr ); defined( my $bytes = $fmgr->put_data( $data, $local, 'upload' ) ) or return $app->error( 500, "Error writing uploaded file" ); - eval { require Image::Size; }; - return - $app->error( - 500, - MT->translate( - "Perl module Image::Size is required to determine width and height of uploaded images." - ) - ) if $@; - my ( $w, $h, $id ) = Image::Size::imgsize($local); - require MT::Asset; my $asset_pkg = MT::Asset->handler_for_file($local); - my $is_image = 0; - if ( defined($w) && defined($h) ) { - $is_image = 1 if $asset_pkg->isa('MT::Asset::Image'); + + my $is_image = 0; + if ( $asset_pkg eq 'MT::Asset::Image' ) { + eval { require Image::Size; }; + if ( $@ ) { + $fmgr->delete( $local ); + return $app->error( + 500, + MT->translate( + 'Perl module Image::Size is required to determine ' + .'width and height of uploaded images.' + ).' Upload aborted.' + ); + } + $is_image = 1; } - else { + my ( $w, $h, $id ) = Image::Size::imgsize($local); + + unless ( defined($w) && defined($h) ) { # rebless to file type $asset_pkg = 'MT::Asset'; } + my $asset; if ( !( @@ -1088,24 +1049,25 @@ sub _upload_to_asset { ) { $asset = $asset_pkg->new(); - $asset->file_path($local_relative); - $asset->file_name( $base . $ext ); - $asset->file_ext($ext_copy); + $asset->file_path( join('/', '%r', $local_basename ) ); + $asset->file_name( $local_basename ); $asset->blog_id( $blog->id ); $asset->created_by( $user->id ); + (my $ext_copy = $ext) =~ s/\.//; + $asset->file_ext($ext_copy); } else { $asset->modified_by( $user->id ); } my $original = $asset->clone; - my $url = '%r/' . $base . $ext; + my $url = join('/', '%r', $local_basename ); $asset->url($url); if ($is_image) { $asset->image_width($w); $asset->image_height($h); } $asset->mime_type($type); - $asset->save; + $asset->save; # FIXME No check? No delete on failure? MT->run_callbacks( 'api_upload_file.' . $asset->class, diff --git a/lib/MT/Auth/OpenID.pm b/lib/MT/Auth/OpenID.pm index 55788bf08..cab835c82 100644 --- a/lib/MT/Auth/OpenID.pm +++ b/lib/MT/Auth/OpenID.pm @@ -362,6 +362,7 @@ sub get_userpicasset { return _asset_from_url($url); } ## end sub get_userpicasset +# FIXME This method must be refactored. See note for MT::CMS::Asset::_upload_file for further details sub _asset_from_url { my ($image_url) = @_; my $ua = _get_ua() or return; @@ -369,7 +370,8 @@ sub _asset_from_url { return undef unless $resp->is_success; my $image = $resp->content; return undef unless $image; - my $mimetype = $resp->header('Content-Type'); + my $mimetype = $resp->header('Content-Type') + or return undef; my $def_ext = { 'image/jpeg' => '.jpg', 'image/png' => '.png', @@ -413,7 +415,16 @@ sub _asset_from_url { require MT::Asset; my $asset_pkg = MT::Asset->handler_for_file($local); - return undef if $asset_pkg ne 'MT::Asset::Image'; + if ( $asset_pkg ne 'MT::Asset::Image' ) { + unlink $local; + return undef; + } + + my $has_html = eval { MT::Image->has_html_signature( path => $local ) }; + if ( $has_html || $@ ) { + unlink $local; + return undef; + } my $asset; $asset = $asset_pkg->new(); @@ -432,7 +443,10 @@ sub _asset_from_url { $asset->image_height($h); $asset->mime_type($mimetype); - $asset->save or return undef; + if ( !$asset->save ) { + unlink $local; + return undef; + } MT->run_callbacks( 'api_upload_file.' . $asset->class, diff --git a/lib/MT/CMS/Asset.pm b/lib/MT/CMS/Asset.pm index 6459d43c7..34577aa2c 100644 --- a/lib/MT/CMS/Asset.pm +++ b/lib/MT/CMS/Asset.pm @@ -379,14 +379,20 @@ sub start_upload { } sub upload_file { - my $app = shift; - my $q = $app->query; - my ( $asset, $bytes ) - = _upload_file( - $app, - require_type => ( $q->param('require_type') || '' ), - @_, - ); + my $app = shift; + my $q = $app->query; + my $perms = $app->permissions; + + return $app->errtrans("Permission denied.") + unless $perms and $perms->can_upload; + + $app->validate_magic() or return; + + my ( $asset, $bytes ) = _upload_file( + $app, + require_type => ( $app->param('require_type') || '' ), + @_, + ); return if !defined $asset; return $asset if !defined $bytes; # whatever it is @@ -986,19 +992,17 @@ sub _set_start_upload_params { $param; } ## end sub _set_start_upload_params +# FIXME This 580-line method MUST be refactored!!!! +# It needs to be broken up and ALL code NOT specifically unique to +# MT::App::CMS must be moved to MT::App or elsewhere so that other apps or +# command-line tools can use all of its functions individually AND so +# that we run all uploads through a common set of security checks. sub _upload_file { my $app = shift; my (%upload_param) = @_; my $ext; require MT::Image; - if ( my $perms = $app->permissions ) { - return $app->error( $app->translate("Permission denied.") ) - unless $perms->can_upload; - } - - $app->validate_magic() or return; - my $q = $app->query; my ( $fh, $info ) = $app->upload_info('file'); my $mimetype; @@ -1140,14 +1144,6 @@ sub _upload_file { ## Local Archive Path setting. So we should be safe. ($local_file) = $local_file =~ /(.+)/s; - ### - # - # Look at new Movable Type configuration parameter AssetFileExtensions to - # see if the file that is being uploaded has a filename extension that is - # explicitly permitted. - # - ### - my ($local_base) = $local_file; $local_base =~ s!\\!/!g; ## Change backslashes to forward slashes $local_base =~ s!^.*/!!; ## Get rid of full directory paths @@ -1156,38 +1152,10 @@ sub _upload_file { ; ## Save the filename so we can use it in the error message later $ext = $local_base; $ext =~ m!.*\.(.*)$! - ; ## Extract the characters to the right of the last dot delimiter / period + ; ## Extract the characters to the right of the last dot delimiter / period $ext = $1; ## Those characters are the file extension - ### - # - # If AssetFileExtensions configuration directive is defined. - # - ### - - if ( my $allow_exts = $app->config('AssetFileExtensions') ) { - - # Split the parameters of the AssetFileExtensions configuration directive into items in an array - my @allowed = map { - if ( $_ =~ m/^\./ ) {qr/$_/i} - else {qr/\.$_/i} - } split '\s?,\s?', $allow_exts; - - # Find the extension in the array - my @found = grep( /\b$ext\b/, @allowed ); - - # If there is no extension or the extension wasn't found in the array - if ( ( length($ext) == 0 ) || ( !@found ) ) { - return - $app->error( - $app->translate( - 'The file ([_1]) you uploaded is not allowed.', - $filename - ) - ); - } - - } ## end if ( my $allow_exts = ...) + return unless $app->validate_upload({ filename => $filename }); my $real_fh; unless ($has_overwrite) { @@ -1416,18 +1384,19 @@ sub _upload_file { ; ## Save the filename so we can use it in the error message later $ext = $local_base; $ext =~ m!.*\.(.*)$! - ; ## Extract the characters to the right of the last dot delimiter / period + ; ## Extract the characters to the right of the last dot delimiter / period $ext = $1; ## Those characters are the file extension - my ( $w, $h, $id, $write_file ) - = MT::Image->check_upload( - Fh => $fh, - Fmgr => $fmgr, - Local => $local_file, - Max => $upload_param{max_size}, - MaxDim => $upload_param{max_image_dimension}, - ext => $ext, - LocalBase => $local_base + return unless $app->validate_upload({ filename => $filename }); + + my ( $w, $h, $id, $write_file ) = MT::Image->check_upload( + Fh => $fh, + Fmgr => $fmgr, + Local => $local_file, + Max => $upload_param{max_size}, + MaxDim => $upload_param{max_image_dimension}, + ext => $ext, + LocalBase => $local_base ); return $app->error( MT::Image->errstr ) unless $write_file; diff --git a/lib/MT/CMS/Comment.pm b/lib/MT/CMS/Comment.pm index befeb7ed0..bc8f3316b 100644 --- a/lib/MT/CMS/Comment.pm +++ b/lib/MT/CMS/Comment.pm @@ -735,8 +735,22 @@ sub save_commenter_perm { my $blog_id = $q->param('blog_id'); my $author = $app->user; + my %permissions; foreach my $id (@ids) { ( $id, $blog_id ) = @$id if ref $id eq 'ARRAY'; + my $perm_blog_id = $app->config->SingleCommunity ? 0 : $blog_id; + if ( $perm_blog_id ) { + my $perm + = $permissions{ $perm_blog_id } + ||= $author->permissions($perm_blog_id); + if ( !$perm->can_manage_feedback && !$perm->can_administer ) { + return $app->errtrans( "Permission denied." ); + } + } + else { + return $app->errtrans( "Permission denied." ) + unless $author->is_superuser; + } my $cmntr = MT::Author->load($id) or return $app->errtrans( "No such commenter [_1].", $id ); diff --git a/lib/MT/CMS/Entry.pm b/lib/MT/CMS/Entry.pm index f628646d0..1a2237bb3 100644 --- a/lib/MT/CMS/Entry.pm +++ b/lib/MT/CMS/Entry.pm @@ -1013,6 +1013,10 @@ sub preview { $entry->id(-1); # fake out things like MT::Taggable::__load_tags $entry->blog_id($blog_id); } + + return $app->error( $app->translate("Permission denied.") ) + unless $app->permissions->can_edit_entry( $entry, $app->user ); + my $cat; my $names = $entry->column_names; diff --git a/lib/MT/CMS/Template.pm b/lib/MT/CMS/Template.pm index 441586a09..68509310f 100644 --- a/lib/MT/CMS/Template.pm +++ b/lib/MT/CMS/Template.pm @@ -928,6 +928,13 @@ sub preview { return unless $app->validate_magic; + my $perms = $app->blog ? $app->permissions : $app->user->permissions; + return $app->return_to_dashboard( redirect => 1 ) + unless $perms || $app->user->is_superuser; + if ( $perms && !$perms->can_edit_templates ) { + return $app->return_to_dashboard( permission => 1 ); + } + # We can only do previews on blog templates. Have to publish # the preview file somewhere! return $app->errtrans("Invalid request.") unless $blog; diff --git a/lib/MT/CMS/User.pm b/lib/MT/CMS/User.pm index f2e234d27..cf2590367 100644 --- a/lib/MT/CMS/User.pm +++ b/lib/MT/CMS/User.pm @@ -917,6 +917,10 @@ sub set_object_status { sub upload_userpic { my $app = shift; + $app->validate_magic() or return; + return $app->errtrans("Invalid request.") + if $app->param('blog_id'); + require MT::CMS::Asset; my ( $asset, $bytes ) = MT::CMS::Asset::_upload_file( $app, @_, require_type => 'image', ); diff --git a/lib/MT/Permission.pm b/lib/MT/Permission.pm index e90bb02b0..08f8bd813 100644 --- a/lib/MT/Permission.pm +++ b/lib/MT/Permission.pm @@ -396,8 +396,8 @@ sub can_edit_entry { require MT::Entry; $entry = MT::Entry->load($entry) or return; } - return unless $entry->is_entry; - if ( 'page' eq $entry->class ) { + + if ( !$entry->is_entry ) { return $perms->can_manage_pages; } return $perms->can_edit_all_posts diff --git a/lib/MT/XMLRPCServer.pm b/lib/MT/XMLRPCServer.pm index cb15ac74d..6ff6d48f3 100644 --- a/lib/MT/XMLRPCServer.pm +++ b/lib/MT/XMLRPCServer.pm @@ -1411,45 +1411,15 @@ sub newMediaObject { die _fault( MT->translate( "Invalid filename '[_1]'", $fname ) ); } - ### - # - # Look at new Movable Type configuration parameter AssetFileExtensions to - # see if the file that is being uploaded has a filename extension that is - # explicitly permitted. - # - # This code is very similar to the AssetFileExtensions check in AtomServer.pm. - # - ### - + $mt->validate_upload({ filename => $fname }) + or die _fault( $mt->errstr ); + # Copy the filename and extract the extension. - my $ext = $fname; $ext =~ m!.*\.(.*)$! ; ## Extract the characters to the right of the last dot delimiter / period $ext = $1; ## Those characters are the file extension - - if ( my $allow_exts = $mt->config('AssetFileExtensions') ) { - - # Split the parameters of the AssetFileExtensions configuration directive into items in an array - my @allowed = map { - if ( $_ =~ m/^\./ ) {qr/$_/i} - else {qr/\.$_/i} - } split '\s?,\s?', $allow_exts; - - # Find the extension in the array - my @found = grep( /\b$ext\b/, @allowed ); - - # If there is no extension or the extension wasn't found in the array - if ( ( length($ext) == 0 ) || ( !@found ) ) { - die _fault( - MT->translate( - 'The file ([_1]) you uploaded is not allowed.', $fname - ) - ); - } - } ## end if ( my $allow_exts = ...) - my $local_file = File::Spec->catfile( $blog->site_path, $file->{name} ); my $fmgr = $blog->file_mgr; my ( $vol, $path, $name ) = File::Spec->splitpath($local_file); @@ -1474,46 +1444,18 @@ sub newMediaObject { my $local_basename = File::Basename::basename($local_file); $ext = ( File::Basename::fileparse( $local_file, qr/[A-Za-z0-9]+$/ ) )[2]; - ### - # - # Function to evaluate content of an image file to see if it contains HTML or JavaScript - # content in the first 1K bytes. Image files that contain embedded HTML or JavaScript are - # prohibited in order to prevent a known IE 6 and 7 content-sniffing vulnerability. - # - # This code based on the ImageValidate plugin written by Six Apart. - # - ### - if ( $ext =~ m/(jpe?g|png|gif|bmp|tiff?|ico)/i ) { - require FileHandle; - my $Fh = FileHandle->new( $local_file, "r" ); - - my $data = ''; - - ## Read first 1k of image file - binmode($Fh); - seek( $Fh, 0, 0 ); - read $Fh, $data, 1024; - seek( $Fh, 0, 0 ); - - ## Using an error message format that already exists in all localizations of Movable Type 4. - die _fault( - MT->translate( - "Saving [_1] failed: [_2]", - $local_basename, - "Invalid image file format." - ) - ) - if ( $data =~ m/^\s*<[!?]/ ) - || ( $data - =~ m/<(HTML|SCRIPT|TITLE|BODY|HEAD|PLAINTEXT|TABLE|IMG|PRE|A)/i ) - || ( $data =~ m/text\/html/i ) - || ( $data - =~ m/^\s*<(FRAMESET|IFRAME|LINK|BASE|STYLE|DIV|P|FONT|APPLET)/i ) - || ( $data - =~ m/^\s*<(APPLET|META|CENTER|FORM|ISINDEX|H[123456]|B|BR)/i ); - } ## end if ( $ext =~ m/(jpe?g|png|gif|bmp|tiff?|ico)/i) + if ( MT::Image->has_html_signature( path => $local_file ) ) { + die _fault( + MT->translate( + "Saving [_1] failed: [_2]", + $local_basename, + "Invalid image file format." + ) + ); + } + } eval { require Image::Size; }; die _fault( diff --git a/t/110-cms-permission.t b/t/110-cms-permission.t new file mode 100644 index 000000000..e0d2db34a --- /dev/null +++ b/t/110-cms-permission.t @@ -0,0 +1,1460 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +BEGIN { + $ENV{MT_CONFIG} = 'mysql-test.cfg'; +} + +use lib 't/lib', 'lib', 'extlib', 'addons/Commercial.pack/lib', + 'addons/Enterprise.pack/lib'; + +use MT; +use MT::Author; +use MT::Blog; +use MT::Test qw( :app :db :data ); + +# Make additional data +my $mt = MT->instance; +make_data(); + +# Now, Ready to start test +my $test_count = 132; +$test_count += 4 + if $mt->component('commercial'); +$test_count += 4 + if $mt->component('enterprise'); + +use Test::More; +plan tests => $test_count; + +my ( $app, $out ); +my $blog = MT::Blog->load(1); +my $user = MT::Author->load(999); # aikawa +my $other = MT::Author->load(998); # ichikawa + +# Create a new Asset +# __mode=save&_type=asset&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'asset', + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new asset" ); +ok( $out =~ m!Permission denied!i, "Create a new Asset: result" ); + +# Delete Asset +# __mode=delete&_type=asset&blog_id=1&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'asset', + blog_id => 1, + id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete asset" ); +ok( $out =~ m/Permission denied/i, "Delete asset: result" ); + +# Update an asset +# __mode=save&_type=asset&blog_id=1&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $other, + __request_method => 'POST', + __mode => 'save', + _type => 'asset', + blog_id => 1, + id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Update an asset" ); +ok( $out =~ m!Permission denied!i, "Update an asset: result" ); + +# Create a new Author +# __mode=save&_type=author&name=new_author&type=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'author', + name => 'new_author', + type => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new author" ); +ok( $out =~ m/Permission denied/i, + "Create a new Author: result" ); + +# Delete Author +# __mode=delete&_type=author&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'author', + id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete author" ); +ok( $out =~ m/Permission denied/i, "Delete author: result" ); + +# Create a new Association +# __mode=save&_type=association&type=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'association', + type => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new association" ); +ok( $out =~ m/Permission denied/i, + "Create a new Association: result" +); + +# Delete Association +# __mode=delete&_type=association&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'association', + id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete association" ); +ok( $out =~ m/Permission denied/i, "Delete association: result" ); + +# Create a new Blog +# __mode=save&_type=blog&name=BlogName +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'blog', + name => 'BlogName' + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new blog" ); +ok( $out =~ m/Permission denied/i, "Create a new Blog: result" ); + +# Delete Blog +# __mode=delete&_type=blog&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'blog', + id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete blog" ); +ok( $out =~ m/Permission denied/i, "Delete blog: result" ); + +# Create a new Category +# __mode=save&_type=category&label=CategoryName&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'category', + label => 'CategoryName', + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new category" ); +ok( $out =~ m/Permission denied/i, + "Create a new Category: result" ); + +# Delete Category +# __mode=delete&_type=category&id=1&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'category', + id => 1, + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete category" ); +ok( $out =~ m/Permission denied/i, "Delete category: result" ); + +# Create a new Folder +# __mode=save&_type=folder&label=FolderName&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'folder', + label => 'FolderName', + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new folder" ); +ok( $out =~ m/Permission denied/i, + "Create a new Folder: result" ); + +# Delete Folder +# __mode=delete&_type=folder&id=20&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'folder', + id => 20, + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete folder" ); +ok( $out =~ m/Permission denied/i, "Delete folder: result" ); + +# Update Folder +# __mode=save&_type=folder&label=FolderName&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $other, + __request_method => 'POST', + __mode => 'save', + _type => 'folder', + label => 'FolderName', + blog_id => 1, + id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Update a folder" ); +ok( $out =~ m/Permission denied/i, "Update a Folder: result" ); + +# Create a new Comment +# __mode=save&_type=comment&&blog_id=1&entry_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'comment', + blog_id => 1, + entry_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new comment" ); +ok( $out =~ m/Permission denied/i, + "Create a new Comment: result" ); + +# Delete Comment +# __mode=delete&_type=comment&id=1&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'comment', + id => 1, + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete comment" ); +ok( $out =~ m/Permission denied/i, "Delete comment: result" ); + +# Create a new Entry +# __mode=save&_type=entry&&blog_id=1&author_id=1&status=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'entry', + blog_id => 1, + author_id => 1, + status => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new entry" ); +ok( $out =~ m/Permission denied/i, "Create a new Entry: result" ); + +# Delete Entry +# __mode=delete&_type=entry&id=1&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'entry', + id => 1, + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete entry" ); +ok( $out =~ m/Permission denied/i, "Delete entry: result" ); + +# Update an Entry +# __mode=save&_type=entry&&blog_id=1&author_id=1&status=1&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $other, + __request_method => 'POST', + __mode => 'save', + _type => 'entry', + blog_id => 1, + author_id => 1, + status => 1, + id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Update an entry" ); +ok( $out =~ m/Permission denied/i, "Update anEntry: result" ); + +# Create a new Page +# __mode=save&_type=page&&blog_id=1&author_id=1&status=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'page', + blog_id => 1, + author_id => 1, + status => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new page" ); +ok( $out =~ m/Permission denied/i, "Create a new Page: result" ); + +# Delete Page +# __mode=delete&_type=page&id=20&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'page', + id => 20, + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete page" ); +ok( $out =~ m/Permission denied/i, "Delete page: result" ); + +# Update a Page +# __mode=save&_type=page&&blog_id=1&author_id=1&status=1&id=20 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $other, + __request_method => 'POST', + __mode => 'save', + _type => 'page', + blog_id => 1, + author_id => 1, + status => 1, + id => 20 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Update an page" ); +ok( $out =~ m/Permission denied/i, "Update an Page: result" ); + +# Create a new Banlist +# __mode=save&_type=banlist&&blog_id=1&ip=1.1.1.1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'banlist', + blog_id => 1, + ip => '1.1.1.1' + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new banlist" ); +ok( $out =~ m/Permission denied/i, + "Create a new Banlist: result" ); + +# Delete Banlist +# __mode=delete&_type=banlist&id=1&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'banlist', + id => 1, + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete banlist" ); +ok( $out =~ m/Permission denied/i, "Delete banlist: result" ); + +# Create a new Notification +# __mode=save&_type=notification&&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'notification', + blog_id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new notification" ); +ok( $out =~ m/Permission denied/i, + "Create a new Notification: result" +); + +# Delete Notification +# __mode=delete&_type=notification&id=1&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'notification', + id => 1, + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete notification" ); +ok( $out =~ m/Permission denied/i, + "Delete notification: result" ); + +# Create a new Role +# __mode=save&_type=role&&blog_id=1&name=NewRole +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'role', + name => "NewRole" + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new role" ); +ok( $out =~ m/Permission denied/i, "Create a new Role: result" ); + +# Delete Role +# __mode=delete&_type=role&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'role', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete role" ); +ok( $out =~ m/Permission denied/i, "Delete role: result" ); + +# Create a new Config +# __mode=save&_type=config +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'config' + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new config" ); +ok( $out =~ m/Permission denied/i, + "Create a new Config: result" ); + +# Delete Config +# __mode=delete&_type=config&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'config', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete config" ); +ok( $out =~ m/Permission denied/i, "Delete config: result" ); + +# Create a new Fileinfo +# __mode=save&_type=fileinfo&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'fileinfo', + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new fileinfo" ); +ok( $out =~ m/Permission denied/i, + "Create a new Fileinfo: result" ); + +# Delete Fileinfo +# __mode=delete&_type=fileinfo&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'fileinfo', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete fileinfo" ); +ok( $out =~ m/Permission denied/i, "Delete fileinfo: result" ); + +# Create a new Log +# __mode=save&_type=log&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'log', + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new log" ); +ok( $out =~ m/Permission denied/i, "Create a new Log: result" ); + +# Delete Log +# __mode=delete&_type=log&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'log', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete log" ); +ok( $out =~ m/Permission denied/i, "Delete log: result" ); + +# Create a new ObjectAsset +# __mode=save&_type=objectasset&asset_id=1&object_id=1&object_ds=entry +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'objectasset', + asset_id => 1, + object_id => 1, + object_ds => 'entry' + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new objectasset" ); +ok( $out =~ m/Permission denied/i, + "Create a new Objectasset: result" +); + +# Delete Objectasset +# __mode=delete&_type=objectasset&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'objectasset', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete objectasset" ); +ok( $out =~ m/Permission denied/i, "Delete objectasset: result" ); + +# Create a new Objectscore +# __mode=save&_type=objectscore&namespace=scope_name&object_ds=entry +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'objectscore', + namespace => 'scope_name', + object_ds => 'entry' + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new objectscore" ); +ok( $out =~ m/Permission denied/i, + "Create a new Objectscore: result" +); + +# Delete Objectscore +# __mode=delete&_type=objectscore&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'objectscore', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete objectscore" ); +ok( $out =~ m/Permission denied/i, "Delete objectscore: result" ); + +# Create a new Objecttag +# __mode=save&_type=objecttag&tag_id=1&object_datasource=entry&object_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'objecttag', + object_datasource => 'entry', + tag_id => 1, + object_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new objecttag" ); +ok( $out =~ m/Permission denied/i, + "Create a new Objecttag: result" +); + +# Delete Objecttag +# __mode=delete&_type=objecttag&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'objecttag', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete objecttag" ); +ok( $out =~ m/Permission denied/i, "Delete objecttag: result" ); + +# Create a new Permission +# __mode=save&_type=permission&blog_id=1&author_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'permission', + author_id => 1, + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new permission" ); +ok( $out =~ m/Permission denied/i, + "Create a new Permission: result" +); + +# Delete Permission +# __mode=delete&_type=permission&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'permission', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete permission" ); +ok( $out =~ m/Permission denied/i, "Delete permission: result" ); + +# Create a new Placement +# __mode=save&_type=placement&blog_id=1&category_id=1&entry_id=1&is_primary=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'placement', + blog_id => 1, + category_id => 1, + entry_id => 1, + is_primary => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new placement" ); +ok( $out =~ m/Permission denied/i, + "Create a new Placement: result" +); + +# Delete Placement +# __mode=delete&_type=placement&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'placement', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete placement" ); +ok( $out =~ m/Permission denied/i, "Delete placement: result" ); + +# Create a new Session +# __mode=save&_type=session&id=THIS_IS_A_FAKE_SESSION_2&start=currenttime +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'session', + id => 'THIS_IS_A_FAKE_SESSION_2', + time => time + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new session" ); +ok( $out =~ m/Permisison denied/i, + "Create a new Session: result" ); + +# Delete Session +# __mode=delete&_type=session&id=THIS_IS_A_FAKE_SESSION +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'session', + id => 'THIS_IS_A_FAKE_SESSION', + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete session" ); +ok( $out =~ m/Permission denied/i, "Delete session: result" ); + +# Create a new Tag +# __mode=save&_type=tag&name=NewTag +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'tag', + name => 'NewTag' + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new tag" ); +ok( $out =~ m/Permission denied/i, "Create a new Tag: result" ); + +# Delete Tag +# __mode=delete&_type=tag&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'tag', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete tag" ); +ok( $out =~ m/Permission denied/i, "Delete tag: result" ); + +# Create a new Ping +# __mode=save&_type=ping&blog_id=1&ip=1.1.1.1&tb_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'ping', + blog_id => 1, + ip => '1.1.1.1', + tb_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new ping" ); +ok( $out =~ m/Permission denied/i, "Create a new Ping: result" ); + +# Delete Ping +# __mode=delete&_type=ping&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'ping', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete ping" ); +ok( $out =~ m/Permission denied/i, "Delete ping: result" ); + +# Create a new Touch +# __mode=save&_type=touch +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'touch', + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new touch" ); +ok( $out =~ m/Permission denied/i, "Create a new Touch: result" ); + +# Delete Touch +# __mode=delete&_type=touch&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'touch', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete touch" ); +ok( $out =~ m/Permission denied/i, "Delete touch: result" ); + +# Create a new Trackback +# __mode=save&_type=trackback&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'trackback', + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new trackback" ); +ok( $out =~ m/Permission denied/i, + "Create a new Trackback: result" +); + +# Delete Trackback +# __mode=delete&_type=trackback&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'trackback', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete trackback" ); +ok( $out =~ m/Permission denied/i, "Delete trackback: result" ); + +# Create a new Template +# __mode=save&_type=template&blog_id=1&name=NewTemplate&type=custom +$user = MT::Author->load(3); # Bobd +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'template', + blog_id => 1, + name => 'NewTemplate', + type => 'custom' + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new template" ); +ok( $out =~ m/Permission denied/i, + "Create a new Template: result" ); + +# Delete Template +# __mode=delete&_type=template&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'template', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete template" ); +ok( $out =~ m/Permission denied/i, "Delete template: result" ); + +# Create a new Templatemap +# __mode=save&_type=templatemap&blog_id=1&archive_type=Author&template_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'templatemap', + blog_id => 1, + archive_type => 'Author', + template_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Create a new templatemap" ); +ok( $out =~ m/Permission denied/i, + "Create a new Templatemap: result" +); + +# Delete Templatemap +# __mode=delete&_type=templatemap&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'templatemap', + id => 1, + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete templatemap" ); +ok( $out =~ m/Permission denied/i, "Delete templatemap: result" ); + + +### Addons +if ( $mt->component('commercial') ) { + +# Create a new Field +# __mode=save&_type=field&blog_id=1&name=NewField&object_type=entry&type=SingleLineText&tag=newtag + $app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'field', + blog_id => 1, + name => 'NewField', + object_type => 'entry', + tag => 'newtag', + type => 'SingleLineText' + } + ); + $out = delete $app->{__test_output}; + ok( $out, "Create a new field" ); + ok( $out =~ m/Permission denied/i, + "Create a new Field: result" ); + + # Delete Field + # __mode=delete&_type=field&id=1&blog_id=1 + $app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'field', + id => 1, + blog_id => 1 + } + ); + $out = delete $app->{__test_output}; + ok( $out, "Delete field" ); + ok( $out =~ m/Permission denied/i, "Delete field: result" ); +} + +if ( $mt->component('enterprise') ) { + + # Create a new Group + # __mode=save&_type=group&blog_id=1&name=NewGroup + $app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'group', + name => 'NewGroup', + } + ); + $out = delete $app->{__test_output}; + ok( $out, "Create a new group" ); + ok( $out =~ m/Permission denied/i, + "Create a new Group: result" ); + + # Delete Group + # __mode=delete&_type=group&id=1 + $app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'group', + id => 1, + } + ); + $out = delete $app->{__test_output}; + ok( $out, "Delete group" ); + ok( $out =~ m/Permission denied/i, "Delete group: result" ); +} + + +### Different type +$user = MT::Author->load(994); #kagawa + +# Update a Category +# __mode=save&_type=category&label=CategoryName&blog_id=1&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'category', + label => 'CategoryName', + blog_id => 1, + id => 20 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Update a category (different)" ); +ok( $out =~ m/Permission denied/i, + " Update a category (different): result" +); + +# Delete Category +# __mode=delete&_type=category&id=20&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'category', + id => 20, + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete category (different)" ); +ok( $out =~ m/Permission denied/i, + "Delete category (different): result" +); + +$user = MT::Author->load(995); #ogawa + +# Update a Folder +# __mode=save&_type=category&label=CategoryName&blog_id=1&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'folder', + label => 'CategoryName', + blog_id => 1, + id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Update a folder (different)" ); +ok( $out =~ m/Permission denied/i, + " Update a folder (different): result" +); + +# Delete Folder +# __mode=delete&_type=folder&id=1&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'folder', + id => 1, + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete folfer (different)" ); +ok( $out =~ m/Permission denied/i, + "Delete folder (different): result" +); + +$user = MT::Author->load(995); #ogawa + +# Update a Page +# __mode=save&_type=page&&blog_id=1&author_id=1&status=1&id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'page', + blog_id => 1, + author_id => 1, + status => 1, + id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Update an page (different)" ); +ok( $out =~ m/Permission denied/i, + "Update an Page(different): result" +); + +# Delete Page +# __mode=delete&_type=page&id=1&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'page', + id => 1, + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete page (different)" ); +ok( $out =~ m/Permission denied/i, + "Delete page (different): result" +); + +$user = MT::Author->load(994); #kagawa + +# Update an Entry +# __mode=save&_type=entry&&blog_id=1&author_id=1&status=1&id=20 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'save', + _type => 'entry', + blog_id => 1, + author_id => 1, + status => 1, + id => 20 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Update an entry (different)" ); +ok( $out =~ m/Permission denied/i, + "Update an Entry(different): result" +); + +# Delete Entry +# __mode=delete&_type=entry&id=20&blog_id=1 +$app = _run_app( + 'MT::App::CMS', + { __test_user => $user, + __request_method => 'POST', + __mode => 'delete', + _type => 'entry', + id => 20, + blog_id => 1 + } +); +$out = delete $app->{__test_output}; +ok( $out, "Delete entry (different)" ); +ok( $out =~ m/Permission denied/i, + "Delete entry (different): result" +); + +sub make_data { + + ### Author + require MT::Author; + my $aikawa = MT::Author->new(); + $aikawa->set_values( + { name => 'aikawa', + nickname => 'Ichiro Aikawa', + email => 'aikawa@example.com', + url => 'http://aikawa.com/', + api_password => 'seecret', + auth_type => 'MT', + created_on => '19780131074500', + } + ); + $aikawa->set_password("pass"); + $aikawa->type( MT::Author::AUTHOR() ); + $aikawa->id(999); + $aikawa->save() + or die "Couldn't save author record 999: " . $aikawa->errstr; + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + + my $ichikawa = MT::Author->new(); + $ichikawa->set_values( + { name => 'ichikawa', + nickname => 'Jiro Ichikawa', + email => 'ichikawa@example.com', + url => 'http://ichikawa.com/', + api_password => 'seecret', + auth_type => 'MT', + created_on => '19780131074500', + } + ); + $ichikawa->set_password("pass"); + $ichikawa->type( MT::Author::AUTHOR() ); + $ichikawa->id(998); + $ichikawa->save() + or die "Couldn't save author record 998: " . $ichikawa->errstr; + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + + my $ukawa = MT::Author->new(); + $ukawa->set_values( + { name => 'ukawa', + nickname => 'Saburo Ukawa', + email => 'ukawa@example.com', + url => 'http://ukawa.com/', + api_password => 'seecret', + auth_type => 'MT', + created_on => '19780131074500', + } + ); + $ukawa->set_password("pass"); + $ukawa->type( MT::Author::AUTHOR() ); + $ukawa->id(997); + $ukawa->save() + or die "Couldn't save author record 997: " . $ukawa->errstr; + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + + my $ogawa = MT::Author->new(); + $ogawa->set_values( + { name => 'ogawa', + nickname => 'Goro Ogawa', + email => 'ogawa@example.com', + url => 'http://ogawa.com/', + api_password => 'seecret', + auth_type => 'MT', + created_on => '19780131074500', + } + ); + $ogawa->set_password("pass"); + $ogawa->type( MT::Author::AUTHOR() ); + $ogawa->id(995); + $ogawa->save() + or die "Couldn't save author record 995: " . $ogawa->errstr; + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + + my $kagawa = MT::Author->new(); + $kagawa->set_values( + { name => 'kagawa', + nickname => 'Ichiro Kagawa', + email => 'kagawa@example.com', + url => 'http://kagawa.com/', + api_password => 'seecret', + auth_type => 'MT', + created_on => '19780131074500', + } + ); + $kagawa->set_password("pass"); + $kagawa->type( MT::Author::AUTHOR() ); + $kagawa->id(994); + $kagawa->save() + or die "Couldn't save author record 994: " . $kagawa->errstr; + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + + ### Role + require MT::Role; + my $role = MT::Role->new(); + $role->set_values( + { name => 'Entry Editor', + perms => [ + 'create_post', 'edit_all_posts', + 'edit_tags', 'edit_categories', + 'publish_post', 'comment', + ], + } + ); + $role->id(20); + $role->save + or die "Couldn't save role record 20: " . $role->errstr; + + ### Association + my $designer_role = MT::Role->load( { name => 'Designer' } ); + my $author_role = MT::Role->load( { name => 'Author' } ); + my $blog_role = MT::Role->load( { name => 'Blog Administrator' } ); + my $page_role = MT::Role->load( { name => 'Webmaster' } ); + my $editor_role = MT::Role->load( { name => 'Entry Editor' } ); + + require MT::Association; + my $assoc = MT::Association->new(); + $assoc->author_id( $aikawa->id ); + $assoc->blog_id(1); + $assoc->role_id( $designer_role->id ); + $assoc->type(1); + $assoc->save(); + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + + $assoc = MT::Association->new(); + $assoc->author_id( $ichikawa->id ); + $assoc->blog_id(1); + $assoc->role_id( $author_role->id ); + $assoc->type(1); + $assoc->save(); + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + $assoc = MT::Association->new(); + $assoc->author_id( $ukawa->id ); + $assoc->blog_id(1); + $assoc->role_id( $blog_role->id ); + $assoc->type(1); + $assoc->save(); + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + + $assoc = MT::Association->new(); + $assoc->author_id( $ogawa->id ); + $assoc->blog_id(1); + $assoc->role_id( $page_role->id ); + $assoc->type(1); + $assoc->save(); + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + + $assoc = MT::Association->new(); + $assoc->author_id( $kagawa->id ); + $assoc->blog_id(1); + $assoc->role_id( $editor_role->id ); + $assoc->type(1); + $assoc->save(); + + ### IPBanList + require MT::IPBanList; + my $banlist = MT::IPBanList->new(); + $banlist->set_values( + { blog_id => 1, + ip => '1.2.3.4', + } + ); + $banlist->save() + or die "Couldn't save ipbanlist record: 1" . $banlist->errstr; + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + + ### Notification + require MT::Notification; + my $address = MT::Notification->new(); + $address->set_values( + { blog_id => 1, + name => 'Foo Bar', + email => 'foo@example.com', + url => 'http://foo.com', + } + ); + $address->save() + or die "Couldn't save notification record: 1" . $address->errstr; + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + + ## Session + require MT::Session; + my $sess = MT::Session->new(); + $sess->id('THIS_IS_A_FAKE_SESSION'); + $sess->kind('UD'); + $sess->start(time); + $sess->set( 'remember', 1 ); + $sess->save + or die "Couldn't save session record" . $sess->errstr; + + ### Log + MT->log( + { message => 'This is a log message.', + class => "system", + level => MT::Log::ERROR(), + category => "test", + } + ); + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + + ### Addons Data + if ( $mt->component('commercial') ) { + require CustomFields::Field; + my $field = CustomFields::Field->new(); + $field->set_values( + { blog_id => 1, + name => 'SingleLine', + obj_type => 'entry', + type => 'SingleLineText', + tag => 'EntryDataSingleLine', + basename => 'singleline', + } + ); + $field->save() + or die "Couldn't save custom field record: 1" . $field->errstr; + } + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + + if ( $mt->component('enterprise') ) { + require MT::Group; + my $group = MT::Group->new(); + $group->set_values( + { name => 'New Group', + status => 1, + display_name => 'Group', + } + ); + $group->save() + or die "Couldn't save group record: 1" . $group->errstr; + } + + MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); + +} +