Evict initial members of group created by SSH command from AccountCache

When a user is added to a group, the new group membership gets not
active as long as the old user information stays in the AccountCache.
This is why the user needs to be evicted from the AccountCache when
he is added to a group. Then the user information together with the
new group membership is reloaded from the database.

If a new group membership is assigned in the WebUI the user is
properly evicted from the AccountCache and the new group membership
is immediately active.

If a group gets created by an SSH command and initial members get
added to the new group, the initial members are not evicted from the
AccountCache. This is why the new group membership does not get
active immediatley if the user information is already in the
AccountCache.

This change now ensures that the initial members of a group created
by an SSH command get evicted from the AccountCache. The fix is done
in such a way that there is now only one implementation of the group
creation functionality which is used from both WebUI and SSH command.

Change-Id: I605277d564d3e2d45d6366afbf3af48cc04458d5
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
Bug: issue 814
This commit is contained in:
Edwin Kempin 2011-03-16 09:01:03 +01:00
parent 0ff5ff0112
commit 85c8acbc51
5 changed files with 146 additions and 94 deletions

View File

@ -18,72 +18,34 @@ import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.AccountGroupMember;
import com.google.gerrit.reviewdb.AccountGroupMemberAudit;
import com.google.gerrit.reviewdb.AccountGroupName;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gwtorm.client.OrmDuplicateKeyException;
import com.google.gerrit.server.account.PerformCreateGroup;
import com.google.gerrit.server.account.PerformCreateGroup;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.Collections;
class CreateGroup extends Handler<AccountGroup.Id> {
interface Factory {
CreateGroup create(String newName);
CreateGroup create(String groupName);
}
private final ReviewDb db;
private final PerformCreateGroup.Factory performCreateGroupFactory;
private final IdentifiedUser user;
private final AccountCache accountCache;
private final String name;
private final String groupName;
@Inject
CreateGroup(final ReviewDb db, final IdentifiedUser user,
final AccountCache accountCache,
@Assisted final String newName) {
this.db = db;
CreateGroup(final PerformCreateGroup.Factory performCreateGroupFactory,
final IdentifiedUser user, @Assisted final String groupName) {
this.performCreateGroupFactory = performCreateGroupFactory;
this.user = user;
this.accountCache = accountCache;
this.name = newName;
this.groupName = groupName;
}
@Override
public AccountGroup.Id call() throws OrmException, NameAlreadyUsedException {
final AccountGroup.NameKey key = new AccountGroup.NameKey(name);
if (db.accountGroupNames().get(key) != null) {
throw new NameAlreadyUsedException();
}
final AccountGroup.Id id = new AccountGroup.Id(db.nextAccountGroupId());
final PerformCreateGroup performCreateGroup = performCreateGroupFactory.create();
final Account.Id me = user.getAccountId();
final AccountGroup group = new AccountGroup(key, id);
db.accountGroups().insert(Collections.singleton(group));
try {
final AccountGroupName n = new AccountGroupName(group);
db.accountGroupNames().insert(Collections.singleton(n));
} catch (OrmDuplicateKeyException dupeErr) {
db.accountGroups().delete(Collections.singleton(group));
throw new NameAlreadyUsedException();
}
AccountGroupMember member = new AccountGroupMember(//
new AccountGroupMember.Key(me, id));
db.accountGroupMembersAudit().insert(
Collections.singleton(new AccountGroupMemberAudit(member, me)));
db.accountGroupMembers().insert(Collections.singleton(member));
accountCache.evict(me);
return id;
return performCreateGroup.createGroup(groupName, null, null, me);
}
}

View File

@ -0,0 +1,118 @@
// Copyright (C) 2011 The Android Open Source Project
//
// 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
package com.google.gerrit.server.account;
import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.AccountGroupMember;
import com.google.gerrit.reviewdb.AccountGroupMemberAudit;
import com.google.gerrit.reviewdb.AccountGroupName;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gwtorm.client.OrmDuplicateKeyException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class PerformCreateGroup {
public interface Factory {
PerformCreateGroup create();
}
private final ReviewDb db;
private final AccountCache accountCache;
private final IdentifiedUser currentUser;
@Inject
PerformCreateGroup(final ReviewDb db, final AccountCache accountCache,
final IdentifiedUser currentUser) {
this.db = db;
this.accountCache = accountCache;
this.currentUser = currentUser;
}
/**
* Creates a new group.
*
* @param groupName the name for the new group
* @param groupDescription the description of the new group, <code>null</code>
* if no description
* @param ownerGroupId the group that should own the new group, if
* <code>null</code> the new group will own itself
* @param initialMembers initial members to be added to the new group
* @return the id of the new group
* @throws OrmException is thrown in case of any data store read or write
* error
* @throws NameAlreadyUsedException is thrown in case a group with the given
* name already exists
*/
public AccountGroup.Id createGroup(final String groupName,
final String groupDescription, final AccountGroup.Id ownerGroupId,
final Account.Id... initialMembers) throws OrmException,
NameAlreadyUsedException {
final AccountGroup.Id groupId =
new AccountGroup.Id(db.nextAccountGroupId());
final AccountGroup.NameKey nameKey = new AccountGroup.NameKey(groupName);
final AccountGroup group = new AccountGroup(nameKey, groupId);
if (ownerGroupId != null) {
group.setOwnerGroupId(ownerGroupId);
}
if (groupDescription != null) {
group.setDescription(groupDescription);
}
final AccountGroupName gn = new AccountGroupName(group);
// first insert the group name to validate that the group name hasn't
// already been used to create another group
try {
db.accountGroupNames().insert(Collections.singleton(gn));
} catch (OrmDuplicateKeyException e) {
throw new NameAlreadyUsedException();
}
db.accountGroups().insert(Collections.singleton(group));
addMembers(groupId, initialMembers);
return groupId;
}
private void addMembers(final AccountGroup.Id groupId,
final Account.Id... members) throws OrmException {
final List<AccountGroupMember> memberships =
new ArrayList<AccountGroupMember>();
final List<AccountGroupMemberAudit> membershipsAudit =
new ArrayList<AccountGroupMemberAudit>();
for (Account.Id accountId : members) {
final AccountGroupMember membership =
new AccountGroupMember(new AccountGroupMember.Key(accountId, groupId));
memberships.add(membership);
final AccountGroupMemberAudit audit =
new AccountGroupMemberAudit(membership, currentUser.getAccountId());
membershipsAudit.add(audit);
}
db.accountGroupMembers().insert(memberships);
db.accountGroupMembersAudit().insert(membershipsAudit);
for (Account.Id accountId : members) {
accountCache.evict(accountId);
}
}
}

View File

@ -21,6 +21,7 @@ import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.RequestCleanup;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.account.PerformCreateGroup;
import com.google.gerrit.server.git.MergeOp;
import com.google.gerrit.server.git.ReceiveCommits;
import com.google.gerrit.server.mail.AbandonedSender;
@ -69,5 +70,6 @@ public class GerritRequestModule extends FactoryModule {
factory(MergedSender.Factory.class);
factory(MergeFailSender.Factory.class);
factory(RegisterNewEmailSender.Factory.class);
factory(PerformCreateGroup.Factory.class);
}
}

