X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Controller-DBIC-API.git;a=blobdiff_plain;f=lib%2FCatalyst%2FController%2FDBIC%2FAPI%2FStaticArguments.pm;h=0ec6c0ddedbcbe111335af541c1b0db0d5e9d4c8;hp=71e873066ff9a90b6821cd542b9af4af6028cc4d;hb=HEAD;hpb=406086f3da2f020cf98b01d994ffe2d1b8a478c4 diff --git a/lib/Catalyst/Controller/DBIC/API/StaticArguments.pm b/lib/Catalyst/Controller/DBIC/API/StaticArguments.pm index 71e8730..0ec6c0d 100644 --- a/lib/Catalyst/Controller/DBIC/API/StaticArguments.pm +++ b/lib/Catalyst/Controller/DBIC/API/StaticArguments.pm @@ -9,7 +9,8 @@ requires 'check_column_relation'; =attribute_public create_requires create_allows update_requires update_allows -These attributes control requirements and limits to columns when creating or updating objects. +These attributes control requirements and limits to columns when creating or +updating objects. Each provides a number of handles: @@ -22,142 +23,229 @@ Each provides a number of handles: =cut -foreach my $var (qw/create_requires create_allows update_requires update_allows/) +foreach my $var ( + qw( create_requires create_allows update_requires update_allows )) { - has $var => - ( - is => 'ro', - isa => ArrayRef[Str|HashRef], - traits => ['Array'], + has $var => ( + is => 'ro', + isa => ArrayRef [ Str | HashRef ], + traits => ['Array'], default => sub { [] }, - trigger => sub - { - my ($self, $new) = @_; - $self->check_column_relation($_, 1) for @$new; + trigger => sub { + my ( $self, $new ) = @_; + $self->check_column_relation( $_, 1 ) for @$new; }, - handles => - { - "get_${var}_column" => 'get', - "set_${var}_column" => 'set', + handles => { + "get_${var}_column" => 'get', + "set_${var}_column" => 'set', "delete_${var}_column" => 'delete', "insert_${var}_column" => 'insert', - "count_${var}_column" => 'count', - "all_${var}_columns" => 'elements', + "count_${var}_column" => 'count', + "all_${var}_columns" => 'elements', } ); - before "set_${var}_column" => sub { $_[0]->check_column_relation($_[2], 1) }; #" - before "insert_${var}_column" => sub { $_[0]->check_column_relation($_[2], 1) }; #" + before "set_${var}_column" => + sub { $_[0]->check_column_relation( $_[2], 1 ) }; + before "insert_${var}_column" => + sub { $_[0]->check_column_relation( $_[2], 1 ) }; } -=attribute_public count_arg is: ro, isa: Str, default: 'list_count' +=attribute_public prefetch_allows -count_arg controls how to reference 'count' in the the request_data +prefetch_allows limits what relations may be prefetched when executing searches +with joins. This is necessary to avoid denial of service attacks in form of +queries which would return a large number of data and unwanted disclosure of +data. + +Like the synopsis in DBIC::API shows, you can declare a "template" of what is +allowed (by using an '*'). Each element passed in, will be converted into a +Data::DPath and added to the validator. + + prefetch_allows => [ 'cds', { cds => tracks }, { cds => producers } ] # to be explicit + prefetch_allows => [ 'cds', { cds => '*' } ] # wildcard means the same thing + +=cut + +has 'prefetch_allows' => ( + is => 'ro', + writer => '_set_prefetch_allows', + isa => ArrayRef [ ArrayRef | Str | HashRef ], + default => sub { [] }, + predicate => 'has_prefetch_allows', + traits => ['Array'], + handles => { all_prefetch_allows => 'elements', }, +); + +has 'prefetch_validator' => ( + is => 'ro', + isa => 'Catalyst::Controller::DBIC::API::Validator', + lazy_build => 1, +); + +sub _build_prefetch_validator { + my $self = shift; + + sub _check_rel { + my ( $self, $rel, $static, $validator ) = @_; + if ( ArrayRef->check($rel) ) { + foreach my $rel_sub (@$rel) { + _check_rel( $self, $rel_sub, $static, $validator ); + } + } + elsif ( HashRef->check($rel) ) { + while ( my ( $k, $v ) = each %$rel ) { + $self->check_has_relation( $k, $v, undef, $static ); + } + $validator->load($rel); + } + else { + $self->check_has_relation( $rel, undef, undef, $static ); + $validator->load($rel); + } + } + + my $validator = Catalyst::Controller::DBIC::API::Validator->new; + + foreach my $rel ( $self->all_prefetch_allows ) { + _check_rel( $self, $rel, 1, $validator ); + } + + return $validator; +} + +=attribute_public count_arg + +Controls how to reference 'count' in the the request_data, defaults to +'list_count'. =cut has 'count_arg' => ( is => 'ro', isa => Str, default => 'list_count' ); -=attribute_public page_arg is: ro, isa: Str, default: 'list_page' +=attribute_public page_arg -page_arg controls how to reference 'page' in the the request_data +Controls how to reference 'page' in the the request_data, defaults to +'list_page'. =cut has 'page_arg' => ( is => 'ro', isa => Str, default => 'list_page' ); -=attribute_public offset_arg is: ro, isa: Str, default: 'offset' +=attribute_public offset_arg -offset_arg controls how to reference 'offset' in the the request_data +Controls how to reference 'offset' in the the request_data, defaults to +'list_offset'. =cut has 'offset_arg' => ( is => 'ro', isa => Str, default => 'list_offset' ); -=attribute_public select_arg is: ro, isa: Str, default: 'list_returns' +=attribute_public select_arg -select_arg controls how to reference 'select' in the the request_data +Controls how to reference 'select' in the the request_data, defaults to +'list_returns'. =cut has 'select_arg' => ( is => 'ro', isa => Str, default => 'list_returns' ); -=attribute_public as_arg is: ro, isa: Str, default: 'as' +=attribute_public as_arg -as_arg controls how to reference 'as' in the the request_data +Controls how to reference 'as' in the the request_data, defaults to 'as'. =cut has 'as_arg' => ( is => 'ro', isa => Str, default => 'as' ); -=attribute_public search_arg is: ro, isa: Str, default: 'search' +=attribute_public search_arg -search_arg controls how to reference 'search' in the the request_data +Controls how to reference 'search' in the the request_data, defaults to +'search'. =cut has 'search_arg' => ( is => 'ro', isa => Str, default => 'search' ); -=attribute_public grouped_by_arg is: ro, isa: Str, default: 'list_grouped_by' +=attribute_public grouped_by_arg -grouped_by_arg controls how to reference 'grouped_by' in the the request_data +Controls how to reference 'grouped_by' in the the request_data, defaults to +'list_grouped_by'. =cut -has 'grouped_by_arg' => ( is => 'ro', isa => Str, default => 'list_grouped_by' ); +has 'grouped_by_arg' => + ( is => 'ro', isa => Str, default => 'list_grouped_by' ); -=attribute_public ordered_by_arg is: ro, isa: Str, default: 'list_ordered_by' +=attribute_public ordered_by_arg -ordered_by_arg controls how to reference 'ordered_by' in the the request_data +Controls how to reference 'ordered_by' in the the request_data, defaults to +'list_ordered_by'. =cut -has 'ordered_by_arg' => ( is => 'ro', isa => Str, default => 'list_ordered_by' ); +has 'ordered_by_arg' => + ( is => 'ro', isa => Str, default => 'list_ordered_by' ); -=attribute_public prefetch_arg is: ro, isa: Str, default: 'list_prefetch' +=attribute_public prefetch_arg -prefetch_arg controls how to reference 'prefetch' in the the request_data +Controls how to reference 'prefetch' in the the request_data, defaults to +'list_prefetch'. =cut has 'prefetch_arg' => ( is => 'ro', isa => Str, default => 'list_prefetch' ); -=attribute_public data_root is: ro, isa: Str, default: 'list' +=attribute_public stash_key + +Controls where in the stash the request_data should be stored, defaults to +'response'. + +=cut + +has 'stash_key' => ( is => 'ro', isa => Str, default => 'response' ); + +=attribute_public data_root -data_root controls how to reference where the data is in the the request_data +Controls how to reference where the data is in the the request_data, defaults to +'list'. =cut -has 'data_root' => ( is => 'ro', isa => Str, default => 'list'); +has 'data_root' => ( is => 'ro', isa => Str, default => 'list' ); -=attribute_public item_root is: ro, isa: Str, default: 'data' +=attribute_public item_root -item_root controls how to reference where the data for single object -requests is in the the request_data +Controls how to reference where the data for single object requests is in the +the request_data, defaults to 'data'. =cut -has 'item_root' => ( is => 'ro', isa => Str, default => 'data'); +has 'item_root' => ( is => 'ro', isa => Str, default => 'data' ); -=attribute_public total_entries_arg is: ro, isa: Str, default: 'totalcount' +=attribute_public total_entries_arg -total_entries_arg controls how to reference 'total_entries' in the the request_data +Controls how to reference 'total_entries' in the the request_data, defaults to +'totalcount'. =cut -has 'total_entries_arg' => ( is => 'ro', isa => Str, default => 'totalcount' ); +has 'total_entries_arg' => + ( is => 'ro', isa => Str, default => 'totalcount' ); -=attribute_public use_json_boolean is: ro, isa: Bool, default: 0 +=attribute_public use_json_boolean -use_json_boolean controls whether JSON::Any boolean types are used in the success parameter of the response or if raw strings are used +Controls whether JSON boolean types are used in the success parameter of the +response or if raw strings are used, defaults to false. =cut has 'use_json_boolean' => ( is => 'ro', isa => Bool, default => 0 ); -=attribute_public return_object is: ro, isa: Bool, default: 0 +=attribute_public return_object -return_object controls whether the results of create/update are serialized and returned in the response +Controls whether the results of create/update are serialized and returned in +the response, defaults to false. =cut @@ -165,7 +253,9 @@ has 'return_object' => ( is => 'ro', isa => Bool, default => 0 ); =head1 DESCRIPTION -StaticArguments is a Role that is composed by the controller to provide configuration parameters such as how where in the request data to find specific elements, and if to use JSON boolean types. +StaticArguments is a role that is composed by the controller to provide +configuration parameters such as where to find specific elements in the request +data and if to use JSON boolean types. =cut