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;

Reply via email to