Commit 9d4872be authored by bugreport%peshkin.net's avatar bugreport%peshkin.net

Bug 304583: Remove all remaining need to rederive inherited groups

Patch by Joel Peshkin <bugreport@peshkin.net> r=mkanat, a=justdave
parent d11ebe02
...@@ -38,6 +38,7 @@ sub login { ...@@ -38,6 +38,7 @@ sub login {
my $matched_userid = ''; my $matched_userid = '';
my $matched_extern_id = ''; my $matched_extern_id = '';
my $disabledtext = ''; my $disabledtext = '';
my $new_login_name = 0;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $sth; my $sth;
...@@ -122,6 +123,7 @@ sub login { ...@@ -122,6 +123,7 @@ sub login {
") VALUES ( ?, ?, ?, '' )"); ") VALUES ( ?, ?, ?, '' )");
$sth->execute($env_email, '*', $env_realname); $sth->execute($env_email, '*', $env_realname);
$matched_userid = $dbh->bz_last_key('profiles', 'userid'); $matched_userid = $dbh->bz_last_key('profiles', 'userid');
$new_login_name = $matched_userid;
} }
} }
} }
...@@ -147,9 +149,16 @@ sub login { ...@@ -147,9 +149,16 @@ sub login {
($env_realname || $this_realname), ($env_realname || $this_realname),
$matched_userid); $matched_userid);
$sth->execute; $sth->execute;
$new_login_name = $matched_userid;
} }
} }
# If the login name may be new, make sure the regexp groups are current
if ($new_login_name) {
my $userprofile = new Bugzilla::User($matched_userid);
$userprofile->derive_regexp_groups;
}
# Now we throw an error if the user has been disabled # Now we throw an error if the user has been disabled
if ($disabledtext) { if ($disabledtext) {
ThrowUserError("account_disabled", ThrowUserError("account_disabled",
......
...@@ -541,25 +541,22 @@ sub groups { ...@@ -541,25 +541,22 @@ sub groups {
# user_group_map record putting the user in that group. # user_group_map record putting the user in that group.
# The LEFT JOINs are checking for record existence. # The LEFT JOINs are checking for record existence.
# #
my $grouplist = Bugzilla->user->groups_as_string;
my $sth = $dbh->prepare( my $sth = $dbh->prepare(
"SELECT DISTINCT groups.id, name, description," . "SELECT DISTINCT groups.id, name, description," .
" bug_group_map.group_id IS NOT NULL," . " bug_group_map.group_id IS NOT NULL," .
" user_group_map.group_id IS NOT NULL," . " CASE WHEN groups.id IN($grouplist) THEN 1 ELSE 0 END," .
" isactive, membercontrol, othercontrol" . " isactive, membercontrol, othercontrol" .
" FROM groups" . " FROM groups" .
" LEFT JOIN bug_group_map" . " LEFT JOIN bug_group_map" .
" ON bug_group_map.group_id = groups.id" . " ON bug_group_map.group_id = groups.id" .
" AND bug_id = ?" . " AND bug_id = ?" .
" LEFT JOIN user_group_map" .
" ON user_group_map.group_id = groups.id" .
" AND user_id = ?" .
" AND isbless = 0" .
" LEFT JOIN group_control_map" . " LEFT JOIN group_control_map" .
" ON group_control_map.group_id = groups.id" . " ON group_control_map.group_id = groups.id" .
" AND group_control_map.product_id = ? " . " AND group_control_map.product_id = ? " .
" WHERE isbuggroup = 1" . " WHERE isbuggroup = 1" .
" ORDER BY description"); " ORDER BY description");
$sth->execute($self->{'bug_id'}, Bugzilla->user->id, $sth->execute($self->{'bug_id'},
$self->{'product_id'}); $self->{'product_id'});
while (my ($groupid, $name, $description, $ison, $ingroup, $isactive, while (my ($groupid, $name, $description, $ison, $ingroup, $isactive,
......
...@@ -53,7 +53,6 @@ use base qw(Exporter); ...@@ -53,7 +53,6 @@ use base qw(Exporter);
LOGOUT_KEEP_CURRENT LOGOUT_KEEP_CURRENT
GRANT_DIRECT GRANT_DIRECT
GRANT_DERIVED
GRANT_REGEXP GRANT_REGEXP
GROUP_MEMBERSHIP GROUP_MEMBERSHIP
...@@ -68,8 +67,6 @@ use base qw(Exporter); ...@@ -68,8 +67,6 @@ use base qw(Exporter);
COMMENT_COLS COMMENT_COLS
DERIVE_GROUPS_TABLES_ALREADY_LOCKED
UNLOCK_ABORT UNLOCK_ABORT
RELATIONSHIPS RELATIONSHIPS
...@@ -157,7 +154,6 @@ use constant contenttypes => ...@@ -157,7 +154,6 @@ use constant contenttypes =>
}; };
use constant GRANT_DIRECT => 0; use constant GRANT_DIRECT => 0;
use constant GRANT_DERIVED => 1;
use constant GRANT_REGEXP => 2; use constant GRANT_REGEXP => 2;
use constant GROUP_MEMBERSHIP => 0; use constant GROUP_MEMBERSHIP => 0;
...@@ -180,10 +176,6 @@ use constant DEFAULT_QUERY_NAME => '(Default query)'; ...@@ -180,10 +176,6 @@ use constant DEFAULT_QUERY_NAME => '(Default query)';
# The column length for displayed (and wrapped) bug comments. # The column length for displayed (and wrapped) bug comments.
use constant COMMENT_COLS => 80; use constant COMMENT_COLS => 80;
# Used to indicate to User::new and User::new_from_login calls
# that the derive_groups tables are already locked
use constant DERIVE_GROUPS_TABLES_ALREADY_LOCKED => 1;
# used by Bugzilla::DB to indicate that tables are being unlocked # used by Bugzilla::DB to indicate that tables are being unlocked
# because of error # because of error
use constant UNLOCK_ABORT => 1; use constant UNLOCK_ABORT => 1;
......
...@@ -906,9 +906,7 @@ sub notify { ...@@ -906,9 +906,7 @@ sub notify {
{ {
my @new_cc_list; my @new_cc_list;
foreach my $cc (split(/[, ]+/, $flag->{'type'}->{'cc_list'})) { foreach my $cc (split(/[, ]+/, $flag->{'type'}->{'cc_list'})) {
my $ccuser = Bugzilla::User->new_from_login($cc, my $ccuser = Bugzilla::User->new_from_login($cc) || next;
DERIVE_GROUPS_TABLES_ALREADY_LOCKED)
|| next;
next if $flag->{'target'}->{'bug'}->{'restricted'} next if $flag->{'target'}->{'bug'}->{'restricted'}
&& !$ccuser->can_see_bug($flag->{'target'}->{'bug'}->{'id'}); && !$ccuser->can_see_bug($flag->{'target'}->{'bug'}->{'id'});
......
...@@ -111,8 +111,6 @@ sub _create { ...@@ -111,8 +111,6 @@ sub _create {
# We're checking for validity here, so any value is OK # We're checking for validity here, so any value is OK
trick_taint($val); trick_taint($val);
my $tables_locked_for_derive_groups = shift;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my ($id, my ($id,
...@@ -137,26 +135,6 @@ sub _create { ...@@ -137,26 +135,6 @@ sub _create {
$self->{'disabledtext'} = $disabledtext; $self->{'disabledtext'} = $disabledtext;
$self->{'showmybugslink'} = $mybugslink; $self->{'showmybugslink'} = $mybugslink;
# Now update any old group information if needed
my $result = $dbh->selectrow_array(q{SELECT 1
FROM profiles, groups
WHERE userid=?
AND profiles.refreshed_when <=
groups.last_changed},
undef,
$id);
if ($result) {
my $is_main_db;
unless ($is_main_db = Bugzilla->dbwritesallowed()) {
Bugzilla->switch_to_main_db();
}
$self->derive_groups($tables_locked_for_derive_groups);
unless ($is_main_db) {
Bugzilla->switch_to_shadow_db();
}
}
return $self; return $self;
} }
...@@ -283,11 +261,36 @@ sub groups { ...@@ -283,11 +261,36 @@ sub groups {
# The above gives us an arrayref [name, id, name, id, ...] # The above gives us an arrayref [name, id, name, id, ...]
# Convert that into a hashref # Convert that into a hashref
my %groups = @$groups; my %groups = @$groups;
my $sth;
my @groupidstocheck = values(%groups);
my %groupidschecked = ();
$sth = $dbh->prepare("SELECT groups.name, groups.id
FROM group_group_map
INNER JOIN groups
ON groups.id = grantor_id
WHERE member_id = ?
AND grant_type = " . GROUP_MEMBERSHIP);
while (my $node = shift @groupidstocheck) {
$sth->execute($node);
my ($member_name, $member_id);
while (($member_name, $member_id) = $sth->fetchrow_array) {
if (!$groupidschecked{$member_id}) {
$groupidschecked{$member_id} = 1;
push @groupidstocheck, $member_id;
$groups{$member_name} = $member_id;
}
}
}
$self->{groups} = \%groups; $self->{groups} = \%groups;
return $self->{groups}; return $self->{groups};
} }
sub groups_as_string {
my $self = shift;
return (join(',',values(%{$self->groups})) || '-1');
}
sub bless_groups { sub bless_groups {
my $self = shift; my $self = shift;
...@@ -308,8 +311,6 @@ sub bless_groups { ...@@ -308,8 +311,6 @@ sub bless_groups {
# Get all groups for the user where: # Get all groups for the user where:
# + They have direct bless privileges # + They have direct bless privileges
# + They are a member of a group that inherits bless privs. # + They are a member of a group that inherits bless privs.
# Because of the second requirement, derive_groups must be up-to-date
# for this to function properly in all circumstances.
$query = q{ $query = q{
SELECT DISTINCT groups.id, groups.name, groups.description SELECT DISTINCT groups.id, groups.name, groups.description
FROM groups, user_group_map, group_group_map AS ggm FROM groups, user_group_map, group_group_map AS ggm
...@@ -318,8 +319,9 @@ sub bless_groups { ...@@ -318,8 +319,9 @@ sub bless_groups {
AND groups.id=user_group_map.group_id) AND groups.id=user_group_map.group_id)
OR (groups.id = ggm.grantor_id OR (groups.id = ggm.grantor_id
AND ggm.grant_type = ? AND ggm.grant_type = ?
AND user_group_map.group_id = ggm.member_id AND ggm.member_id IN(} .
AND user_group_map.isbless = 0))}; $self->groups_as_string .
q{)))};
$connector = 'AND'; $connector = 'AND';
@bindValues = ($self->id, GROUP_BLESS); @bindValues = ($self->id, GROUP_BLESS);
} }
...@@ -340,26 +342,13 @@ sub bless_groups { ...@@ -340,26 +342,13 @@ sub bless_groups {
sub in_group { sub in_group {
my ($self, $group) = @_; my ($self, $group) = @_;
return exists $self->groups->{$group} ? 1 : 0;
}
# If we already have the info, just return it. sub in_group_id {
return defined($self->{groups}->{$group}) if defined $self->{groups}; my ($self, $id) = @_;
return 0 unless $self->id; my %j = reverse(%{$self->groups});
return exists $j{$id} ? 1 : 0;
# Otherwise, go check for it
my $dbh = Bugzilla->dbh;
my ($res) = $dbh->selectrow_array(q{SELECT 1
FROM groups, user_group_map
WHERE groups.id=user_group_map.group_id
AND user_group_map.user_id=?
AND isbless=0
AND groups.name=?},
undef,
$self->id,
$group);
return defined($res);
} }
sub can_see_user { sub can_see_user {
...@@ -405,7 +394,7 @@ sub can_see_bug { ...@@ -405,7 +394,7 @@ sub can_see_bug {
LEFT JOIN bug_group_map LEFT JOIN bug_group_map
ON bugs.bug_id = bug_group_map.bug_id ON bugs.bug_id = bug_group_map.bug_id
AND bug_group_map.group_ID NOT IN(" . AND bug_group_map.group_ID NOT IN(" .
join(',',(-1, values(%{$self->groups}))) . $self->groups_as_string .
") WHERE bugs.bug_id = ? ") WHERE bugs.bug_id = ?
AND creation_ts IS NOT NULL " . AND creation_ts IS NOT NULL " .
$dbh->sql_group_by('bugs.bug_id', 'reporter, ' . $dbh->sql_group_by('bugs.bug_id', 'reporter, ' .
...@@ -445,7 +434,7 @@ sub get_selectable_products { ...@@ -445,7 +434,7 @@ sub get_selectable_products {
CONTROLMAPMANDATORY . " "; CONTROLMAPMANDATORY . " ";
} }
$query .= "AND group_id NOT IN(" . $query .= "AND group_id NOT IN(" .
join(',', (-1,values(%{Bugzilla->user->groups}))) . ") " . $self->groups_as_string . ") " .
"WHERE group_id IS NULL ORDER BY name"; "WHERE group_id IS NULL ORDER BY name";
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $sth = $dbh->prepare($query); my $sth = $dbh->prepare($query);
...@@ -501,8 +490,8 @@ sub visible_groups_as_string { ...@@ -501,8 +490,8 @@ sub visible_groups_as_string {
return join(', ', @{$self->visible_groups_inherited()}); return join(', ', @{$self->visible_groups_inherited()});
} }
sub derive_groups { sub derive_regexp_groups {
my ($self, $already_locked) = @_; my ($self) = @_;
my $id = $self->id; my $id = $self->id;
return unless $id; return unless $id;
...@@ -511,71 +500,32 @@ sub derive_groups { ...@@ -511,71 +500,32 @@ sub derive_groups {
my $sth; my $sth;
$dbh->bz_lock_tables('profiles WRITE', 'user_group_map WRITE',
'group_group_map READ',
'groups READ') unless $already_locked;
# avoid races, we are only up to date as of the BEGINNING of this process # avoid races, we are only up to date as of the BEGINNING of this process
my $time = $dbh->selectrow_array("SELECT NOW()"); my $time = $dbh->selectrow_array("SELECT NOW()");
# first remove any old derived stuff for this user
$dbh->do(q{DELETE FROM user_group_map
WHERE user_id = ?
AND grant_type != ?},
undef,
$id,
GRANT_DIRECT);
my %groupidsadded = ();
# add derived records for any matching regexps # add derived records for any matching regexps
$sth = $dbh->prepare("SELECT id, userregexp FROM groups WHERE userregexp != ''"); $sth = $dbh->prepare("SELECT id, userregexp, user_group_map.group_id
$sth->execute; FROM groups
LEFT JOIN user_group_map
my $group_insert; ON groups.id = user_group_map.group_id
while (my $row = $sth->fetch) { AND user_group_map.user_id = ?
if ($self->{login} =~ m/$row->[1]/i) { AND user_group_map.grant_type = ?");
$group_insert ||= $dbh->prepare(q{INSERT INTO user_group_map $sth->execute($id, GRANT_REGEXP);
(user_id, group_id, isbless, grant_type)
VALUES (?, ?, 0, ?)}); my $group_insert = $dbh->prepare(q{INSERT INTO user_group_map
$groupidsadded{$row->[0]} = 1; (user_id, group_id, isbless, grant_type)
$group_insert->execute($id, $row->[0], GRANT_REGEXP); VALUES (?, ?, 0, ?)});
} my $group_delete = $dbh->prepare(q{DELETE FROM user_group_map
} WHERE user_id = ?
AND group_id = ?
# Get a list of the groups of which the user is a member. AND isbless = 0
my %groupidschecked = (); AND grant_type = ?});
while (my ($group, $regexp, $present) = $sth->fetchrow_array()) {
my @groupidstocheck = @{$dbh->selectcol_arrayref(q{SELECT group_id if (($regexp ne '') && ($self->{login} =~ m/$regexp/i)) {
FROM user_group_map $group_insert->execute($id, $group, GRANT_REGEXP) unless $present;
WHERE user_id=?}, } else {
undef, $group_delete->execute($id, $group, GRANT_REGEXP) if $present;
$id)};
# Each group needs to be checked for inherited memberships once.
my $group_sth;
while (@groupidstocheck) {
my $group = shift @groupidstocheck;
if (!defined($groupidschecked{"$group"})) {
$groupidschecked{"$group"} = 1;
$group_sth ||= $dbh->prepare(q{SELECT grantor_id
FROM group_group_map
WHERE member_id=?
AND grant_type = } .
GROUP_MEMBERSHIP);
$group_sth->execute($group);
while (my ($groupid) = $group_sth->fetchrow_array) {
if (!defined($groupidschecked{"$groupid"})) {
push(@groupidstocheck,$groupid);
}
if (!$groupidsadded{$groupid}) {
$groupidsadded{$groupid} = 1;
$group_insert ||= $dbh->prepare(q{INSERT INTO user_group_map
(user_id, group_id, isbless, grant_type)
VALUES (?, ?, 0, ?)});
$group_insert->execute($id, $groupid, GRANT_DERIVED);
}
}
} }
} }
...@@ -585,7 +535,6 @@ sub derive_groups { ...@@ -585,7 +535,6 @@ sub derive_groups {
undef, undef,
$time, $time,
$id); $id);
$dbh->bz_unlock_tables() unless $already_locked;
} }
sub product_responsibilities { sub product_responsibilities {
...@@ -690,8 +639,8 @@ sub match { ...@@ -690,8 +639,8 @@ sub match {
$query .= "AND user_group_map.user_id = userid " . $query .= "AND user_group_map.user_id = userid " .
"AND isbless = 0 " . "AND isbless = 0 " .
"AND group_id IN(" . "AND group_id IN(" .
join(', ', (-1, @{$user->visible_groups_inherited})) . ") " . join(', ', (-1, @{$user->visible_groups_inherited})) .
"AND grant_type <> " . GRANT_DERIVED; ")";
} }
$query .= " AND disabledtext = '' " if $exclude_disabled; $query .= " AND disabledtext = '' " if $exclude_disabled;
$query .= "ORDER BY namelength "; $query .= "ORDER BY namelength ";
...@@ -743,8 +692,7 @@ sub match { ...@@ -743,8 +692,7 @@ sub match {
$query .= " AND user_group_map.user_id = userid" . $query .= " AND user_group_map.user_id = userid" .
" AND isbless = 0" . " AND isbless = 0" .
" AND group_id IN(" . " AND group_id IN(" .
join(', ', (-1, @{$user->visible_groups_inherited})) . ")" . join(', ', (-1, @{$user->visible_groups_inherited})) . ")";
" AND grant_type <> " . GRANT_DERIVED;
} }
$query .= " AND disabledtext = ''" if $exclude_disabled; $query .= " AND disabledtext = ''" if $exclude_disabled;
$query .= " ORDER BY namelength"; $query .= " ORDER BY namelength";
...@@ -1151,8 +1099,7 @@ sub get_userlist { ...@@ -1151,8 +1099,7 @@ sub get_userlist {
$query .= "LEFT JOIN user_group_map " . $query .= "LEFT JOIN user_group_map " .
"ON user_group_map.user_id = userid AND isbless = 0 " . "ON user_group_map.user_id = userid AND isbless = 0 " .
"AND group_id IN(" . "AND group_id IN(" .
join(', ', (-1, @{$self->visible_groups_inherited})) . ") " . join(', ', (-1, @{$self->visible_groups_inherited})) . ")";
"AND grant_type <> " . GRANT_DERIVED;
} }
$query .= " WHERE disabledtext = '' "; $query .= " WHERE disabledtext = '' ";
$query .= $dbh->sql_group_by('userid', 'login_name, realname'); $query .= $dbh->sql_group_by('userid', 'login_name, realname');
...@@ -1271,7 +1218,7 @@ sub login_to_id { ...@@ -1271,7 +1218,7 @@ sub login_to_id {
} }
sub UserInGroup { sub UserInGroup {
return defined Bugzilla->user->groups->{$_[0]} ? 1 : 0; return exists Bugzilla->user->groups->{$_[0]} ? 1 : 0;
} }
1; 1;
...@@ -1346,10 +1293,6 @@ C<undef> if no matching user is found. ...@@ -1346,10 +1293,6 @@ C<undef> if no matching user is found.
This routine should not be required in general; most scripts should be using This routine should not be required in general; most scripts should be using
userids instead. userids instead.
This routine and C<new> both take an extra optional argument, which is
passed as the argument to C<derive_groups> to avoid locking. See that
routine's documentation for details.
=end undocumented =end undocumented
=item C<id> =item C<id>
...@@ -1439,12 +1382,19 @@ are the names of the groups, whilst the values are the respective group ids. ...@@ -1439,12 +1382,19 @@ are the names of the groups, whilst the values are the respective group ids.
(This is so that a set of all groupids for groups the user is in can be (This is so that a set of all groupids for groups the user is in can be
obtained by C<values(%{$user-E<gt>groups})>.) obtained by C<values(%{$user-E<gt>groups})>.)
=item C<groups_as_string>
Returns a string containing a comma-seperated list of numeric group ids. If
the user is not a member of any groups, returns "-1". This is most often used
within an SQL IN() function.
=item C<in_group> =item C<in_group>
Determines whether or not a user is in the given group. This method is mainly Determines whether or not a user is in the given group by name.
intended for cases where we are not looking at the currently logged in user,
and only need to make a quick check for the group, where calling C<groups> =item C<in_group_id>
and getting all of the groups would be overkill.
Determines whether or not a user is in the given group by id.
=item C<bless_groups> =item C<bless_groups>
...@@ -1464,7 +1414,7 @@ Returns 1 if the specified user account exists and is visible to the user, ...@@ -1464,7 +1414,7 @@ Returns 1 if the specified user account exists and is visible to the user,
Determines if the user can see the specified bug. Determines if the user can see the specified bug.
=item C<derive_groups> =item C<derive_regexp_groups>
Bugzilla allows for group inheritance. When data about the user (or any of the Bugzilla allows for group inheritance. When data about the user (or any of the
groups) changes, the database must be updated. Handling updated groups is taken groups) changes, the database must be updated. Handling updated groups is taken
...@@ -1508,23 +1458,6 @@ Returns a list of groups that the user is aware of. ...@@ -1508,23 +1458,6 @@ Returns a list of groups that the user is aware of.
Returns the result of C<visible_groups_direct> as a string (a comma-separated Returns the result of C<visible_groups_direct> as a string (a comma-separated
list). list).
=begin undocumented
This routine takes an optional argument. If true, then this routine will not
lock the tables, but will rely on the caller to have done so itsself.
This is required because mysql will only execute a query if all of the tables
are locked, or if none of them are, not a mixture. If the caller has already
done some locking, then this routine would fail. Thus the caller needs to lock
all the tables required by this method, and then C<derive_groups> won't do
any locking.
This is a really ugly solution, and when Bugzilla supports transactions
instead of using the explicit table locking we were forced to do when thats
all MySQL supported, this will go away.
=end undocumented
=item C<product_responsibilities> =item C<product_responsibilities>
Retrieve user's product responsibilities as a list of hashes. Retrieve user's product responsibilities as a list of hashes.
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
# Erik Stambaugh <erik@dasbistro.com> # Erik Stambaugh <erik@dasbistro.com>
# Dave Lawrence <dkl@redhat.com> # Dave Lawrence <dkl@redhat.com>
# Max Kanat-Alexander <mkanat@bugzilla.org> # Max Kanat-Alexander <mkanat@bugzilla.org>
# Joel Peshkin <bugreport@peshkin.net>
# #
# #
# #
...@@ -3557,11 +3558,8 @@ AddFDef("owner_idle_time", "Time Since Assignee Touched", 0); ...@@ -3557,11 +3558,8 @@ AddFDef("owner_idle_time", "Time Since Assignee Touched", 0);
if ($dbh->bz_column_info("user_group_map", "isderived")) { if ($dbh->bz_column_info("user_group_map", "isderived")) {
$dbh->bz_add_column('user_group_map', 'grant_type', $dbh->bz_add_column('user_group_map', 'grant_type',
{TYPE => 'INT1', NOTNULL => 1, DEFAULT => '0'}); {TYPE => 'INT1', NOTNULL => 1, DEFAULT => '0'});
$dbh->do("UPDATE user_group_map SET grant_type = " . $dbh->do("DELETE FROM user_group_map WHERE isderived != 0");
"IF(isderived, " . GRANT_DERIVED . ", " . $dbh->do("UPDATE user_group_map SET grant_type = " . GRANT_DIRECT);
GRANT_DIRECT . ")");
$dbh->do("DELETE FROM user_group_map
WHERE isbless = 0 AND grant_type != " . GRANT_DIRECT);
$dbh->bz_drop_column("user_group_map", "isderived"); $dbh->bz_drop_column("user_group_map", "isderived");
$dbh->bz_drop_index('user_group_map', 'user_group_map_user_id_idx'); $dbh->bz_drop_index('user_group_map', 'user_group_map_user_id_idx');
...@@ -3569,21 +3567,6 @@ if ($dbh->bz_column_info("user_group_map", "isderived")) { ...@@ -3569,21 +3567,6 @@ if ($dbh->bz_column_info("user_group_map", "isderived")) {
{TYPE => 'UNIQUE', {TYPE => 'UNIQUE',
FIELDS => [qw(user_id group_id grant_type isbless)]}); FIELDS => [qw(user_id group_id grant_type isbless)]});
# Evaluate regexp-based group memberships
my $sth = $dbh->prepare("SELECT profiles.userid, profiles.login_name,
groups.id, groups.userregexp
FROM profiles, groups
WHERE userregexp != ''");
$sth->execute();
my $sth2 = $dbh->prepare("INSERT IGNORE INTO user_group_map
(user_id, group_id, isbless, grant_type)
VALUES(?, ?, 0, " . GRANT_REGEXP . ")");
while (my ($uid, $login, $gid, $rexp) = $sth->fetchrow_array()) {
if ($login =~ m/$rexp/i) {
$sth2->execute($uid, $gid);
}
}
# Make sure groups get rederived # Make sure groups get rederived
$dbh->do("UPDATE groups SET last_changed = NOW() WHERE name = 'admin'"); $dbh->do("UPDATE groups SET last_changed = NOW() WHERE name = 'admin'");
} }
...@@ -4090,6 +4073,42 @@ if (!GroupDoesExist('bz_canusewhines')) { ...@@ -4090,6 +4073,42 @@ if (!GroupDoesExist('bz_canusewhines')) {
GROUP_MEMBERSHIP . ")") unless $group_exists; GROUP_MEMBERSHIP . ")") unless $group_exists;
} }
# 2005-08-14 bugreport@peshkin.net -- Bug 304583
use constant GRANT_DERIVED => 1;
# Get rid of leftover DERIVED group permissions
$dbh->do("DELETE FROM user_group_map WHERE grant_type = " . GRANT_DERIVED);
# Evaluate regexp-based group memberships
$sth = $dbh->prepare("SELECT profiles.userid, profiles.login_name,
groups.id, groups.userregexp,
user_group_map.group_id
FROM profiles
CROSS JOIN groups
LEFT JOIN user_group_map
ON user_group_map.user_id = profiles.userid
AND user_group_map.group_id = groups.id
AND user_group_map.grant_type = ?
WHERE (userregexp != ''
OR user_group_map.group_id IS NOT NULL)");
my $sth_add = $dbh->prepare("INSERT INTO user_group_map
(user_id, group_id, isbless, grant_type)
VALUES(?, ?, 0, " . GRANT_REGEXP . ")");
my $sth_del = $dbh->prepare("DELETE FROM user_group_map
WHERE user_id = ?
AND group_id = ?
AND isbless = 0
AND grant_type = " . GRANT_REGEXP);
$sth->execute(GRANT_REGEXP);
while (my ($uid, $login, $gid, $rexp, $present) = $sth->fetchrow_array()) {
if ($login =~ m/$rexp/i) {
$sth_add->execute($uid, $gid) unless $present;
} else {
$sth_del->execute($uid, $gid) if $present;
}
}
########################################################################### ###########################################################################
# Create --SETTINGS-- users can adjust # Create --SETTINGS-- users can adjust
########################################################################### ###########################################################################
......
...@@ -56,20 +56,21 @@ sub RederiveRegexp ...@@ -56,20 +56,21 @@ sub RederiveRegexp
my $regexp = shift; my $regexp = shift;
my $gid = shift; my $gid = shift;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $sth = $dbh->prepare("SELECT userid, login_name FROM profiles"); my $sth = $dbh->prepare("SELECT userid, login_name, group_id
my $sthqry = $dbh->prepare("SELECT 1 FROM user_group_map FROM profiles
WHERE user_id = ? AND group_id = ? LEFT JOIN user_group_map
AND grant_type = ? and isbless = 0"); ON user_group_map.user_id = profiles.userid
AND group_id = ?
AND grant_type = ?
AND isbless = 0");
my $sthadd = $dbh->prepare("INSERT INTO user_group_map my $sthadd = $dbh->prepare("INSERT INTO user_group_map
(user_id, group_id, grant_type, isbless) (user_id, group_id, grant_type, isbless)
VALUES (?, ?, ?, 0)"); VALUES (?, ?, ?, 0)");
my $sthdel = $dbh->prepare("DELETE FROM user_group_map my $sthdel = $dbh->prepare("DELETE FROM user_group_map
WHERE user_id = ? AND group_id = ? WHERE user_id = ? AND group_id = ?
AND grant_type = ? and isbless = 0"); AND grant_type = ? and isbless = 0");
$sth->execute(); $sth->execute($gid, GRANT_REGEXP);
while (my ($uid, $login) = $sth->fetchrow_array()) { while (my ($uid, $login, $present) = $sth->fetchrow_array()) {
my $present = $dbh->selectrow_array($sthqry, undef,
$uid, $gid, GRANT_REGEXP);
if (($regexp =~ /\S+/) && ($login =~ m/$regexp/i)) if (($regexp =~ /\S+/) && ($login =~ m/$regexp/i))
{ {
$sthadd->execute($uid, $gid, GRANT_REGEXP) unless $present; $sthadd->execute($uid, $gid, GRANT_REGEXP) unless $present;
......
...@@ -185,6 +185,8 @@ if ($action eq 'search') { ...@@ -185,6 +185,8 @@ if ($action eq 'search') {
insert_new_user($login, $realname, $password, $disabledtext); insert_new_user($login, $realname, $password, $disabledtext);
$otherUserID = $dbh->bz_last_key('profiles', 'userid'); $otherUserID = $dbh->bz_last_key('profiles', 'userid');
$dbh->bz_unlock_tables(); $dbh->bz_unlock_tables();
my $newprofile = new Bugzilla::User($otherUserID);
$newprofile->derive_regexp_groups();
userDataToVars($otherUserID); userDataToVars($otherUserID);
$vars->{'message'} = 'account_created'; $vars->{'message'} = 'account_created';
...@@ -290,6 +292,12 @@ if ($action eq 'search') { ...@@ -290,6 +292,12 @@ if ($action eq 'search') {
'WHERE userid = ?', 'WHERE userid = ?',
undef, @values); undef, @values);
# XXX: should create profiles_activity entries. # XXX: should create profiles_activity entries.
#
# We create a new user object here because it needs to
# read information that may have changed since this
# script started.
my $newprofile = new Bugzilla::User($otherUserID);
$newprofile->derive_regexp_groups();
} }
} }
...@@ -639,7 +647,7 @@ sub userDataToVars { ...@@ -639,7 +647,7 @@ sub userDataToVars {
my $query; my $query;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
$otheruser->derive_groups(); my $grouplist = $otheruser->groups_as_string;
$vars->{'otheruser'} = $otheruser; $vars->{'otheruser'} = $otheruser;
$vars->{'groups'} = $user->bless_groups(); $vars->{'groups'} = $user->bless_groups();
...@@ -648,7 +656,7 @@ sub userDataToVars { ...@@ -648,7 +656,7 @@ sub userDataToVars {
qq{SELECT id, qq{SELECT id,
COUNT(directmember.group_id) AS directmember, COUNT(directmember.group_id) AS directmember,
COUNT(regexpmember.group_id) AS regexpmember, COUNT(regexpmember.group_id) AS regexpmember,
COUNT(derivedmember.group_id) AS derivedmember, CASE WHEN groups.id IN ($grouplist) THEN 1 ELSE 0 END,
COUNT(directbless.group_id) AS directbless COUNT(directbless.group_id) AS directbless
FROM groups FROM groups
LEFT JOIN user_group_map AS directmember LEFT JOIN user_group_map AS directmember
...@@ -661,11 +669,6 @@ sub userDataToVars { ...@@ -661,11 +669,6 @@ sub userDataToVars {
AND regexpmember.user_id = ? AND regexpmember.user_id = ?
AND regexpmember.isbless = 0 AND regexpmember.isbless = 0
AND regexpmember.grant_type = ? AND regexpmember.grant_type = ?
LEFT JOIN user_group_map AS derivedmember
ON derivedmember.group_id = id
AND derivedmember.user_id = ?
AND derivedmember.isbless = 0
AND derivedmember.grant_type = ?
LEFT JOIN user_group_map AS directbless LEFT JOIN user_group_map AS directbless
ON directbless.group_id = id ON directbless.group_id = id
AND directbless.user_id = ? AND directbless.user_id = ?
...@@ -675,20 +678,17 @@ sub userDataToVars { ...@@ -675,20 +678,17 @@ sub userDataToVars {
'id', undef, 'id', undef,
($otheruserid, GRANT_DIRECT, ($otheruserid, GRANT_DIRECT,
$otheruserid, GRANT_REGEXP, $otheruserid, GRANT_REGEXP,
$otheruserid, GRANT_DERIVED,
$otheruserid, GRANT_DIRECT)); $otheruserid, GRANT_DIRECT));
# Find indirect bless permission. # Find indirect bless permission.
$query = qq{SELECT groups.id $query = qq{SELECT groups.id
FROM groups, user_group_map AS ugm, group_group_map AS ggm FROM groups, group_group_map AS ggm
WHERE ugm.user_id = ? WHERE groups.id = ggm.grantor_id
AND groups.id = ggm.grantor_id AND ggm.member_id IN ($grouplist)
AND ggm.member_id = ugm.group_id
AND ugm.isbless = 0
AND ggm.grant_type = ? AND ggm.grant_type = ?
} . $dbh->sql_group_by('id'); } . $dbh->sql_group_by('id');
foreach (@{$dbh->selectall_arrayref($query, undef, foreach (@{$dbh->selectall_arrayref($query, undef,
($otheruserid, GROUP_BLESS))}) { (GROUP_BLESS))}) {
# Merge indirect bless permissions into permission variable. # Merge indirect bless permissions into permission variable.
$vars->{'permissions'}{${$_}[0]}{'indirectbless'} = 1; $vars->{'permissions'}{${$_}[0]}{'indirectbless'} = 1;
} }
......
...@@ -335,11 +335,7 @@ foreach my $b (grep(/^bit-\d*$/, $cgi->param())) { ...@@ -335,11 +335,7 @@ foreach my $b (grep(/^bit-\d*$/, $cgi->param())) {
$vars->{'bit'} = $v; $vars->{'bit'} = $v;
ThrowCodeError("inactive_group"); ThrowCodeError("inactive_group");
} }
SendSQL("SELECT user_id FROM user_group_map my ($permit) = $user->in_group_id($v);
WHERE user_id = $::userid
AND group_id = $v
AND isbless = 0");
my ($permit) = FetchSQLData();
if (!$permit) { if (!$permit) {
SendSQL("SELECT othercontrol FROM group_control_map SendSQL("SELECT othercontrol FROM group_control_map
WHERE group_id = $v AND product_id = $product_id"); WHERE group_id = $v AND product_id = $product_id");
......
...@@ -73,6 +73,7 @@ use vars qw(@legal_product ...@@ -73,6 +73,7 @@ use vars qw(@legal_product
my $user = Bugzilla->login(LOGIN_REQUIRED); my $user = Bugzilla->login(LOGIN_REQUIRED);
my $whoid = $user->id; my $whoid = $user->id;
my $grouplist = $user->groups_as_string;
my $cgi = Bugzilla->cgi; my $cgi = Bugzilla->cgi;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
...@@ -704,10 +705,9 @@ sub ChangeResolution { ...@@ -704,10 +705,9 @@ sub ChangeResolution {
my @groupAdd = (); my @groupAdd = ();
my @groupDel = (); my @groupDel = ();
SendSQL("SELECT groups.id, isactive FROM groups INNER JOIN user_group_map " . SendSQL("SELECT groups.id, isactive FROM groups " .
"ON groups.id = user_group_map.group_id " . "WHERE id IN($grouplist) " .
"WHERE user_group_map.user_id = $whoid " . "AND isbuggroup = 1");
"AND isbless = 0 AND isbuggroup = 1");
while (my ($b, $isactive) = FetchSQLData()) { while (my ($b, $isactive) = FetchSQLData()) {
# The multiple change page may not show all groups a bug is in # The multiple change page may not show all groups a bug is in
# (eg product groups when listing more than one product) # (eg product groups when listing more than one product)
...@@ -1560,7 +1560,7 @@ foreach my $id (@idlist) { ...@@ -1560,7 +1560,7 @@ foreach my $id (@idlist) {
# - Is the bug in this group? # - Is the bug in this group?
SendSQL("SELECT DISTINCT groups.id, isactive, " . SendSQL("SELECT DISTINCT groups.id, isactive, " .
"oldcontrolmap.membercontrol, newcontrolmap.membercontrol, " . "oldcontrolmap.membercontrol, newcontrolmap.membercontrol, " .
"user_group_map.user_id IS NOT NULL, " . "CASE WHEN groups_id IN ($grouplist) THEN 1 ELSE 0 END, " .
"bug_group_map.group_id IS NOT NULL " . "bug_group_map.group_id IS NOT NULL " .
"FROM groups " . "FROM groups " .
"LEFT JOIN group_control_map AS oldcontrolmap " . "LEFT JOIN group_control_map AS oldcontrolmap " .
...@@ -1569,10 +1569,6 @@ foreach my $id (@idlist) { ...@@ -1569,10 +1569,6 @@ foreach my $id (@idlist) {
" LEFT JOIN group_control_map AS newcontrolmap " . " LEFT JOIN group_control_map AS newcontrolmap " .
"ON newcontrolmap.group_id = groups.id " . "ON newcontrolmap.group_id = groups.id " .
"AND newcontrolmap.product_id = $newproduct_id " . "AND newcontrolmap.product_id = $newproduct_id " .
"LEFT JOIN user_group_map " .
"ON user_group_map.group_id = groups.id " .
"AND user_group_map.user_id = $whoid " .
"AND user_group_map.isbless = 0 " .
"LEFT JOIN bug_group_map " . "LEFT JOIN bug_group_map " .
"ON bug_group_map.group_id = groups.id " . "ON bug_group_map.group_id = groups.id " .
"AND bug_group_map.bug_id = $id " "AND bug_group_map.bug_id = $id "
......
...@@ -118,66 +118,6 @@ if (defined $cgi->param('rebuildvotecache')) { ...@@ -118,66 +118,6 @@ if (defined $cgi->param('rebuildvotecache')) {
} }
########################################################################### ###########################################################################
# Fix group derivations
###########################################################################
if (defined $cgi->param('rederivegroups')) {
Status("OK, All users' inherited permissions will be rechecked when " .
"they next access Bugzilla.");
SendSQL("UPDATE groups SET last_changed = NOW() " . $dbh->sql_limit(1));
}
# rederivegroupsnow is REALLY only for testing.
# If it wasn't, then we'd do this the faster way as a per-group
# thing rather than per-user for group inheritance
if (defined $cgi->param('rederivegroupsnow')) {
require Bugzilla::User;
Status("OK, now rederiving groups.");
SendSQL("SELECT userid FROM profiles");
while ((my $id) = FetchSQLData()) {
my $user = new Bugzilla::User($id);
$user->derive_groups();
Status("User $id");
}
}
if (defined $cgi->param('cleangroupsnow')) {
Status("OK, now cleaning stale groups.");
# Only users that were out of date already long ago should be cleaned
# and the cleaning is done with tables locked. This is require in order
# to keep another session from proceeding with permission checks
# after the groups have been cleaned unless it first had an opportunity
# to get the groups up to date.
# If any page starts taking longer than one hour to load, this interval
# should be revised.
SendSQL("SELECT MAX(last_changed) FROM groups WHERE last_changed < NOW() - " .
$dbh->sql_interval('1 HOUR'));
(my $cutoff) = FetchSQLData();
Status("Cutoff is $cutoff");
SendSQL("SELECT COUNT(*) FROM user_group_map");
(my $before) = FetchSQLData();
$dbh->bz_lock_tables('user_group_map WRITE', 'profiles WRITE');
SendSQL("SELECT userid FROM profiles " .
"WHERE refreshed_when > 0 " .
"AND refreshed_when < " . SqlQuote($cutoff) . " " .
$dbh->sql_limit(1000));
my $count = 0;
while ((my $id) = FetchSQLData()) {
$count++;
PushGlobalSQLState();
SendSQL("DELETE FROM user_group_map WHERE " .
"user_id = $id AND isderived = 1 AND isbless = 0");
SendSQL("UPDATE profiles SET refreshed_when = 0 WHERE userid = $id");
PopGlobalSQLState();
}
$dbh->bz_unlock_tables();
SendSQL("SELECT COUNT(*) FROM user_group_map");
(my $after) = FetchSQLData();
Status("Cleaned table for $count users " .
"- reduced from $before records to $after records");
}
###########################################################################
# Create missing group_control_map entries # Create missing group_control_map entries
########################################################################### ###########################################################################
......
...@@ -268,7 +268,7 @@ sub changeEmail { ...@@ -268,7 +268,7 @@ sub changeEmail {
# The email address has been changed, so we need to rederive the groups # The email address has been changed, so we need to rederive the groups
my $user = new Bugzilla::User($userid); my $user = new Bugzilla::User($userid);
$user->derive_groups; $user->derive_regexp_groups;
# Return HTTP response headers. # Return HTTP response headers.
print $cgi->header(); print $cgi->header();
...@@ -313,7 +313,7 @@ sub cancelChangeEmail { ...@@ -313,7 +313,7 @@ sub cancelChangeEmail {
# issue # issue
my $user = new Bugzilla::User($userid); my $user = new Bugzilla::User($userid);
$user->derive_groups; $user->derive_regexp_groups;
$vars->{'message'} = "email_change_cancelled_reinstated"; $vars->{'message'} = "email_change_cancelled_reinstated";
} }
......
...@@ -311,11 +311,9 @@ sub DoPermissions { ...@@ -311,11 +311,9 @@ sub DoPermissions {
my (@has_bits, @set_bits); my (@has_bits, @set_bits);
SendSQL("SELECT DISTINCT name, description FROM groups " . SendSQL("SELECT DISTINCT name, description FROM groups " .
"INNER JOIN user_group_map " . "WHERE id IN (" .
"ON user_group_map.group_id = groups.id " . Bugzilla->user->groups_as_string .
"WHERE user_id = $::userid " . ") ORDER BY name");
"AND isbless = 0 " .
"ORDER BY name");
while (MoreSQLData()) { while (MoreSQLData()) {
my ($nam, $desc) = FetchSQLData(); my ($nam, $desc) = FetchSQLData();
push(@has_bits, {"desc" => $desc, "name" => $nam}); push(@has_bits, {"desc" => $desc, "name" => $nam});
......
...@@ -240,8 +240,7 @@ sub get_next_event { ...@@ -240,8 +240,7 @@ sub get_next_event {
return undef unless $fetched; return undef unless $fetched;
my ($eventid, $owner_id, $subject, $body) = @{$fetched}; my ($eventid, $owner_id, $subject, $body) = @{$fetched};
my $owner = Bugzilla::User->new($owner_id, my $owner = Bugzilla::User->new($owner_id);
DERIVE_GROUPS_TABLES_ALREADY_LOCKED);
my $whineatothers = $owner->in_group('bz_canusewhineatothers'); my $whineatothers = $owner->in_group('bz_canusewhineatothers');
...@@ -275,10 +274,13 @@ sub get_next_event { ...@@ -275,10 +274,13 @@ sub get_next_event {
my $group_id = Bugzilla::Group::ValidateGroupName( my $group_id = Bugzilla::Group::ValidateGroupName(
$groupname, $owner); $groupname, $owner);
if ($group_id) { if ($group_id) {
my $glist = join(',',
Bugzilla::User->flatten_group_membership(
$group_id));
$sth = $dbh->prepare("SELECT user_id FROM " . $sth = $dbh->prepare("SELECT user_id FROM " .
"user_group_map " . "user_group_map " .
"WHERE group_id=?"); "WHERE group_id IN ($glist)");
$sth->execute($group_id); $sth->execute();
for my $row (@{$sth->fetchall_arrayref}) { for my $row (@{$sth->fetchall_arrayref}) {
if (not defined $user_objects{$row->[0]}) { if (not defined $user_objects{$row->[0]}) {
$user_objects{$row->[0]} = $user_objects{$row->[0]} =
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment