Moving UI::TenantUtils to Utils::Tenant
Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/2be62a6b Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/2be62a6b Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/2be62a6b Branch: refs/heads/master Commit: 2be62a6bc0942bffa125466c4e108f1310c923ba Parents: ab80179 Author: nir-sopher <[email protected]> Authored: Mon Jun 26 21:03:55 2017 +0300 Committer: Jeremy Mitchell <[email protected]> Committed: Tue Jul 18 12:12:32 2017 -0600 ---------------------------------------------------------------------- traffic_ops/app/lib/API/Tenant.pm | 12 +- traffic_ops/app/lib/API/User.pm | 6 +- traffic_ops/app/lib/UI/TenantUtils.pm | 403 ----------------------------- traffic_ops/app/lib/UI/User.pm | 4 +- traffic_ops/app/lib/Utils/Tenant.pm | 403 +++++++++++++++++++++++++++++ traffic_ops/app/t/api/1.2/tenant.t | 12 +- 6 files changed, 420 insertions(+), 420 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/2be62a6b/traffic_ops/app/lib/API/Tenant.pm ---------------------------------------------------------------------- diff --git a/traffic_ops/app/lib/API/Tenant.pm b/traffic_ops/app/lib/API/Tenant.pm index 932ab0f..8600871 100644 --- a/traffic_ops/app/lib/API/Tenant.pm +++ b/traffic_ops/app/lib/API/Tenant.pm @@ -17,7 +17,7 @@ package API::Tenant; # use UI::Utils; -use UI::TenantUtils; +use Utils::Tenant; use Mojo::Base 'Mojolicious::Controller'; use Data::Dumper; @@ -36,7 +36,7 @@ sub index { my $self = shift; my $orderby = $self->param('orderby') || "name"; - my $tenant_utils = UI::TenantUtils->new($self); + my $tenant_utils = Utils::Tenant->new($self); my $tenants_data = $tenant_utils->create_tenants_data_from_db($orderby); my @data = (); @@ -63,7 +63,7 @@ sub show { my $self = shift; my $id = $self->param('id'); - my $tenant_utils = UI::TenantUtils->new($self); + my $tenant_utils = Utils::Tenant->new($self); my $tenants_data = $tenant_utils->create_tenants_data_from_db(undef); my @data = (); @@ -116,7 +116,7 @@ sub update { } - my $tenant_utils = UI::TenantUtils->new($self); + my $tenant_utils = Utils::Tenant->new($self); my $tenants_data = $tenant_utils->create_tenants_data_from_db(undef); if ( $tenant_utils->is_root_tenant($tenants_data, $id) ) { @@ -243,7 +243,7 @@ sub create { return $self->alert("Parent Id is required."); } - my $tenant_utils = UI::TenantUtils->new($self); + my $tenant_utils = Utils::Tenant->new($self); my $tenants_data = $tenant_utils->create_tenants_data_from_db(undef); if (!$tenant_utils->is_tenant_resource_accessible($tenants_data, $params->{parentId})) { @@ -328,7 +328,7 @@ sub delete { my $parent_tenant = $tenant->parent_id; - my $tenant_utils = UI::TenantUtils->new($self); + my $tenant_utils = Utils::Tenant->new($self); my $tenants_data = $tenant_utils->create_tenants_data_from_db(undef); if (!$tenant_utils->is_tenant_resource_accessible($tenants_data, $parent_tenant)) { http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/2be62a6b/traffic_ops/app/lib/API/User.pm ---------------------------------------------------------------------- diff --git a/traffic_ops/app/lib/API/User.pm b/traffic_ops/app/lib/API/User.pm index e98f969..9f905d6 100644 --- a/traffic_ops/app/lib/API/User.pm +++ b/traffic_ops/app/lib/API/User.pm @@ -18,7 +18,7 @@ package API::User; # JvD Note: you always want to put Utils as the first use. Sh*t don't work if it's after the Mojo lines. use UI::Utils; -use UI::TenantUtils; +use Utils::Tenant; use Mojo::Base 'Mojolicious::Controller'; use Utils::Helper; @@ -250,7 +250,7 @@ sub create { } #setting tenant_id to the user's tenant if tenant is not set. TODO(nirs): remove when tenancy is no longer optional in the API - my $tenantUtils = UI::TenantUtils->new($self); + my $tenantUtils = Utils::Tenant->new($self); my $tenant_id = exists( $params->{tenantId} ) ? $params->{tenantId} : $tenantUtils->current_user_tenant(); my $values = { @@ -406,7 +406,7 @@ sub current { my $self = shift; my @data; my $current_username = $self->current_user()->{username}; -it if ( &is_ldap($self) ) { + if ( &is_ldap($self) ) { my $role = $self->db->resultset('Role')->search( { name => "read-only" } )->get_column('id')->single; push( http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/2be62a6b/traffic_ops/app/lib/UI/TenantUtils.pm ---------------------------------------------------------------------- diff --git a/traffic_ops/app/lib/UI/TenantUtils.pm b/traffic_ops/app/lib/UI/TenantUtils.pm deleted file mode 100644 index bdbe586..0000000 --- a/traffic_ops/app/lib/UI/TenantUtils.pm +++ /dev/null @@ -1,403 +0,0 @@ -package UI::TenantUtils; -# -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# -# - -# -# This class provide utilities to examine different tenancy aspects, -# and specifically tenancy based access restrictions. -# -# The class itself is almost* stateless. However in order to reduce calls to DB, its -# API allows the user to create the "DATA" object (using "create_tenants_data_from_db") -# and run utility functions over it. -# -# For now, until the current user tenant ID will come from the jwt, the current user tenant is taken from the DB. -# In order to reduce the number of calls from the DB, the current user tenant is taken in the class creation -# -# -# A usage example - examing if a tenant can update a delivery-service: -# my $tenant_utils = UI::TenantUtils->new($self); -# my $tenants_data = $tenant_utils->create_tenants_data_from_db(); -# if (!$tenant_utils->is_ds_writeable($tenants_data, <resource_tenant>)) { -# return $self->forbidden(); #Parent tenant is not under user's tenancy -# } -# - -use Data::Dumper; -use UI::Utils; - -sub new { - my $class = shift; - my $context = shift; - my $current_user_tenant = shift - ; #optional - allowing the user tenancy to be set from outside, for testing capabilities - my $dbh = shift - ; #optional - allowing the DB handle to be set from outside, for testing capabilities - - if ( !defined($current_user_tenant) ) { - -# For now, until the current user tenant ID will come from the jwt, the current user tenant is taken from the DB. - $current_user_tenant = - $context->db->resultset('TmUser') - ->search( { username => $context->current_user()->{username} } ) - ->get_column('tenant_id')->single(); - } - - if ( !defined($dbh) ) { - $dbh = $context->db; - } - - my $ignore_tenancy_value = $dbh->resultset("Parameter")->search( { config_file => 'global', name => 'ignore-tenancy' } ) - ->get_column('value')->single(); - my $ignore_tenancy = defined($ignore_tenancy_value) ? $ignore_tenancy_value : 0; - - my $self = { - dbh => $dbh, - context => $context, #saving the context - use it only for log please... - -# In order to reduce the number of calls from the DB, the current user tenant is taken in the class creation. -# the below parameters are held temporarily until the info is taken from the jwt - current_user_tenant => $current_user_tenant, - ignore_tenancy => $ignore_tenancy, - }; - bless $self, $class; - return $self; -} - -sub create_tenants_data_from_db { - my $self = shift; - my $orderby = shift || "name"; #some default - - my $tenants_data = { - tenants_dict => undef, - root_tenants => undef, - order_by => -1, - ordered_tenants_list => undef, - }; - $tenants_data->{order_by} = $orderby; - - # read the data from the DB - my $tenants_table = $self->{dbh}->resultset("Tenant") - ->search( undef, { order_by => $tenants_data->{order_by} } ); - - # build the tenants dict and list. tenants list is kept ordered - $tenants_data->{ordered_tenants_list} = (); - $tenants_data->{tenants_dict} = {}; - while ( my $row = $tenants_table->next ) { - push( @{ $tenants_data->{ordered_tenants_list} }, $row->id ); - $tenants_data->{tenants_dict}->{ $row->id } = { - row => $row, - parent => $row->parent_id, - children => (), - }; - } - - #build the root and children tenants lists, ordered by the orderby - $tenants_data->{root_tenants} = (); - foreach my $key ( @{ $tenants_data->{ordered_tenants_list} } ) { - my $value = $tenants_data->{tenants_dict}->{$key}; - my $parent = $value->{parent}; - if ( !defined($parent) ) { - push @{ $tenants_data->{root_tenants} }, $key; - } - else { - push @{ $tenants_data->{tenants_dict}->{$parent}{children} }, $key; - } - } - - return $tenants_data; -} - -sub current_user_tenant { - my $self = shift; - return $self->{current_user_tenant}; -} - -sub get_tenant_by_id { - my $self = shift; - my $tenants_data = shift; - my $tenant_id = shift; - - return $tenants_data->{tenants_dict}->{$tenant_id}{row}; -} - -sub get_tenants_list { - my $self = shift; - my $tenants_data = shift; - - my @result = (); - foreach my $tenant_id ( @{ $tenants_data->{ordered_tenants_list} } ) { - push @result, $tenants_data->{tenants_dict}->{$tenant_id}{row}; - } - - return @result; -} - -sub get_hierarchic_tenants_list { - my $self = shift; - my $tenants_data = shift; - my $tree_root = shift; - -#building an heirarchic list via standard DFS. -#First - adding to the stack the root nodes under which we want to get the tenats - my @stack = (); - if ( defined($tree_root) ) { - push( @stack, $tree_root ); - } - else { -# root is not set, putting all roots, using "reverse" as we push it into a stack -# (from which we pop) and we want to keep the original order - push( @stack, reverse( @{ $tenants_data->{root_tenants} } ) ); - } - -#starting the actual DFS, poping from the stack, and pushing the poped node children - my @result = (); - while (@stack) { - my $tenant_id = pop @stack; - push( @result, $tenants_data->{tenants_dict}->{$tenant_id}{row} ); - - # pushing the children in a reverse order, as we working with stack and we - # pop from the end (but want to keep the overall order) - push( - @stack, - reverse( - @{ $tenants_data->{tenants_dict}->{$tenant_id}{children} } - ) - ); - } - - return @result; -} - -sub is_root_tenant { - my $self = shift; - my $tenants_data = shift; - my $tenant_id = shift; - - if ( !defined($tenant_id) ) { - return 0; - } - - #root <==> parent is undef - return !( defined( $tenants_data->{tenants_dict}{$tenant_id}{parent} ) ); -} - -sub is_tenant_resource_accessible { - my $self = shift; - my $tenants_data = shift; - my $resource_tenancy = shift; - - return $self->_is_resource_accessable( $tenants_data, $resource_tenancy); -} - -sub get_tenant_heirarchy_depth { - - #return "undef" in case of error - #a root tenant is of depth 0 - my $self = shift; - my $tenants_data = shift; - my $tenant_id = shift; - - if ( !defined( $tenants_data->{tenants_dict}{$tenant_id} ) ) { - $self->_error( - "Check tenancy depth - tenant $tenant_id does not exists"); - return undef; - } - - my $iter_id = $tenant_id; - - my $depth = 0; - while ( defined($iter_id) ) { - $iter_id = $tenants_data->{tenants_dict}{$iter_id}{parent}; - $depth++; - if ( $depth > $self->max_heirarchy_limit() ) { - $self->_error( -"Check tenancy depth for tenant $tenant_id - reached heirarchy limit" - ); - return undef; - } - } - - return $depth - 1; -} - -sub get_tenant_heirarchy_height { - - #return "undef" in case of error - #a leaf tenant is of height 0 - my $self = shift; - my $tenants_data = shift; - my $tenant_id = shift; - - if ( !defined( $tenants_data->{tenants_dict}{$tenant_id} ) ) { - $self->_error( - "Check tenancy height - tenant $tenant_id does not exists"); - return undef; - } - - #calc tenant height - my @tenants_list = reverse( - $self->get_hierarchic_tenants_list( $tenants_data, $tenant_id ) ); - my %tenants_height = {}; - - foreach my $tenant_row (@tenants_list) { - my $tid = $tenant_row->id; - $tenants_height{$tid} = 0; - } - - foreach my $tenant_row (@tenants_list) { - my $tid = $tenant_row->id; - my $par_id = $tenant_row->parent_id; - if ( ( $tenants_height{$par_id} ) < ( $tenants_height{$tid} + 1 ) ) { - $tenants_height{$par_id} = $tenants_height{$tid} + 1; - } - } - - return $tenants_height{$tenant_id}; -} - -sub is_anchestor_of { - - #return "undef" in case of error - my $self = shift; - my $tenants_data = shift; - my $anchestor_id = shift; - my $descendant_id = shift; - - if ( !defined($anchestor_id) ) { - $self->_error("Check tenants relations - got undef anchestor"); - return undef; - } - - if ( !defined( $tenants_data->{tenants_dict}{$anchestor_id} ) ) { - $self->_error( - "Check tenants relations - tenant $anchestor_id does not exists"); - return undef; - } - - if ( !defined($descendant_id) ) { - $self->_error("Check tenants relations - got undef descendant"); - return undef; - } - - if ( !defined( $tenants_data->{tenants_dict}{$descendant_id} ) ) { - $self->_error( - "Check tenants relations - tenant $descendant_id does not exists"); - return undef; - } - - my $iter_id = $descendant_id; - - my $descendant_depth = 0; - while ( defined($iter_id) ) { - if ( $anchestor_id == $iter_id ) { - return 1; - } - $iter_id = $tenants_data->{tenants_dict}{$iter_id}{parent}; - $descendant_depth++; - if ( $descendant_depth > $self->max_heirarchy_limit() ) - { #recursion limit - $self->_error( -"Tenants relation failed for tenants $anchestor_id / $descendant_id - reached heirarchy limit" - ); - return undef; - } - } - - return 0; -} - -sub max_heirarchy_limit { - my $self = shift; - return 100; -} - -############################################################## - -sub _error { - my $self = shift; - my $message = shift; - - $context = $self->{context}; - if ( defined($context) ) { - $context->app->log->error($message); - } - else { - print "Error: ", $message, "\n"; - } -} - -sub _is_resource_accessable { - my $self = shift; - my $tenants_data = shift; - my $resource_tenant = shift; - my $user_tenant = $self->current_user_tenant(); - return $self->_is_resource_accessable_to_tenant($tenants_data, $resource_tenant, $user_tenant) -} - -sub _is_resource_accessable_to_tenant { - my $self = shift; - my $tenants_data = shift; - my $resource_tenant = shift; - my $user_tenant = shift; - - if ($self->{ignore_tenancy}) { - #mechanisem disabled - return 1; - } - - - if ( defined($user_tenant) ) { - my $tenant_record = $tenants_data->{tenants_dict}->{$user_tenant}; - my $is_active_tenant = $tenant_record->{row}->active; - if ( !$is_active_tenant ) { - - #user tenant is in-active - cannot do any operation - return 0; - } - } - - if ( !defined($resource_tenant) ) { - - #the object has no tenancy - opened for all - return 1; - } - - my $user_tenant = $self->current_user_tenant(); - if ( !defined($user_tenant) ) { - - #the user has no tenancy, - cannot approach items with tenancy - return 0; - } - - if ( $user_tenant == $resource_tenant ) { - - #resource has same tenancy of the user, operations are allowed - return 1; - } - - #checking if the user tenant is an ancestor of the resource tenant - my $is_user_tenat_parent_of_resource = - $self->is_anchestor_of( $tenants_data, $user_tenant, $resource_tenant ); - if ( !defined($is_user_tenat_parent_of_resource) ) { - - #error - give access only to root tenant (so it can fix the problem) - return $self->is_root_tenant( $tenants_data, $user_tenant ); - } - return $is_user_tenat_parent_of_resource; -} - -1; http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/2be62a6b/traffic_ops/app/lib/UI/User.pm ---------------------------------------------------------------------- diff --git a/traffic_ops/app/lib/UI/User.pm b/traffic_ops/app/lib/UI/User.pm index 2f7c1fe..a8d5487 100644 --- a/traffic_ops/app/lib/UI/User.pm +++ b/traffic_ops/app/lib/UI/User.pm @@ -18,7 +18,7 @@ package UI::User; # JvD Note: you always want to put Utils as the first use. Sh*t don't work if it's after the Mojo lines. use UI::Utils; -use UI::TenantUtils; +use Utils::Tenant; use Mojo::Base 'Mojolicious::Controller'; use Utils::Helper; @@ -286,7 +286,7 @@ sub is_send_register_valid { sub create_user { my $self = shift; my $new_id = -1; - my $tenantUtils = UI::TenantUtils->new($self); + my $tenantUtils = Utils::Tenant->new($self); my $dbh = $self->db->resultset('TmUser')->create( { full_name => $self->param('tm_user.full_name'), http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/2be62a6b/traffic_ops/app/lib/Utils/Tenant.pm ---------------------------------------------------------------------- diff --git a/traffic_ops/app/lib/Utils/Tenant.pm b/traffic_ops/app/lib/Utils/Tenant.pm new file mode 100644 index 0000000..23fa32b --- /dev/null +++ b/traffic_ops/app/lib/Utils/Tenant.pm @@ -0,0 +1,403 @@ +package Utils::Tenant; +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# + +# +# This class provide utilities to examine different tenancy aspects, +# and specifically tenancy based access restrictions. +# +# The class itself is almost* stateless. However in order to reduce calls to DB, its +# API allows the user to create the "DATA" object (using "create_tenants_data_from_db") +# and run utility functions over it. +# +# For now, until the current user tenant ID will come from the jwt, the current user tenant is taken from the DB. +# In order to reduce the number of calls from the DB, the current user tenant is taken in the class creation +# +# +# A usage example - examing if a tenant can update a delivery-service: +# my $tenant_utils = Utils::Tenant->new($self); +# my $tenants_data = $tenant_utils->create_tenants_data_from_db(); +# if (!$tenant_utils->is_ds_writeable($tenants_data, <resource_tenant>)) { +# return $self->forbidden(); #Parent tenant is not under user's tenancy +# } +# + +use Data::Dumper; +use UI::Utils; + +sub new { + my $class = shift; + my $context = shift; + my $current_user_tenant = shift + ; #optional - allowing the user tenancy to be set from outside, for testing capabilities + my $dbh = shift + ; #optional - allowing the DB handle to be set from outside, for testing capabilities + + if ( !defined($current_user_tenant) ) { + +# For now, until the current user tenant ID will come from the jwt, the current user tenant is taken from the DB. + $current_user_tenant = + $context->db->resultset('TmUser') + ->search( { username => $context->current_user()->{username} } ) + ->get_column('tenant_id')->single(); + } + + if ( !defined($dbh) ) { + $dbh = $context->db; + } + + my $ignore_tenancy_value = $dbh->resultset("Parameter")->search( { config_file => 'global', name => 'ignore-tenancy' } ) + ->get_column('value')->single(); + my $ignore_tenancy = defined($ignore_tenancy_value) ? $ignore_tenancy_value : 0; + + my $self = { + dbh => $dbh, + context => $context, #saving the context - use it only for log please... + +# In order to reduce the number of calls from the DB, the current user tenant is taken in the class creation. +# the below parameters are held temporarily until the info is taken from the jwt + current_user_tenant => $current_user_tenant, + ignore_tenancy => $ignore_tenancy, + }; + bless $self, $class; + return $self; +} + +sub create_tenants_data_from_db { + my $self = shift; + my $orderby = shift || "name"; #some default + + my $tenants_data = { + tenants_dict => undef, + root_tenants => undef, + order_by => -1, + ordered_tenants_list => undef, + }; + $tenants_data->{order_by} = $orderby; + + # read the data from the DB + my $tenants_table = $self->{dbh}->resultset("Tenant") + ->search( undef, { order_by => $tenants_data->{order_by} } ); + + # build the tenants dict and list. tenants list is kept ordered + $tenants_data->{ordered_tenants_list} = (); + $tenants_data->{tenants_dict} = {}; + while ( my $row = $tenants_table->next ) { + push( @{ $tenants_data->{ordered_tenants_list} }, $row->id ); + $tenants_data->{tenants_dict}->{ $row->id } = { + row => $row, + parent => $row->parent_id, + children => (), + }; + } + + #build the root and children tenants lists, ordered by the orderby + $tenants_data->{root_tenants} = (); + foreach my $key ( @{ $tenants_data->{ordered_tenants_list} } ) { + my $value = $tenants_data->{tenants_dict}->{$key}; + my $parent = $value->{parent}; + if ( !defined($parent) ) { + push @{ $tenants_data->{root_tenants} }, $key; + } + else { + push @{ $tenants_data->{tenants_dict}->{$parent}{children} }, $key; + } + } + + return $tenants_data; +} + +sub current_user_tenant { + my $self = shift; + return $self->{current_user_tenant}; +} + +sub get_tenant_by_id { + my $self = shift; + my $tenants_data = shift; + my $tenant_id = shift; + + return $tenants_data->{tenants_dict}->{$tenant_id}{row}; +} + +sub get_tenants_list { + my $self = shift; + my $tenants_data = shift; + + my @result = (); + foreach my $tenant_id ( @{ $tenants_data->{ordered_tenants_list} } ) { + push @result, $tenants_data->{tenants_dict}->{$tenant_id}{row}; + } + + return @result; +} + +sub get_hierarchic_tenants_list { + my $self = shift; + my $tenants_data = shift; + my $tree_root = shift; + +#building an heirarchic list via standard DFS. +#First - adding to the stack the root nodes under which we want to get the tenats + my @stack = (); + if ( defined($tree_root) ) { + push( @stack, $tree_root ); + } + else { +# root is not set, putting all roots, using "reverse" as we push it into a stack +# (from which we pop) and we want to keep the original order + push( @stack, reverse( @{ $tenants_data->{root_tenants} } ) ); + } + +#starting the actual DFS, poping from the stack, and pushing the poped node children + my @result = (); + while (@stack) { + my $tenant_id = pop @stack; + push( @result, $tenants_data->{tenants_dict}->{$tenant_id}{row} ); + + # pushing the children in a reverse order, as we working with stack and we + # pop from the end (but want to keep the overall order) + push( + @stack, + reverse( + @{ $tenants_data->{tenants_dict}->{$tenant_id}{children} } + ) + ); + } + + return @result; +} + +sub is_root_tenant { + my $self = shift; + my $tenants_data = shift; + my $tenant_id = shift; + + if ( !defined($tenant_id) ) { + return 0; + } + + #root <==> parent is undef + return !( defined( $tenants_data->{tenants_dict}{$tenant_id}{parent} ) ); +} + +sub is_tenant_resource_accessible { + my $self = shift; + my $tenants_data = shift; + my $resource_tenancy = shift; + + return $self->_is_resource_accessable( $tenants_data, $resource_tenancy); +} + +sub get_tenant_heirarchy_depth { + + #return "undef" in case of error + #a root tenant is of depth 0 + my $self = shift; + my $tenants_data = shift; + my $tenant_id = shift; + + if ( !defined( $tenants_data->{tenants_dict}{$tenant_id} ) ) { + $self->_error( + "Check tenancy depth - tenant $tenant_id does not exists"); + return undef; + } + + my $iter_id = $tenant_id; + + my $depth = 0; + while ( defined($iter_id) ) { + $iter_id = $tenants_data->{tenants_dict}{$iter_id}{parent}; + $depth++; + if ( $depth > $self->max_heirarchy_limit() ) { + $self->_error( +"Check tenancy depth for tenant $tenant_id - reached heirarchy limit" + ); + return undef; + } + } + + return $depth - 1; +} + +sub get_tenant_heirarchy_height { + + #return "undef" in case of error + #a leaf tenant is of height 0 + my $self = shift; + my $tenants_data = shift; + my $tenant_id = shift; + + if ( !defined( $tenants_data->{tenants_dict}{$tenant_id} ) ) { + $self->_error( + "Check tenancy height - tenant $tenant_id does not exists"); + return undef; + } + + #calc tenant height + my @tenants_list = reverse( + $self->get_hierarchic_tenants_list( $tenants_data, $tenant_id ) ); + my %tenants_height = {}; + + foreach my $tenant_row (@tenants_list) { + my $tid = $tenant_row->id; + $tenants_height{$tid} = 0; + } + + foreach my $tenant_row (@tenants_list) { + my $tid = $tenant_row->id; + my $par_id = $tenant_row->parent_id; + if ( ( $tenants_height{$par_id} ) < ( $tenants_height{$tid} + 1 ) ) { + $tenants_height{$par_id} = $tenants_height{$tid} + 1; + } + } + + return $tenants_height{$tenant_id}; +} + +sub is_anchestor_of { + + #return "undef" in case of error + my $self = shift; + my $tenants_data = shift; + my $anchestor_id = shift; + my $descendant_id = shift; + + if ( !defined($anchestor_id) ) { + $self->_error("Check tenants relations - got undef anchestor"); + return undef; + } + + if ( !defined( $tenants_data->{tenants_dict}{$anchestor_id} ) ) { + $self->_error( + "Check tenants relations - tenant $anchestor_id does not exists"); + return undef; + } + + if ( !defined($descendant_id) ) { + $self->_error("Check tenants relations - got undef descendant"); + return undef; + } + + if ( !defined( $tenants_data->{tenants_dict}{$descendant_id} ) ) { + $self->_error( + "Check tenants relations - tenant $descendant_id does not exists"); + return undef; + } + + my $iter_id = $descendant_id; + + my $descendant_depth = 0; + while ( defined($iter_id) ) { + if ( $anchestor_id == $iter_id ) { + return 1; + } + $iter_id = $tenants_data->{tenants_dict}{$iter_id}{parent}; + $descendant_depth++; + if ( $descendant_depth > $self->max_heirarchy_limit() ) + { #recursion limit + $self->_error( +"Tenants relation failed for tenants $anchestor_id / $descendant_id - reached heirarchy limit" + ); + return undef; + } + } + + return 0; +} + +sub max_heirarchy_limit { + my $self = shift; + return 100; +} + +############################################################## + +sub _error { + my $self = shift; + my $message = shift; + + $context = $self->{context}; + if ( defined($context) ) { + $context->app->log->error($message); + } + else { + print "Error: ", $message, "\n"; + } +} + +sub _is_resource_accessable { + my $self = shift; + my $tenants_data = shift; + my $resource_tenant = shift; + my $user_tenant = $self->current_user_tenant(); + return $self->_is_resource_accessable_to_tenant($tenants_data, $resource_tenant, $user_tenant) +} + +sub _is_resource_accessable_to_tenant { + my $self = shift; + my $tenants_data = shift; + my $resource_tenant = shift; + my $user_tenant = shift; + + if ($self->{ignore_tenancy}) { + #mechanisem disabled + return 1; + } + + + if ( defined($user_tenant) ) { + my $tenant_record = $tenants_data->{tenants_dict}->{$user_tenant}; + my $is_active_tenant = $tenant_record->{row}->active; + if ( !$is_active_tenant ) { + + #user tenant is in-active - cannot do any operation + return 0; + } + } + + if ( !defined($resource_tenant) ) { + + #the object has no tenancy - opened for all + return 1; + } + + my $user_tenant = $self->current_user_tenant(); + if ( !defined($user_tenant) ) { + + #the user has no tenancy, - cannot approach items with tenancy + return 0; + } + + if ( $user_tenant == $resource_tenant ) { + + #resource has same tenancy of the user, operations are allowed + return 1; + } + + #checking if the user tenant is an ancestor of the resource tenant + my $is_user_tenat_parent_of_resource = + $self->is_anchestor_of( $tenants_data, $user_tenant, $resource_tenant ); + if ( !defined($is_user_tenat_parent_of_resource) ) { + + #error - give access only to root tenant (so it can fix the problem) + return $self->is_root_tenant( $tenants_data, $user_tenant ); + } + return $is_user_tenat_parent_of_resource; +} + +1; http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/2be62a6b/traffic_ops/app/t/api/1.2/tenant.t ---------------------------------------------------------------------- diff --git a/traffic_ops/app/t/api/1.2/tenant.t b/traffic_ops/app/t/api/1.2/tenant.t index 08a3913..7f18ede 100644 --- a/traffic_ops/app/t/api/1.2/tenant.t +++ b/traffic_ops/app/t/api/1.2/tenant.t @@ -22,7 +22,7 @@ use warnings; no warnings 'once'; use warnings 'all'; use Test::TestHelper; -use UI::TenantUtils; +use Utils::Tenant; #no_transactions=>1 ==> keep fixtures after every execution, beware of duplicate data! #no_transactions=>0 ==> delete fixtures after every execution @@ -174,7 +174,7 @@ ok $t->get_ok("/api/1.2/tenants")->status_is(200) ->json_is( "/response/1/id", $tenantB_id)->or( sub { diag $t->tx->res->content->asset->{content}; } );; #tenants heirarchy- test depth, height, root -my $tenant_utils_of_root = UI::TenantUtils->new(undef, $root_tenant_id, $schema); +my $tenant_utils_of_root = Utils::Tenant->new(undef, $root_tenant_id, $schema); my $tenants_data = $tenant_utils_of_root->create_tenants_data_from_db(); ok $tenant_utils_of_root->is_root_tenant($tenants_data, $root_tenant_id) == 1; @@ -205,7 +205,7 @@ ok $tenant_utils_of_root->is_tenant_resource_accessible($tenants_data, undef) == ok $tenant_utils_of_root->is_tenant_resource_accessible($tenants_data, $tenantA_id) == 1; ok $tenant_utils_of_root->is_tenant_resource_accessible($tenants_data, $tenantE_id) == 1; -my $tenant_utils_of_a = UI::TenantUtils->new(undef, $tenantA_id, $schema); +my $tenant_utils_of_a = Utils::Tenant->new(undef, $tenantA_id, $schema); my $tenants_data_of_a = $tenant_utils_of_a->create_tenants_data_from_db(); #parent - no access ok $tenant_utils_of_a->is_tenant_resource_accessible($tenants_data_of_a, $root_tenant_id) == 0; @@ -219,7 +219,7 @@ ok $tenant_utils_of_a->is_tenant_resource_accessible($tenants_data_of_a, $tenant ok $tenant_utils_of_a->is_tenant_resource_accessible($tenants_data_of_a, $tenantB_id) == 0; #leaf test -my $tenant_utils_of_d = UI::TenantUtils->new(undef, $tenantD_id, $schema); +my $tenant_utils_of_d = Utils::Tenant->new(undef, $tenantD_id, $schema); my $tenants_data_of_d = $tenant_utils_of_d->create_tenants_data_from_db(); #anchestor - no access ok $tenant_utils_of_d->is_tenant_resource_accessible($tenants_data_of_d, $root_tenant_id) == 0; @@ -233,7 +233,7 @@ ok $tenant_utils_of_d->is_tenant_resource_accessible($tenants_data_of_d, $tenant ok $tenant_utils_of_d->is_tenant_resource_accessible($tenants_data_of_d, $tenantB_id) == 0; #inactive - nothing can do -my $tenant_utils_of_e = UI::TenantUtils->new(undef, $tenantE_id, $schema); +my $tenant_utils_of_e = Utils::Tenant->new(undef, $tenantE_id, $schema); my $tenants_data_of_e = $tenant_utils_of_e->create_tenants_data_from_db(); #anchestor - no access ok $tenant_utils_of_e->is_tenant_resource_accessible($tenants_data_of_e, $root_tenant_id) == 0; @@ -258,7 +258,7 @@ ok $t->post_ok('/api/1.2/parameters' => {Accept => 'application/json'} => json = )->status_is(200) , 'Was the disabling paramter created?'; -my $tenant_utils_of_d_disabled = UI::TenantUtils->new(undef, $tenantD_id, $schema); +my $tenant_utils_of_d_disabled = Utils::Tenant->new(undef, $tenantD_id, $schema); my $tenants_data_of_d_disabled = $tenant_utils_of_d_disabled->create_tenants_data_from_db(); #anchestor - now can access ok $tenant_utils_of_d_disabled->is_tenant_resource_accessible($tenants_data_of_d_disabled, $root_tenant_id) == 1;
