use strict;
use warnings;
use RT::Test nodata => 1, tests => 114;

RT::Group->AddRights(
    'RTxGroupRight' => 'Just a right for testing rights',
);

# this company is split into two halves, the hackers and the non-hackers
# herbert is a hacker but eric is not.
my $herbert = RT::User->new(RT->SystemUser);
my ($ok, $msg) = $herbert->Create(Name => 'herbert');
ok($ok, $msg);

my $eric = RT::User->new(RT->SystemUser);
($ok, $msg) = $eric->Create(Name => 'eric');
ok($ok, $msg);

my $hackers = RT::Group->new(RT->SystemUser);
($ok, $msg) = $hackers->CreateUserDefinedGroup(Name => 'Hackers');
ok($ok, $msg);

my $employees = RT::Group->new(RT->SystemUser);
($ok, $msg) = $employees->CreateUserDefinedGroup(Name => 'Employees');
ok($ok, $msg);

($ok, $msg) = $employees->AddMember($hackers->PrincipalId);
ok($ok, $msg);

($ok, $msg) = $hackers->AddMember($herbert->PrincipalId);
ok($ok, $msg);

($ok, $msg) = $employees->AddMember($eric->PrincipalId);
ok($ok, $msg);

ok($employees->HasMemberRecursively($hackers->PrincipalId), 'employees has member hackers');
ok($employees->HasMemberRecursively($herbert->PrincipalId), 'employees has member herbert');
ok($employees->HasMemberRecursively($eric->PrincipalId), 'employees has member eric');

ok($hackers->HasMemberRecursively($herbert->PrincipalId), 'hackers has member herbert');
ok(!$hackers->HasMemberRecursively($eric->PrincipalId), 'hackers does not have member eric');

# There's also a separate group, "Other", which both are a member of.
my $other = RT::Group->new(RT->SystemUser);
($ok, $msg) = $other->CreateUserDefinedGroup(Name => 'Other');
ok($ok, $msg);
($ok, $msg) = $other->AddMember($eric->PrincipalId);
ok($ok, $msg);
($ok, $msg) = $other->AddMember($herbert->PrincipalId);
ok($ok, $msg);


# Everyone can SeeGroup on all three groups
my $everyone = RT::Group->new( RT->SystemUser );
($ok, $msg) = $everyone->LoadSystemInternalGroup( 'Everyone' );
ok($ok, $msg);
$everyone->PrincipalObj->GrantRight(Right => 'SeeGroup', Object => $employees);
$everyone->PrincipalObj->GrantRight(Right => 'SeeGroup', Object => $hackers);
$everyone->PrincipalObj->GrantRight(Right => 'SeeGroup', Object => $other);

sub CheckRights {
    my $cu = shift;
    my %groups = (Employees => 0, Hackers => 0, Other => 0, @_);
    my $name = $cu->Name;

    my $groups = RT::Groups->new(RT::CurrentUser->new($cu));
    $groups->LimitToUserDefinedGroups;
    $groups->ForWhichCurrentUserHasRight(Right => 'RTxGroupRight');
    my %has_right = map { ($_->Name => 1) } @{ $groups->ItemsArrayRef };

    local $Test::Builder::Level = $Test::Builder::Level + 1;
    for my $groupname (sort keys %groups) {
        my $g = RT::Group->new(RT::CurrentUser->new($cu));
        $g->LoadUserDefinedGroup($groupname);
        if ($groups{$groupname}) {
            ok( $g->CurrentUserHasRight("RTxGroupRight"), "$name has right on $groupname (direct query)" );
            ok( delete $has_right{$groupname},            "..and also in ForWhichCurrentUserHasRight");
        } else {
            ok( !$g->CurrentUserHasRight("RTxGroupRight"), "$name doesn't have right on $groupname (direct query)" );
            ok( !delete $has_right{$groupname},            "..and also not in ForWhichCurrentUserHasRight");
        }
    }
    ok(not(keys %has_right), "ForWhichCurrentUserHasRight has no extra groups");
}

# Neither should have it on any group yet
CheckRights($eric);
CheckRights($herbert);


# Grant it to employees, on employees.  Both Herbert and Eric will have
# it on employees, though Herbert gets it by way of hackers.  Neither
# will have it on hackers, because the target does not recurse.
$employees->PrincipalObj->GrantRight( Right => 'RTxGroupRight', Object => $employees);
CheckRights($eric,    Employees => 1);
CheckRights($herbert, Employees => 1);


# Grant it to employees, on hackers.  This means both Eric and Herbert
# will have the right on hackers, but not on employees.
$employees->PrincipalObj->RevokeRight(Right => 'RTxGroupRight', Object => $employees);
$employees->PrincipalObj->GrantRight( Right => 'RTxGroupRight', Object => $hackers);
CheckRights($eric,    Hackers => 1);
CheckRights($herbert, Hackers => 1);


# Grant it to hackers, on employees.  Eric will have it nowhere, and
# Herbert will have it on employees.  Note that the target of the right
# itself does _not_ recurse down, so Herbert will not have it on
# hackers.
$employees->PrincipalObj->RevokeRight(Right => 'RTxGroupRight', Object => $hackers);
$hackers->PrincipalObj->GrantRight(   Right => 'RTxGroupRight', Object => $employees);
CheckRights($eric);
CheckRights($herbert, Employees => 1);


# Grant it globally to hackers; herbert will see the right on all
# employees, hackers, and other.
$hackers->PrincipalObj->RevokeRight(  Right => 'RTxGroupRight', Object => $employees);
$hackers->PrincipalObj->GrantRight(   Right => 'RTxGroupRight', Object => RT->System);
CheckRights($eric);
CheckRights($herbert, Employees => 1, Hackers => 1, Other => 1 );


# Grant it globally to employees; both eric and herbert will see the
# right on all employees, hackers, and other.
$hackers->PrincipalObj->RevokeRight(  Right => 'RTxGroupRight', Object => RT->System);
$employees->PrincipalObj->GrantRight( Right => 'RTxGroupRight', Object => RT->System);
CheckRights($eric,    Employees => 1, Hackers => 1, Other => 1 );
CheckRights($herbert, Employees => 1, Hackers => 1, Other => 1 );


# Disable the employees group.  Neither eric nor herbert will see the
# right anywhere.
$employees->SetDisabled(1);
CheckRights($eric);
CheckRights($herbert);
