Change to use most permissive permission mode.

This commit is contained in:
robin shine 2013-09-22 07:20:28 +08:00
parent e2b33f6adf
commit 426b02af5a
33 changed files with 491 additions and 267 deletions

View File

@ -55,5 +55,8 @@ public abstract class AbstractEntity implements Serializable, Comparable<Abstrac
public int compareTo(AbstractEntity entity) {
return getId().compareTo(entity.getId());
}
public boolean isNew() {
return getId() == null;
}
}

View File

@ -2,7 +2,8 @@ package com.pmease.commons.shiro;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import javax.annotation.Nullable;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
@ -11,29 +12,16 @@ import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.WildcardPermission;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import com.google.inject.Inject;
import com.pmease.commons.hibernate.dao.GeneralDao;
import com.pmease.commons.util.ReflectionUtils;
import com.pmease.commons.util.EasySet;
public abstract class AbstractRealm<T extends AbstractUser> extends AuthorizingRealm {
public abstract class AbstractRealm extends AuthorizingRealm {
private GeneralDao generalDao;
private final Class<T> userClass;
@SuppressWarnings("unchecked")
@Inject
public AbstractRealm(GeneralDao generalDao, CredentialsMatcher credentialsMatcher) {
this.generalDao = generalDao;
List<Class<?>> typeArguments = ReflectionUtils.getTypeArguments(AbstractRealm.class, getClass());
userClass = ((Class<T>) typeArguments.get(0));
public AbstractRealm(CredentialsMatcher credentialsMatcher) {
setCredentialsMatcher(credentialsMatcher);
}
@ -56,21 +44,11 @@ public abstract class AbstractRealm<T extends AbstractUser> extends AuthorizingR
@Override
public Collection<Permission> getObjectPermissions() {
return permissionsOf(userId);
return EasySet.of((Permission)getUserById(userId));
}
};
}
/**
* Get assigned permissions of user of specified identifier.
*
* @param userId
* Identifier of user to get permissions of. Value of <tt>0</tt> means anonymous user
* @return
* Collection of {@link WildcardPermission} string
*/
protected abstract Collection<Permission> permissionsOf(Long userId);
/**
* Retrieve {@link AuthenticationInfo} of specified token.
*
@ -85,16 +63,33 @@ public abstract class AbstractRealm<T extends AbstractUser> extends AuthorizingR
* AuthenticationException
*/
protected AuthenticationInfo authenticationInfoOf(UsernamePasswordToken token) throws AuthenticationException {
DetachedCriteria criteria = DetachedCriteria.forClass(userClass);
criteria.add(Restrictions.eq("name", token.getUsername()));
return (AbstractUser) generalDao.find(criteria);
return getUserByName(token.getUsername());
}
@Override
protected final AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return authenticationInfoOf((UsernamePasswordToken) token);
}
/**
* Get user by identifier.
*
* @param userId
* identifier of the user, <tt>0</tt> represents anonymous user
* @return
* user with specified identifier, not allowed to return null
*/
protected abstract AbstractUser getUserById(Long userId);
/**
* Get user by name.
*
* @param userName
* name of the user
* @return
* user with specified name, or <tt>null</tt> if not found
*/
protected abstract @Nullable AbstractUser getUserByName(String userName);
@Override
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {

View File

@ -3,15 +3,20 @@ package com.pmease.commons.shiro;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import com.google.common.base.Preconditions;
import com.pmease.commons.hibernate.AbstractEntity;
import com.pmease.commons.loader.AppLoader;
@SuppressWarnings("serial")
@MappedSuperclass
public class AbstractUser extends AbstractEntity implements AuthenticationInfo {
public abstract class AbstractUser extends AbstractEntity implements AuthenticationInfo, Permission {
@Column(unique=true, nullable=false)
private String name;
@ -45,4 +50,19 @@ public class AbstractUser extends AbstractEntity implements AuthenticationInfo {
return passwordHash;
}
public Subject asSubject() {
PrincipalCollection principals = new SimplePrincipalCollection(getId(), "");
return new Subject.Builder().principals(principals).buildSubject();
}
public static AbstractUser getCurrent() {
Object principal = SecurityUtils.getSubject().getPrincipal();
Preconditions.checkNotNull(principal);
Long userId = (Long) principal;
if (userId != 0L)
return AppLoader.getInstance(AbstractRealm.class).getUserById(userId);
else
return null;
}
}

View File

@ -20,7 +20,6 @@ import com.pmease.commons.loader.PluginManager;
@Singleton
public class DefaultWebSecurityManager extends org.apache.shiro.web.mgt.DefaultWebSecurityManager {
@SuppressWarnings("rawtypes")
@Inject
public DefaultWebSecurityManager(PluginManager pluginManager, AbstractRealm userRealm) {

View File

@ -1,29 +0,0 @@
package com.pmease.commons.shiro;
import org.apache.shiro.SecurityUtils;
import com.google.common.base.Preconditions;
import com.pmease.commons.hibernate.dao.GeneralDao;
import com.pmease.commons.loader.AppLoader;
public class SecurityHelper extends SecurityUtils {
/**
* Get current user.
*
* @param userClass
* Class of the user
* @return
* Current user object or <tt>null</tt> for anonymous access
*/
public static <T extends AbstractUser> T getUser(Class<T> userClass) {
Object principal = getSubject().getPrincipal();
Preconditions.checkNotNull(principal);
Long userId = (Long) principal;
if (userId != 0L)
return (T) AppLoader.getInstance(GeneralDao.class).load(userClass, userId);
else
return null;
}
}

View File

@ -1,11 +0,0 @@
package com.pmease.gitop.core.manager;
import com.google.inject.ImplementedBy;
import com.pmease.commons.hibernate.dao.GenericDao;
import com.pmease.gitop.core.manager.impl.DefaultAuthorizationManager;
import com.pmease.gitop.core.model.RepositoryAuthorization;
@ImplementedBy(DefaultAuthorizationManager.class)
public interface AuthorizationManager extends GenericDao<RepositoryAuthorization> {
}

View File

@ -0,0 +1,11 @@
package com.pmease.gitop.core.manager;
import com.google.inject.ImplementedBy;
import com.pmease.commons.hibernate.dao.GenericDao;
import com.pmease.gitop.core.manager.impl.DefaultRepositoryAuthorizationByTeamManager;
import com.pmease.gitop.core.model.RepositoryAuthorizationByIndividual;
@ImplementedBy(DefaultRepositoryAuthorizationByTeamManager.class)
public interface RepositoryAuthorizationByIndividualManager extends GenericDao<RepositoryAuthorizationByIndividual> {
}

View File

@ -0,0 +1,11 @@
package com.pmease.gitop.core.manager;
import com.google.inject.ImplementedBy;
import com.pmease.commons.hibernate.dao.GenericDao;
import com.pmease.gitop.core.manager.impl.DefaultRepositoryAuthorizationByTeamManager;
import com.pmease.gitop.core.model.RepositoryAuthorizationByTeam;
@ImplementedBy(DefaultRepositoryAuthorizationByTeamManager.class)
public interface RepositoryAuthorizationByTeamManager extends GenericDao<RepositoryAuthorizationByTeam> {
}

View File

@ -1,6 +1,7 @@
package com.pmease.gitop.core.manager;
import java.io.File;
import java.util.Collection;
import com.google.inject.ImplementedBy;
import com.pmease.commons.hibernate.dao.GenericDao;
@ -12,4 +13,5 @@ public interface RepositoryManager extends GenericDao<Repository> {
File locateStorage(Repository repository);
Collection<Repository> findPublic();
}

View File

@ -1,7 +1,5 @@
package com.pmease.gitop.core.manager;
import java.util.Collection;
import com.google.inject.ImplementedBy;
import com.pmease.commons.hibernate.dao.GenericDao;
import com.pmease.gitop.core.manager.impl.DefaultRoleManager;
@ -10,7 +8,4 @@ import com.pmease.gitop.core.model.Role;
@ImplementedBy(DefaultRoleManager.class)
public interface RoleManager extends GenericDao<Role> {
Collection<Role> findAnonymousRoles();
Collection<Role> findRegisterRoles();
}

View File

@ -1,7 +1,5 @@
package com.pmease.gitop.core.manager;
import java.util.Collection;
import com.google.inject.ImplementedBy;
import com.pmease.commons.hibernate.dao.GenericDao;
import com.pmease.commons.util.namedentity.EntityLoader;
@ -12,10 +10,6 @@ import com.pmease.gitop.core.model.User;
@ImplementedBy(DefaultTeamManager.class)
public interface TeamManager extends GenericDao<Team> {
Collection<Team> findAnonymousTeams();
Collection<Team> findRegisterTeams();
/**
* Find team of specified name belonging to specified owner.
* <p>

View File

@ -0,0 +1,11 @@
package com.pmease.gitop.core.manager;
import com.google.inject.ImplementedBy;
import com.pmease.commons.hibernate.dao.GenericDao;
import com.pmease.gitop.core.manager.impl.DefaultRepositoryAuthorizationByTeamManager;
import com.pmease.gitop.core.model.UserAuthorizationByIndividual;
@ImplementedBy(DefaultRepositoryAuthorizationByTeamManager.class)
public interface UserAuthorizationByIndividualManager extends GenericDao<UserAuthorizationByIndividual> {
}

View File

@ -1,19 +0,0 @@
package com.pmease.gitop.core.manager.impl;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.pmease.commons.hibernate.dao.DefaultGenericDao;
import com.pmease.commons.hibernate.dao.GeneralDao;
import com.pmease.gitop.core.manager.AuthorizationManager;
import com.pmease.gitop.core.model.RepositoryAuthorization;
@Singleton
public class DefaultAuthorizationManager extends DefaultGenericDao<RepositoryAuthorization> implements AuthorizationManager {
@Inject
public DefaultAuthorizationManager(GeneralDao generalDao) {
super(generalDao);
}
}

View File

@ -0,0 +1,21 @@
package com.pmease.gitop.core.manager.impl;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.pmease.commons.hibernate.dao.DefaultGenericDao;
import com.pmease.commons.hibernate.dao.GeneralDao;
import com.pmease.gitop.core.manager.RepositoryAuthorizationByIndividualManager;
import com.pmease.gitop.core.model.RepositoryAuthorizationByIndividual;
@Singleton
public class DefaultRepositoryAuthorizationByIndividualManager
extends DefaultGenericDao<RepositoryAuthorizationByIndividual>
implements RepositoryAuthorizationByIndividualManager {
@Inject
public DefaultRepositoryAuthorizationByIndividualManager(GeneralDao generalDao) {
super(generalDao);
}
}

View File

@ -0,0 +1,20 @@
package com.pmease.gitop.core.manager.impl;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.pmease.commons.hibernate.dao.DefaultGenericDao;
import com.pmease.commons.hibernate.dao.GeneralDao;
import com.pmease.gitop.core.manager.RepositoryAuthorizationByTeamManager;
import com.pmease.gitop.core.model.RepositoryAuthorizationByTeam;
@Singleton
public class DefaultRepositoryAuthorizationByTeamManager extends DefaultGenericDao<RepositoryAuthorizationByTeam>
implements RepositoryAuthorizationByTeamManager {
@Inject
public DefaultRepositoryAuthorizationByTeamManager(GeneralDao generalDao) {
super(generalDao);
}
}

View File

@ -1,10 +1,14 @@
package com.pmease.gitop.core.manager.impl;
import java.io.File;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import com.pmease.commons.hibernate.dao.DefaultGenericDao;
import com.pmease.commons.hibernate.dao.GeneralDao;
import com.pmease.gitop.core.manager.RepositoryManager;
@ -24,4 +28,9 @@ public class DefaultRepositoryManager extends DefaultGenericDao<Repository> impl
return null;
}
@Override
public Collection<Repository> findPublic() {
return query(new Criterion[]{Restrictions.eq("publiclyAccessible", true)});
}
}

View File

@ -1,16 +1,11 @@
package com.pmease.gitop.core.manager.impl;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import com.pmease.commons.hibernate.Sessional;
import com.pmease.commons.hibernate.dao.DefaultGenericDao;
import com.pmease.commons.hibernate.dao.GeneralDao;
import com.pmease.gitop.core.manager.RoleManager;
@ -24,16 +19,4 @@ public class DefaultRoleManager extends DefaultGenericDao<Role> implements RoleM
super(generalDao);
}
@Sessional
@Override
public Collection<Role> findAnonymousRoles() {
return query(new Criterion[]{Restrictions.eq("anonymous", true)});
}
@Sessional
@Override
public Collection<Role> findRegisterRoles() {
return query(new Criterion[]{Restrictions.eq("register", true)});
}
}

View File

@ -1,7 +1,5 @@
package com.pmease.gitop.core.manager.impl;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -25,18 +23,6 @@ public class DefaultTeamManager extends DefaultGenericDao<Team> implements TeamM
super(generalDao);
}
@Sessional
@Override
public Collection<Team> findAnonymousTeams() {
return query(new Criterion[]{Restrictions.eq("anonymous", true)});
}
@Sessional
@Override
public Collection<Team> findRegisterTeams() {
return query(new Criterion[]{Restrictions.eq("register", true)});
}
@Sessional
@Override
public Team find(User owner, String teamName) {

View File

@ -0,0 +1,21 @@
package com.pmease.gitop.core.manager.impl;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.pmease.commons.hibernate.dao.DefaultGenericDao;
import com.pmease.commons.hibernate.dao.GeneralDao;
import com.pmease.gitop.core.manager.UserAuthorizationByIndividualManager;
import com.pmease.gitop.core.model.UserAuthorizationByIndividual;
@Singleton
public class DefaultUserAuthorizationByIndividualManager
extends DefaultGenericDao<UserAuthorizationByIndividual>
implements UserAuthorizationByIndividualManager {
@Inject
public DefaultUserAuthorizationByIndividualManager(GeneralDao generalDao) {
super(generalDao);
}
}

View File

@ -32,7 +32,7 @@ public class DefaultVoteInvitationManager extends DefaultGenericDao<VoteInvitati
public void save(VoteInvitation voteInvitation) {
if (voteInvitation.getId() == null) {
voteInvitation.getRequest().getVoteInvitations().add(voteInvitation);
voteInvitation.getReviewer().getVoteInvitations().add(voteInvitation);
voteInvitation.getVoter().getVoteInvitations().add(voteInvitation);
}
super.save(voteInvitation);
}
@ -41,7 +41,7 @@ public class DefaultVoteInvitationManager extends DefaultGenericDao<VoteInvitati
@Override
public void delete(VoteInvitation voteInvitation) {
voteInvitation.getRequest().getVoteInvitations().remove(voteInvitation);
voteInvitation.getReviewer().getVoteInvitations().remove(voteInvitation);
voteInvitation.getVoter().getVoteInvitations().remove(voteInvitation);
super.delete(voteInvitation);
}

View File

@ -275,7 +275,7 @@ public class MergeRequest extends AbstractEntity {
// users already voted for latest update should be excluded
for (Vote vote: getLatestUpdate().getVotes())
candidates.remove(vote.getReviewer());
candidates.remove(vote.getVoter());
getPotentialVoters().addAll(candidates);
@ -284,12 +284,12 @@ public class MergeRequest extends AbstractEntity {
* invitation list as their votes are still valid
*/
for (Vote vote: getBaseUpdate().listVotesOnwards()) {
candidates.remove(vote.getReviewer());
candidates.remove(vote.getVoter());
}
Set<User> invited = new HashSet<User>();
for (VoteInvitation each: getVoteInvitations())
invited.add(each.getReviewer());
invited.add(each.getVoter());
invited.retainAll(candidates);
@ -307,7 +307,7 @@ public class MergeRequest extends AbstractEntity {
VoteInvitation invitation = new VoteInvitation();
invitation.setRequest(this);
invitation.setReviewer(selected);
invitation.setVoter(selected);
Gitop.getInstance(VoteInvitationManager.class).save(invitation);
}
@ -332,7 +332,7 @@ public class MergeRequest extends AbstractEntity {
Collection<VoteInvitation> withdraws = new HashSet<VoteInvitation>();
for (VoteInvitation invitation: getVoteInvitations()) {
if (!getPotentialVoters().contains(invitation.getReviewer())) {
if (!getPotentialVoters().contains(invitation.getVoter())) {
withdraws.add(invitation);
}
}

View File

@ -2,9 +2,8 @@ package com.pmease.gitop.core.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
@ -15,7 +14,10 @@ import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import com.pmease.commons.hibernate.AbstractEntity;
import com.pmease.gitop.core.Gitop;
import com.pmease.gitop.core.gatekeeper.GateKeeper;
import com.pmease.gitop.core.manager.UserManager;
import com.pmease.gitop.core.permission.ObjectPermission;
import com.pmease.gitop.core.permission.object.ProtectedObject;
import com.pmease.gitop.core.permission.object.UserBelonging;
import com.pmease.gitop.core.permission.operation.RepositoryOperation;
@ -36,10 +38,20 @@ public class Repository extends AbstractEntity implements UserBelonging {
private String description;
private boolean publiclyAccessible;
@Column(nullable=false)
private RepositoryOperation defaultAuthorizedOperation;
private GateKeeper gateKeeper;
@OneToMany(mappedBy="repository")
private Collection<RepositoryAuthorization> authorizations = new ArrayList<RepositoryAuthorization>();
private Collection<RepositoryAuthorizationByTeam> authorizationsByTeam =
new ArrayList<RepositoryAuthorizationByTeam>();
@OneToMany(mappedBy="repository")
private Collection<RepositoryAuthorizationByIndividual> authorizationsByIndividual =
new ArrayList<RepositoryAuthorizationByIndividual>();
public User getOwner() {
return owner;
@ -65,6 +77,23 @@ public class Repository extends AbstractEntity implements UserBelonging {
this.description = description;
}
public boolean isPubliclyAccessible() {
return publiclyAccessible;
}
public void setPubliclyAccessible(boolean publiclyAccessible) {
this.publiclyAccessible = publiclyAccessible;
}
public RepositoryOperation getDefaultAuthorizedOperation() {
return defaultAuthorizedOperation;
}
public void setDefaultAuthorizedOperation(
RepositoryOperation defaultAuthorizedOperation) {
this.defaultAuthorizedOperation = defaultAuthorizedOperation;
}
public GateKeeper getGateKeeper() {
if (gateKeeper != null)
gateKeeper = (GateKeeper) gateKeeper.trim(this);
@ -80,12 +109,22 @@ public class Repository extends AbstractEntity implements UserBelonging {
return getOwner();
}
public Collection<RepositoryAuthorization> getAuthorizations() {
return authorizations;
public Collection<RepositoryAuthorizationByTeam> getAuthorizationsByTeam() {
return authorizationsByTeam;
}
public void setAuthorizations(Collection<RepositoryAuthorization> authorizations) {
this.authorizations = authorizations;
public void setAuthorizationsByTeam(
Collection<RepositoryAuthorizationByTeam> authorizationsByTeam) {
this.authorizationsByTeam = authorizationsByTeam;
}
public Collection<RepositoryAuthorizationByIndividual> getAuthorizationsByIndividual() {
return authorizationsByIndividual;
}
public void setAuthorizationsByIndividual(
Collection<RepositoryAuthorizationByIndividual> authorizationsByIndividual) {
this.authorizationsByIndividual = authorizationsByIndividual;
}
@Override
@ -99,32 +138,12 @@ public class Repository extends AbstractEntity implements UserBelonging {
}
public Collection<User> findAuthorizedUsers(RepositoryOperation operation) {
Map<Long, Boolean> authorizationMap = new HashMap<Long, Boolean>();
for (RepositoryAuthorization authorization: getAuthorizations()) {
authorizationMap.put(authorization.getTeam().getId(), authorization.getAuthorizedOperation().can(operation));
Set<User> authorizedUsers = new HashSet<User>();
for (User user: Gitop.getInstance(UserManager.class).query(null)) {
if (user.asSubject().isPermitted(new ObjectPermission(this, operation)))
authorizedUsers.add(user);
}
Collection<Team> teams = new HashSet<Team>();
for (Team team: getOwner().getTeams()) {
Boolean authorized = authorizationMap.get(team.getId());
if (authorized != null) {
if (authorized)
teams.add(team);
else
continue;
} else {
if (team.getAuthorizedOperation().can(operation))
teams.add(team);
}
}
Collection<User> users = new HashSet<User>();
for (Team team: teams) {
for (TeamMembership membership: team.getMemberships())
users.add(membership.getUser());
}
return users;
return authorizedUsers;
}
}

View File

@ -0,0 +1,53 @@
package com.pmease.gitop.core.model;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import com.pmease.commons.hibernate.AbstractEntity;
import com.pmease.gitop.core.permission.operation.RepositoryOperation;
@SuppressWarnings("serial")
@Entity
@Table(uniqueConstraints={
@UniqueConstraint(columnNames={"individual", "repository"})
})
public class RepositoryAuthorizationByIndividual extends AbstractEntity {
@ManyToOne
@JoinColumn(nullable=false)
private User individual;
@ManyToOne
@JoinColumn(nullable=false)
private Repository repository;
private RepositoryOperation authorizedOperation = RepositoryOperation.READ;
public RepositoryOperation getAuthorizedOperation() {
return authorizedOperation;
}
public void setAuthorizedOperation(RepositoryOperation authorizedOperation) {
this.authorizedOperation = authorizedOperation;
}
public User getIndividual() {
return individual;
}
public void setIndividual(User individual) {
this.individual = individual;
}
public Repository getRepository() {
return repository;
}
public void setRepository(Repository repository) {
this.repository = repository;
}
}

View File

@ -14,7 +14,7 @@ import com.pmease.gitop.core.permission.operation.RepositoryOperation;
@Table(uniqueConstraints={
@UniqueConstraint(columnNames={"team", "repository"})
})
public class RepositoryAuthorization extends AbstractEntity {
public class RepositoryAuthorizationByTeam extends AbstractEntity {
@ManyToOne
@JoinColumn(nullable=false)

View File

@ -11,7 +11,6 @@ import org.apache.shiro.authz.Permission;
import com.pmease.commons.hibernate.AbstractEntity;
import com.pmease.gitop.core.permission.ObjectPermission;
import com.pmease.gitop.core.permission.object.SystemObject;
import com.pmease.gitop.core.permission.operation.PrivilegedOperation;
@Entity
@ -23,10 +22,6 @@ public class Role extends AbstractEntity implements Permission {
private String description;
private boolean anonymous;
private boolean register;
@Column(nullable=false)
private ArrayList<PrivilegedOperation> operations = new ArrayList<PrivilegedOperation>();
@ -49,22 +44,6 @@ public class Role extends AbstractEntity implements Permission {
this.description = description;
}
public boolean isAnonymous() {
return anonymous;
}
public void setAnonymous(boolean anonymous) {
this.anonymous = anonymous;
}
public boolean isRegister() {
return register;
}
public void setRegister(boolean register) {
this.register = register;
}
public Collection<RoleMembership> getMemberships() {
return memberships;
}
@ -85,11 +64,9 @@ public class Role extends AbstractEntity implements Permission {
public boolean implies(Permission permission) {
if (permission instanceof ObjectPermission) {
ObjectPermission objectPermission = (ObjectPermission) permission;
if (new SystemObject().has(objectPermission.getObject())) {
for (PrivilegedOperation each: getOperations()) {
if (each.can(objectPermission.getOperation()))
return true;
}
for (PrivilegedOperation each: getOperations()) {
if (each.can(objectPermission.getOperation()))
return true;
}
}
return false;

View File

@ -44,7 +44,7 @@ public class Team extends AbstractEntity implements Permission {
private Collection<TeamMembership> memberships = new ArrayList<TeamMembership>();
@OneToMany(mappedBy="team")
private Collection<RepositoryAuthorization> repositoryAuthorizations = new ArrayList<RepositoryAuthorization>();
private Collection<RepositoryAuthorizationByTeam> repositoryAuthorizations = new ArrayList<RepositoryAuthorizationByTeam>();
public User getOwner() {
return owner;
@ -102,11 +102,11 @@ public class Team extends AbstractEntity implements Permission {
this.memberships = memberships;
}
public Collection<RepositoryAuthorization> getRepositoryAuthorizations() {
public Collection<RepositoryAuthorizationByTeam> getRepositoryAuthorizations() {
return repositoryAuthorizations;
}
public void setAuthorizations(Collection<RepositoryAuthorization> repositoryAuthorizations) {
public void setRepositoryAuthorizations(Collection<RepositoryAuthorizationByTeam> repositoryAuthorizations) {
this.repositoryAuthorizations = repositoryAuthorizations;
}
@ -114,16 +114,17 @@ public class Team extends AbstractEntity implements Permission {
public boolean implies(Permission permission) {
if (permission instanceof ObjectPermission) {
ObjectPermission objectPermission = (ObjectPermission) permission;
Permission userPermission = new ObjectPermission(getOwner(), getAuthorizedOperation());
if (userPermission.implies(objectPermission))
return true;
for (RepositoryAuthorization each: getRepositoryAuthorizations()) {
if (each.getRepository().has(objectPermission.getObject()))
return each.getAuthorizedOperation().can(objectPermission.getOperation());
for (RepositoryAuthorizationByTeam authorization: getRepositoryAuthorizations()) {
Permission repositoryPermission = new ObjectPermission(
authorization.getRepository(), authorization.getAuthorizedOperation());
if (repositoryPermission.implies(objectPermission))
return true;
}
if (getOwner().has(objectPermission.getObject()))
return getAuthorizedOperation().can(objectPermission.getOperation());
}
}
return false;
}

View File

@ -7,14 +7,20 @@ import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import org.apache.shiro.authz.Permission;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import com.pmease.commons.editable.annotation.Editable;
import com.pmease.commons.editable.annotation.Password;
import com.pmease.commons.shiro.AbstractUser;
import com.pmease.gitop.core.Gitop;
import com.pmease.gitop.core.manager.RepositoryManager;
import com.pmease.gitop.core.manager.UserManager;
import com.pmease.gitop.core.permission.ObjectPermission;
import com.pmease.gitop.core.permission.object.ProtectedObject;
import com.pmease.gitop.core.permission.object.UserBelonging;
import com.pmease.gitop.core.permission.operation.RepositoryOperation;
/**
* This class represents either a project or an user in the system.
@ -51,12 +57,24 @@ public class User extends AbstractUser implements ProtectedObject {
@OneToMany(mappedBy="owner")
private Collection<Team> teams = new ArrayList<Team>();
@OneToMany(mappedBy="reviewer")
@OneToMany(mappedBy="voter")
private Collection<Vote> votes = new ArrayList<Vote>();
@OneToMany(mappedBy="reviewer")
@OneToMany(mappedBy="voter")
private Collection<VoteInvitation> voteVitations = new ArrayList<VoteInvitation>();
@OneToMany(mappedBy="individual")
private Collection<RepositoryAuthorizationByIndividual> repositoryAuthorizations =
new ArrayList<RepositoryAuthorizationByIndividual>();
@OneToMany(mappedBy="individual")
private Collection<UserAuthorizationByIndividual> userAuthorizations =
new ArrayList<UserAuthorizationByIndividual>();
@OneToMany(mappedBy="user")
private Collection<UserAuthorizationByIndividual> authorizationsByIndividual =
new ArrayList<UserAuthorizationByIndividual>();
@Editable
@NotEmpty
@Override
@ -147,6 +165,41 @@ public class User extends AbstractUser implements ProtectedObject {
this.voteVitations = voteInvitations;
}
public Collection<VoteInvitation> getVoteVitations() {
return voteVitations;
}
public void setVoteVitations(Collection<VoteInvitation> voteVitations) {
this.voteVitations = voteVitations;
}
public Collection<RepositoryAuthorizationByIndividual> getRepositoryAuthorizations() {
return repositoryAuthorizations;
}
public void setRepositoryAuthorizations(
Collection<RepositoryAuthorizationByIndividual> repositoryAuthorizations) {
this.repositoryAuthorizations = repositoryAuthorizations;
}
public Collection<UserAuthorizationByIndividual> getUserAuthorizations() {
return userAuthorizations;
}
public void setUserAuthorizations(
Collection<UserAuthorizationByIndividual> userAuthorizations) {
this.userAuthorizations = userAuthorizations;
}
public Collection<UserAuthorizationByIndividual> getAuthorizationsByIndividual() {
return authorizationsByIndividual;
}
public void setAuthorizationsByIndividual(
Collection<UserAuthorizationByIndividual> authorizationsByIndividual) {
this.authorizationsByIndividual = authorizationsByIndividual;
}
@Override
public boolean has(ProtectedObject object) {
if (object instanceof User) {
@ -165,7 +218,7 @@ public class User extends AbstractUser implements ProtectedObject {
return Vote.Result.ACCEPT;
for (Vote vote: update.listVotesOnwards()) {
if (vote.getReviewer().equals(this)) {
if (vote.getVoter().equals(this)) {
return vote.getResult();
}
}
@ -173,4 +226,68 @@ public class User extends AbstractUser implements ProtectedObject {
return null;
}
public static User getCurrent() {
return (User) AbstractUser.getCurrent();
}
@Override
public boolean implies(Permission permission) {
// Root user can do anything
if (isRoot())
return true;
if (permission instanceof ObjectPermission) {
ObjectPermission objectPermission = (ObjectPermission) permission;
if (!isAnonymous()) {
// One can do anything against its belongings
if (has(objectPermission.getObject()))
return true;
for (RepositoryAuthorizationByIndividual authorization: getRepositoryAuthorizations()) {
ObjectPermission repositoryPermission = new ObjectPermission(
authorization.getRepository(), authorization.getAuthorizedOperation());
if (repositoryPermission.implies(objectPermission))
return true;
}
for (UserAuthorizationByIndividual each: getUserAuthorizations()) {
ObjectPermission userPermission = new ObjectPermission(each.getUser(), each.getAuthorizedOperation());
if (userPermission.implies(objectPermission))
return true;
}
for (Team team: getTeams()) {
if (team.implies(objectPermission))
return true;
}
for (RoleMembership each: getRoleMemberships()) {
if (each.getRole().implies(objectPermission))
return true;
}
for (Repository each: Gitop.getInstance(RepositoryManager.class).query(null)) {
ObjectPermission repositoryPermission = new ObjectPermission(each, each.getDefaultAuthorizedOperation());
if (repositoryPermission.implies(objectPermission))
return true;
}
} else {
for (Repository each: Gitop.getInstance(RepositoryManager.class).findPublic()) {
ObjectPermission repositoryPermission = new ObjectPermission(each, RepositoryOperation.READ);
if (repositoryPermission.implies(objectPermission))
return true;
}
}
}
return false;
}
public boolean isRoot() {
return Gitop.getInstance(UserManager.class).getRootUser().equals(this);
}
public boolean isAnonymous() {
return getId() == 0L;
}
}

View File

@ -0,0 +1,53 @@
package com.pmease.gitop.core.model;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import com.pmease.commons.hibernate.AbstractEntity;
import com.pmease.gitop.core.permission.operation.UserOperation;
@SuppressWarnings("serial")
@Entity
@Table(uniqueConstraints={
@UniqueConstraint(columnNames={"individual", "user"})
})
public class UserAuthorizationByIndividual extends AbstractEntity {
@ManyToOne
@JoinColumn(nullable=false)
private User individual;
@ManyToOne
@JoinColumn(nullable=false)
private User user;
private UserOperation authorizedOperation = UserOperation.READ;
public UserOperation getAuthorizedOperation() {
return authorizedOperation;
}
public void setAuthorizedOperation(UserOperation authorizedOperation) {
this.authorizedOperation = authorizedOperation;
}
public User getIndividual() {
return individual;
}
public void setIndividual(User individual) {
this.individual = individual;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}

View File

@ -12,7 +12,7 @@ import com.pmease.commons.hibernate.AbstractEntity;
@SuppressWarnings("serial")
@Entity
@Table(uniqueConstraints={
@UniqueConstraint(columnNames={"reviewer", "update"})
@UniqueConstraint(columnNames={"voter", "update"})
})
public class Vote extends AbstractEntity {
@ -47,7 +47,7 @@ public class Vote extends AbstractEntity {
@ManyToOne
@JoinColumn(nullable=false)
private User reviewer;
private User voter;
@ManyToOne
@JoinColumn(nullable=false)
@ -56,12 +56,12 @@ public class Vote extends AbstractEntity {
@Column(nullable=false)
private Result result;
public User getReviewer() {
return reviewer;
public User getVoter() {
return voter;
}
public void setReviewer(User reviewer) {
this.reviewer = reviewer;
public void setVoter(User voter) {
this.voter = voter;
}
public MergeRequestUpdate getUpdate() {

View File

@ -11,24 +11,24 @@ import com.pmease.commons.hibernate.AbstractEntity;
@SuppressWarnings("serial")
@Entity
@Table(uniqueConstraints={
@UniqueConstraint(columnNames={"reviewer", "request"})
@UniqueConstraint(columnNames={"voter", "request"})
})
public class VoteInvitation extends AbstractEntity {
@ManyToOne
@JoinColumn(nullable=false)
private User reviewer;
private User voter;
@ManyToOne
@JoinColumn(nullable=false)
private MergeRequest request;
public User getReviewer() {
return reviewer;
public User getVoter() {
return voter;
}
public void setReviewer(User reviewer) {
this.reviewer = reviewer;
public void setVoter(User voter) {
this.voter = voter;
}
public MergeRequest getRequest() {

View File

@ -1,63 +1,45 @@
package com.pmease.gitop.core.permission;
import java.util.ArrayList;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.Permission;
import com.pmease.commons.hibernate.dao.GeneralDao;
import com.pmease.commons.shiro.AbstractRealm;
import com.pmease.commons.shiro.AbstractUser;
import com.pmease.gitop.core.manager.RoleManager;
import com.pmease.gitop.core.manager.TeamManager;
import com.pmease.gitop.core.manager.UserManager;
import com.pmease.gitop.core.model.RoleMembership;
import com.pmease.gitop.core.model.TeamMembership;
import com.pmease.gitop.core.model.User;
@Singleton
public class UserRealm extends AbstractRealm<User> {
public class UserRealm extends AbstractRealm {
private final UserManager userManager;
private final RoleManager roleManager;
private final TeamManager teamManager;
@Inject
public UserRealm(GeneralDao generalDao, CredentialsMatcher credentialsMatcher,
UserManager userManager, RoleManager roleManager, TeamManager teamManager) {
super(generalDao, credentialsMatcher);
super(credentialsMatcher);
this.userManager = userManager;
this.roleManager = roleManager;
this.teamManager = teamManager;
}
@Override
protected Collection<Permission> permissionsOf(Long userId) {
Collection<Permission> permissions = new ArrayList<Permission>();
protected AbstractUser getUserById(Long userId) {
if (userId != 0L) {
User user = userManager.load(userId);
for (RoleMembership membership: user.getRoleMemberships())
permissions.add(membership.getRole());
for (TeamMembership membership: user.getTeamMemberships())
permissions.add(membership.getTeam());
/* an user is administrator of its own account */
permissions.add(ObjectPermission.ofUserAdmin(user));
} else {
permissions.addAll(roleManager.findAnonymousRoles());
permissions.addAll(teamManager.findAnonymousTeams());
return userManager.load(userId);
} else {
User user = new User();
user.setId(0L);
return user;
}
}
return permissions;
@Override
protected AbstractUser getUserByName(String userName) {
return userManager.find(userName);
}
}

View File

@ -1,14 +1,6 @@
package com.pmease.gitop.core.permission.operation;
public enum RepositoryOperation implements PrivilegedOperation {
NO_ACCESS {
@Override
public boolean can(PrivilegedOperation operation) {
return false;
}
},
READ {
@Override

View File

@ -1,6 +1,14 @@
package com.pmease.gitop.core.permission.operation;
public enum UserOperation implements PrivilegedOperation {
NO_ACCESS {
@Override
public boolean can(PrivilegedOperation operation) {
return false;
}
},
READ {
@Override