View File

@ -347,6 +347,10 @@ public abstract class BaseCommand implements Command {
return new UnloggedFailure(1, "fatal: " + msg);
}
protected UnloggedFailure die(Throwable why) {
return new UnloggedFailure(1, "fatal: " + why.getMessage(), why);
}
private final class TaskThunk implements CancelableRunnable, ProjectRunnable {
private final CommandRunnable thunk;
private final Context context;

View File

@ -14,16 +14,13 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.AccountGroupMember;
import com.google.gerrit.reviewdb.AccountGroupMemberAudit;
import com.google.gerrit.reviewdb.AccountGroupName;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.PerformCreateGroup;
import com.google.gerrit.server.account.PerformCreateGroup;
import com.google.gerrit.sshd.AdminCommand;
import com.google.gerrit.sshd.BaseCommand;
import com.google.gwtorm.client.OrmDuplicateKeyException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
@ -32,10 +29,7 @@ import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
@ -54,12 +48,6 @@ public class AdminCreateGroup extends BaseCommand {
@Argument(index = 0, required = true, metaVar = "GROUP", usage = "name of group to be created")
private String groupName;
@Inject
private IdentifiedUser currentUser;
@Inject
private ReviewDb db;
private final Set<Account.Id> initialMembers = new HashSet<Account.Id>();
@Option(name = "--member", aliases = {"-m"}, metaVar = "USERNAME", usage = "initial set of users to become members of the group")
@ -67,6 +55,9 @@ public class AdminCreateGroup extends BaseCommand {
initialMembers.add(id);
}
@Inject
private PerformCreateGroup.Factory performCreateGroupFactory;
@Override
public void start(Environment env) throws IOException {
startThread(new CommandRunnable() {
@ -79,37 +70,12 @@ public class AdminCreateGroup extends BaseCommand {
}
private void createGroup() throws OrmException, UnloggedFailure {
AccountGroup.Id groupId = new AccountGroup.Id(db.nextAccountGroupId());
AccountGroup.NameKey nameKey = new AccountGroup.NameKey(groupName);
AccountGroup group = new AccountGroup(nameKey, groupId);
if (ownerGroupId != null) {
group.setOwnerGroupId(ownerGroupId);
}
if (groupDescription != null) {
group.setDescription(groupDescription);
}
AccountGroupName gn = new AccountGroupName(group);
// first insert the group name to validate that the group name hasn't already been
// used to create another group
final PerformCreateGroup performCreateGroup =
performCreateGroupFactory.create();
try {
db.accountGroupNames().insert(Collections.singleton(gn));
} catch (OrmDuplicateKeyException e) {
throw die("group '" + groupName + "' already exists");
performCreateGroup.createGroup(groupName, groupDescription, ownerGroupId, initialMembers.toArray(new Account.Id[initialMembers.size()]));
} catch (NameAlreadyUsedException e) {
throw die(e);
}
db.accountGroups().insert(Collections.singleton(group));
List<AccountGroupMember> memberships = new ArrayList<AccountGroupMember>();
List<AccountGroupMemberAudit> membershipsAudit = new ArrayList<AccountGroupMemberAudit>();
for (Account.Id accountId : initialMembers) {
AccountGroupMember membership =
new AccountGroupMember(new AccountGroupMember.Key(accountId, groupId));
memberships.add(membership);
AccountGroupMemberAudit audit =
new AccountGroupMemberAudit(membership, currentUser.getAccountId());
membershipsAudit.add(audit);
}
db.accountGroupMembers().insert(memberships);
db.accountGroupMembersAudit().insert(membershipsAudit);
}
}