From b6f615a001cceb51fd42f2fa9de9dfd86fc7196a Mon Sep 17 00:00:00 2001 From: Robin Shen Date: Wed, 23 Nov 2022 15:40:39 +0800 Subject: [PATCH] Fix issue #992 - Unable to access OneDev UI if user belongs to login group --- .../impl/DefaultGroupManager.java | 82 +++++++++++++++++-- .../java/io/onedev/server/model/Group.java | 8 +- .../administration/SecuritySetting.java | 18 ++-- .../onedev/server/util/facade/GroupCache.java | 33 ++++++++ 4 files changed, 119 insertions(+), 22 deletions(-) create mode 100644 server-core/src/main/java/io/onedev/server/util/facade/GroupCache.java diff --git a/server-core/src/main/java/io/onedev/server/entitymanager/impl/DefaultGroupManager.java b/server-core/src/main/java/io/onedev/server/entitymanager/impl/DefaultGroupManager.java index 0a465b1f2b..4e4d545550 100644 --- a/server-core/src/main/java/io/onedev/server/entitymanager/impl/DefaultGroupManager.java +++ b/server-core/src/main/java/io/onedev/server/entitymanager/impl/DefaultGroupManager.java @@ -10,19 +10,29 @@ import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; +import com.hazelcast.core.HazelcastInstance; + +import io.onedev.server.cluster.ClusterManager; import io.onedev.server.entitymanager.GroupManager; import io.onedev.server.entitymanager.IssueFieldManager; import io.onedev.server.entitymanager.ProjectManager; import io.onedev.server.entitymanager.SettingManager; +import io.onedev.server.event.Listen; +import io.onedev.server.event.entity.EntityPersisted; +import io.onedev.server.event.entity.EntityRemoved; +import io.onedev.server.event.system.SystemStarted; import io.onedev.server.model.Group; import io.onedev.server.model.Project; import io.onedev.server.model.support.BranchProtection; import io.onedev.server.model.support.TagProtection; +import io.onedev.server.persistence.TransactionManager; import io.onedev.server.persistence.annotation.Sessional; import io.onedev.server.persistence.annotation.Transactional; import io.onedev.server.persistence.dao.BaseEntityManager; import io.onedev.server.persistence.dao.Dao; import io.onedev.server.persistence.dao.EntityCriteria; +import io.onedev.server.util.facade.GroupCache; +import io.onedev.server.util.facade.GroupFacade; import io.onedev.server.util.usage.Usage; @Singleton @@ -34,15 +44,70 @@ public class DefaultGroupManager extends BaseEntityManager implements Gro private final IssueFieldManager issueFieldManager; + private final ClusterManager clusterManager; + + private final TransactionManager transactionManager; + + private volatile GroupCache cache; + @Inject public DefaultGroupManager(Dao dao, ProjectManager projectManager, SettingManager settingManager, - IssueFieldManager issueFieldManager) { + IssueFieldManager issueFieldManager, ClusterManager clusterManager, + TransactionManager transactionManager) { super(dao); this.projectManager = projectManager; this.settingManager = settingManager; this.issueFieldManager = issueFieldManager; + this.clusterManager = clusterManager; + this.transactionManager = transactionManager; } + @Sessional + @Listen + public void on(SystemStarted event) { + HazelcastInstance hazelcastInstance = clusterManager.getHazelcastInstance(); + cache = new GroupCache(hazelcastInstance.getReplicatedMap("groupCache")); + + for (Group group: query()) + cache.put(group.getId(), group.getFacade()); + } + + @Transactional + @Listen + public void on(EntityRemoved event) { + if (event.getEntity() instanceof Group) { + Long id = event.getEntity().getId(); + transactionManager.runAfterCommit(new Runnable() { + + @Override + public void run() { + cache.remove(id); + } + + }); + } + } + + @Transactional + @Listen + public void on(EntityPersisted event) { + if (event.getEntity() instanceof Group) + cacheAfterCommit((Group) event.getEntity()); + } + + private void cacheAfterCommit(Group group) { + GroupFacade facade = group.getFacade(); + transactionManager.runAfterCommit(new Runnable() { + + @Override + public void run() { + if (cache != null) + cache.put(facade.getId(), facade); + } + + }); + } + @Transactional @Override public void save(Group group, String oldName) { @@ -82,13 +147,14 @@ public class DefaultGroupManager extends BaseEntityManager implements Gro } @Sessional - @Override - public Group find(String name) { - EntityCriteria criteria = newCriteria(); - criteria.add(Restrictions.ilike("name", name)); - criteria.setCacheable(true); - return find(criteria); - } + @Override + public Group find(String groupName) { + GroupFacade facade = cache.find(groupName); + if (facade != null) + return load(facade.getId()); + else + return null; + } @Override public List query() { diff --git a/server-core/src/main/java/io/onedev/server/model/Group.java b/server-core/src/main/java/io/onedev/server/model/Group.java index 5659b1708f..5ef8249a66 100644 --- a/server-core/src/main/java/io/onedev/server/model/Group.java +++ b/server-core/src/main/java/io/onedev/server/model/Group.java @@ -8,15 +8,16 @@ import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.OneToMany; +import javax.validation.constraints.NotEmpty; import org.apache.shiro.authz.Permission; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; -import javax.validation.constraints.NotEmpty; import io.onedev.server.security.permission.CreateRootProjects; import io.onedev.server.security.permission.ProjectPermission; import io.onedev.server.util.EditContext; +import io.onedev.server.util.facade.GroupFacade; import io.onedev.server.web.editable.annotation.Editable; import io.onedev.server.web.editable.annotation.ShowCondition; @@ -142,6 +143,11 @@ public class Group extends AbstractEntity implements Permission { return members; } + @Override + public GroupFacade getFacade() { + return new GroupFacade(getId(), getName()); + } + @Override public int compareTo(AbstractEntity entity) { Group group = (Group) entity; diff --git a/server-core/src/main/java/io/onedev/server/model/support/administration/SecuritySetting.java b/server-core/src/main/java/io/onedev/server/model/support/administration/SecuritySetting.java index 50eba5090f..9eac3c6c85 100644 --- a/server-core/src/main/java/io/onedev/server/model/support/administration/SecuritySetting.java +++ b/server-core/src/main/java/io/onedev/server/model/support/administration/SecuritySetting.java @@ -1,7 +1,6 @@ package io.onedev.server.model.support.administration; import java.io.Serializable; -import java.util.Optional; import javax.annotation.Nullable; @@ -30,8 +29,6 @@ public class SecuritySetting implements Serializable { private boolean enforce2FA; - private transient Optional defaultLoginGroup; - @Editable(order=100, description="Whether or not to allow anonymous users to access this server") public boolean isEnableAnonymousAccess() { return enableAnonymousAccess; @@ -74,17 +71,12 @@ public class SecuritySetting implements Serializable { @Nullable public Group getDefaultLoginGroup() { - if (defaultLoginGroup == null) { - if (defaultLoginGroupName != null) { - Group group = OneDev.getInstance(GroupManager.class).find(defaultLoginGroupName); - if (group == null) - logger.error("Unable to find default login group: " + defaultLoginGroupName); - defaultLoginGroup = Optional.ofNullable(group); - } else { - defaultLoginGroup = Optional.empty(); - } + if (defaultLoginGroupName != null) { + Group group = OneDev.getInstance(GroupManager.class).find(defaultLoginGroupName); + if (group == null) + logger.error("Unable to find default login group: " + defaultLoginGroupName); } - return defaultLoginGroup.orElse(null); + return null; } public void onRenameGroup(String oldName, String newName) { diff --git a/server-core/src/main/java/io/onedev/server/util/facade/GroupCache.java b/server-core/src/main/java/io/onedev/server/util/facade/GroupCache.java new file mode 100644 index 0000000000..70184914a8 --- /dev/null +++ b/server-core/src/main/java/io/onedev/server/util/facade/GroupCache.java @@ -0,0 +1,33 @@ +package io.onedev.server.util.facade; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; + +import io.onedev.server.util.MapProxy; + +public class GroupCache extends MapProxy implements Serializable { + + private static final long serialVersionUID = 1L; + + public GroupCache(Map delegate) { + super(delegate); + } + + @Nullable + public GroupFacade find(String name) { + for (GroupFacade facade: values()) { + if (name.equals(facade.getName())) + return facade; + } + return null; + } + + @Override + public GroupCache clone() { + return new GroupCache(new HashMap<>(delegate)); + } + +}