mirror of
https://github.com/theonedev/onedev.git
synced 2025-12-08 18:26:30 +00:00
Several gate keepers.
This commit is contained in:
parent
9a79414fa3
commit
58348f4c5c
@ -88,8 +88,9 @@ public class DefaultGenericDao<T extends AbstractEntity> implements GenericDao<T
|
||||
return search(criterions, null, 0, 0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object find(Criterion[] criterions) {
|
||||
public T find(Criterion[] criterions) {
|
||||
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
|
||||
|
||||
if (criterions != null) {
|
||||
@ -97,7 +98,7 @@ public class DefaultGenericDao<T extends AbstractEntity> implements GenericDao<T
|
||||
detachedCriteria.add(criterion);
|
||||
}
|
||||
|
||||
return generalDao.find(detachedCriteria);
|
||||
return (T) generalDao.find(detachedCriteria);
|
||||
}
|
||||
|
||||
protected Session getSession() {
|
||||
|
||||
@ -16,8 +16,6 @@ public class AbstractUser extends AbstractEntity implements AuthenticationInfo {
|
||||
@Column(unique=true, nullable=false)
|
||||
private String name;
|
||||
|
||||
private String fullName;
|
||||
|
||||
@Column(length=1024)
|
||||
private String passwordHash;
|
||||
|
||||
@ -29,14 +27,6 @@ public class AbstractUser extends AbstractEntity implements AuthenticationInfo {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public String getPasswordHash() {
|
||||
return passwordHash;
|
||||
}
|
||||
|
||||
@ -26,24 +26,4 @@ public class SecurityHelper extends SecurityUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get display name of current user.
|
||||
* @param userClass
|
||||
* Class of the user
|
||||
* @param anonymousName
|
||||
* Display name of anonymous user
|
||||
* @return
|
||||
* Display name of current user or specified anonymous name for anonymous user
|
||||
*/
|
||||
public static <T extends AbstractUser> String getUserDisplayName(Class<T> userClass, String anonymousName) {
|
||||
T user = getUser(userClass);
|
||||
if (user != null) {
|
||||
if (user.getFullName() != null)
|
||||
return user.getFullName();
|
||||
else
|
||||
return user.getName();
|
||||
} else {
|
||||
return anonymousName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,19 @@ package com.pmease.commons.util.namedentity;
|
||||
import com.pmease.commons.util.pattern.WildcardUtils;
|
||||
import com.pmease.commons.util.trimmable.Trimmable;
|
||||
|
||||
/**
|
||||
* An entity pattern refers to a string matching one or more database entities.
|
||||
* <p>
|
||||
* When refer to only one entity, the string should represent id of the entity.
|
||||
* This is necessary as entity name can be changed while id will never. In case
|
||||
* the entity is deleted, the data structure can implement {@link Trimmable} to
|
||||
* trim the pattern.
|
||||
* <p>
|
||||
* Example entity patterns of stored form: <i>100, qa*, 5.0.?</i>
|
||||
* <p>
|
||||
* @author robin
|
||||
*
|
||||
*/
|
||||
public class EntityPattern implements Trimmable {
|
||||
|
||||
private final String stored;
|
||||
@ -46,7 +59,7 @@ public class EntityPattern implements Trimmable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trimmable trim() {
|
||||
public Object trim(Object context) {
|
||||
if (asId() == null || asEntity() != null)
|
||||
return this;
|
||||
else
|
||||
|
||||
@ -8,6 +8,14 @@ import com.pmease.commons.util.pattern.ExclusiveAwarePattern;
|
||||
import com.pmease.commons.util.pattern.PatternSet;
|
||||
import com.pmease.commons.util.trimmable.Trimmable;
|
||||
|
||||
/**
|
||||
* Entity pattern set represents a set of entity patterns.
|
||||
*
|
||||
* @see EntityPattern
|
||||
*
|
||||
* @author robin
|
||||
*
|
||||
*/
|
||||
public class EntityPatternSet extends PatternSet implements Trimmable {
|
||||
|
||||
private final EntityLoader entityLoader;
|
||||
@ -22,10 +30,18 @@ public class EntityPatternSet extends PatternSet implements Trimmable {
|
||||
return getPatterns();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim stored patterns of this pattern set.
|
||||
* <p>
|
||||
* Invalid pattern entries of current instance will be removed.
|
||||
* <p>
|
||||
* @return
|
||||
* trimmed instance, or <i>null</i> if no longer contains any patterns
|
||||
*/
|
||||
@Override
|
||||
public Trimmable trim() {
|
||||
public Object trim(Object context) {
|
||||
for (Iterator<ExclusiveAwarePattern> it = getStored().iterator(); it.hasNext();) {
|
||||
if (EntityPattern.fromStored(it.next().getPattern(), entityLoader).trim() == null)
|
||||
if (EntityPattern.fromStored(it.next().getPattern(), entityLoader).trim(context) == null)
|
||||
it.remove();
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
package com.pmease.commons.util.namedentity;
|
||||
|
||||
/**
|
||||
* A named entity normally refers to a database entity with name.
|
||||
* <p>
|
||||
* @author robin
|
||||
*
|
||||
*/
|
||||
public interface NamedEntity {
|
||||
Long getId();
|
||||
|
||||
|
||||
@ -2,10 +2,16 @@ package com.pmease.commons.util.trimmable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A simple interface for a And/Or data structure.
|
||||
* <p>
|
||||
* @author robin
|
||||
*
|
||||
*/
|
||||
public interface AndOrConstruct {
|
||||
|
||||
Trimmable getSelf();
|
||||
Object getSelf();
|
||||
|
||||
List<? extends Trimmable> getMembers();
|
||||
List<?> getMembers();
|
||||
|
||||
}
|
||||
|
||||
@ -5,26 +5,31 @@ import java.util.List;
|
||||
|
||||
public class TrimUtils {
|
||||
|
||||
public static void trim(List<Trimmable> list) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void trim(List<?> list, Object context) {
|
||||
int index = 0;
|
||||
for (Iterator<Trimmable> it = list.iterator(); it.hasNext();) {
|
||||
Trimmable trimmable = it.next();
|
||||
Trimmable trimmed = trimmable.trim();
|
||||
if (trimmed == null) {
|
||||
it.remove();
|
||||
for (Iterator<?> it = list.iterator(); it.hasNext();) {
|
||||
Object entry = it.next();
|
||||
if (entry instanceof Trimmable) {
|
||||
Trimmable trimmable = (Trimmable) entry;
|
||||
Object trimmed = trimmable.trim(context);
|
||||
if (trimmed == null) {
|
||||
it.remove();
|
||||
} else {
|
||||
if (trimmed != trimmable)
|
||||
((List<Object>)list).set(index, trimmed);
|
||||
index ++;
|
||||
}
|
||||
} else {
|
||||
if (trimmed != trimmable)
|
||||
list.set(index, trimmed);
|
||||
index ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Trimmable trim(AndOrConstruct andOr) {
|
||||
List<? extends Trimmable> members = andOr.getMembers();
|
||||
public static Object trim(AndOrConstruct andOr, Object context) {
|
||||
List<?> members = andOr.getMembers();
|
||||
|
||||
trim((List<Trimmable>) members);
|
||||
trim(members, context);
|
||||
|
||||
if (members.size() == 1)
|
||||
return members.iterator().next();
|
||||
|
||||
@ -1,5 +1,28 @@
|
||||
package com.pmease.commons.util.trimmable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Use this interface to trim unused parts in a data structure.
|
||||
* <p>
|
||||
* For instance, some parts of a data structure might refer to database entities
|
||||
* (where using foreign key is inappropriate), and you may implement this
|
||||
* interface to remove those parts if the entity is removed from database.
|
||||
*
|
||||
* @author robin
|
||||
*
|
||||
*/
|
||||
public interface Trimmable {
|
||||
Trimmable trim();
|
||||
/**
|
||||
* Trim unused parts of current data structure.
|
||||
* <p>
|
||||
* @param context
|
||||
* context to help the trimming. Note context object is not the object to
|
||||
* be checked for trimming, instead it is used to help the trim process
|
||||
* @return
|
||||
* null if the data structure has to be removed. If not null, caller should
|
||||
* check if the return value is the same instance as current instance, and
|
||||
* to replace that instance if not the same.
|
||||
*/
|
||||
Object trim(@Nullable Object context);
|
||||
}
|
||||
|
||||
@ -12,21 +12,21 @@ public class TrimUtilsTest {
|
||||
|
||||
@Test
|
||||
public void shouldTrimList() {
|
||||
List<Trimmable> list = new ArrayList<Trimmable>();
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
list.add(new SimpleTrimmable(null));
|
||||
list.add(new SimpleTrimmable("1"));
|
||||
list.add(new SimpleTrimmable(null));
|
||||
list.add(new Trimmable() {
|
||||
|
||||
@Override
|
||||
public Trimmable trim() {
|
||||
public Trimmable trim(Object context) {
|
||||
return new SimpleTrimmable("2");
|
||||
}
|
||||
|
||||
});
|
||||
list.add(new SimpleTrimmable("3"));
|
||||
|
||||
TrimUtils.trim(list);
|
||||
TrimUtils.trim(list, null);
|
||||
|
||||
assertEquals(list.size(), 3);
|
||||
assertTrue(list.get(0) instanceof SimpleTrimmable);
|
||||
@ -46,7 +46,7 @@ public class TrimUtilsTest {
|
||||
new SimpleTrimmable("2")),
|
||||
new SimpleAndOrConstruct());
|
||||
|
||||
Trimmable result = TrimUtils.trim(construct);
|
||||
Object result = TrimUtils.trim(construct, null);
|
||||
|
||||
assertTrue(result instanceof AndOrConstruct);
|
||||
assertEquals(((AndOrConstruct)result).getMembers().size(), 2);
|
||||
@ -59,7 +59,7 @@ public class TrimUtilsTest {
|
||||
new SimpleTrimmable("2")),
|
||||
new SimpleAndOrConstruct());
|
||||
|
||||
result = TrimUtils.trim(construct);
|
||||
result = TrimUtils.trim(construct, null);
|
||||
|
||||
assertTrue(result instanceof SimpleTrimmable);
|
||||
assertEquals(((SimpleTrimmable)result).getValue(), "2");
|
||||
@ -67,7 +67,7 @@ public class TrimUtilsTest {
|
||||
|
||||
private static class SimpleAndOrConstruct implements AndOrConstruct, Trimmable {
|
||||
|
||||
private List<Trimmable> members = new ArrayList<Trimmable>();
|
||||
private List<Object> members = new ArrayList<Object>();
|
||||
|
||||
public SimpleAndOrConstruct(Trimmable...members) {
|
||||
for (Trimmable each: members)
|
||||
@ -75,17 +75,17 @@ public class TrimUtilsTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trimmable trim() {
|
||||
return TrimUtils.trim(this);
|
||||
public Object trim(Object context) {
|
||||
return TrimUtils.trim(this, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trimmable getSelf() {
|
||||
public Object getSelf() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Trimmable> getMembers() {
|
||||
public List<Object> getMembers() {
|
||||
return members;
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ public class TrimUtilsTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trimmable trim() {
|
||||
public Trimmable trim(Object context) {
|
||||
if (value == null)
|
||||
return null;
|
||||
else
|
||||
|
||||
@ -17,6 +17,7 @@ import com.pmease.gitop.core.entitymanager.UserManager;
|
||||
import com.pmease.gitop.core.model.RoleMembership;
|
||||
import com.pmease.gitop.core.model.TeamMembership;
|
||||
import com.pmease.gitop.core.model.User;
|
||||
import com.pmease.gitop.core.model.permission.ObjectPermission;
|
||||
|
||||
@Singleton
|
||||
public class UserRealm extends AbstractRealm<User> {
|
||||
@ -50,6 +51,8 @@ public class UserRealm extends AbstractRealm<User> {
|
||||
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.getAnonymousRoles());
|
||||
permissions.addAll(teamManager.getAnonymousTeams());
|
||||
|
||||
@ -4,8 +4,10 @@ import java.util.Collection;
|
||||
|
||||
import com.google.inject.ImplementedBy;
|
||||
import com.pmease.commons.persistence.dao.GenericDao;
|
||||
import com.pmease.commons.util.namedentity.EntityLoader;
|
||||
import com.pmease.gitop.core.entitymanager.impl.DefaultTeamManager;
|
||||
import com.pmease.gitop.core.model.Team;
|
||||
import com.pmease.gitop.core.model.User;
|
||||
|
||||
@ImplementedBy(DefaultTeamManager.class)
|
||||
public interface TeamManager extends GenericDao<Team> {
|
||||
@ -14,4 +16,18 @@ public interface TeamManager extends GenericDao<Team> {
|
||||
|
||||
Collection<Team> getRegisterTeams();
|
||||
|
||||
/**
|
||||
* Find team of specified name belonging to specified owner.
|
||||
* <p>
|
||||
* @param owner
|
||||
* user owns the team
|
||||
* @param name
|
||||
* name of the team
|
||||
* @return
|
||||
* matching team, or <i>null</i> if not found
|
||||
*/
|
||||
Team find(User owner, String name);
|
||||
|
||||
EntityLoader asEntityLoader(User owner);
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package com.pmease.gitop.core.entitymanager;
|
||||
|
||||
import com.google.inject.ImplementedBy;
|
||||
import com.pmease.commons.persistence.dao.GenericDao;
|
||||
import com.pmease.commons.util.namedentity.EntityLoader;
|
||||
import com.pmease.gitop.core.entitymanager.impl.DefaultUserManager;
|
||||
import com.pmease.gitop.core.model.User;
|
||||
|
||||
@ -14,4 +15,16 @@ public interface UserManager extends GenericDao<User> {
|
||||
* root account of the system, or null if root account has not been populated
|
||||
*/
|
||||
User getRootUser();
|
||||
|
||||
/**
|
||||
* Find user of specified name.
|
||||
* <p>
|
||||
* @param name
|
||||
* name of the user
|
||||
* @return
|
||||
* matching user, or <i>null</i> if not found
|
||||
*/
|
||||
User find(String name);
|
||||
|
||||
EntityLoader asEntityLoader();
|
||||
}
|
||||
|
||||
@ -12,8 +12,11 @@ import org.hibernate.criterion.Restrictions;
|
||||
import com.pmease.commons.persistence.Transactional;
|
||||
import com.pmease.commons.persistence.dao.DefaultGenericDao;
|
||||
import com.pmease.commons.persistence.dao.GeneralDao;
|
||||
import com.pmease.commons.util.namedentity.EntityLoader;
|
||||
import com.pmease.commons.util.namedentity.NamedEntity;
|
||||
import com.pmease.gitop.core.entitymanager.TeamManager;
|
||||
import com.pmease.gitop.core.model.Team;
|
||||
import com.pmease.gitop.core.model.User;
|
||||
|
||||
@Singleton
|
||||
public class DefaultTeamManager extends DefaultGenericDao<Team> implements TeamManager {
|
||||
@ -33,5 +36,62 @@ public class DefaultTeamManager extends DefaultGenericDao<Team> implements TeamM
|
||||
public Collection<Team> getRegisterTeams() {
|
||||
return search(new Criterion[]{Restrictions.eq("register", true)});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public Team find(User owner, String name) {
|
||||
return find(new Criterion[]{Restrictions.eq("owner", owner), Restrictions.eq("name", name)});
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityLoader asEntityLoader(final User owner) {
|
||||
return new EntityLoader() {
|
||||
|
||||
@Override
|
||||
public NamedEntity get(final Long id) {
|
||||
final Team team = DefaultTeamManager.this.find(id);
|
||||
if (team != null) {
|
||||
return new NamedEntity() {
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return team.getName();
|
||||
}
|
||||
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedEntity get(String name) {
|
||||
final Team team = find(owner, name);
|
||||
if (team != null) {
|
||||
return new NamedEntity() {
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return team.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return team.getName();
|
||||
}
|
||||
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,12 +5,16 @@ import javax.inject.Singleton;
|
||||
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.criterion.Criterion;
|
||||
import org.hibernate.criterion.Order;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.pmease.commons.persistence.Transactional;
|
||||
import com.pmease.commons.persistence.dao.DefaultGenericDao;
|
||||
import com.pmease.commons.persistence.dao.GeneralDao;
|
||||
import com.pmease.commons.util.namedentity.EntityLoader;
|
||||
import com.pmease.commons.util.namedentity.NamedEntity;
|
||||
import com.pmease.gitop.core.entitymanager.UserManager;
|
||||
import com.pmease.gitop.core.model.User;
|
||||
|
||||
@ -37,5 +41,62 @@ public class DefaultUserManager extends DefaultGenericDao<User> implements UserM
|
||||
}
|
||||
return rootUser;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public User find(String name) {
|
||||
return (User) find(new Criterion[]{Restrictions.eq("name", name)});
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityLoader asEntityLoader() {
|
||||
return new EntityLoader() {
|
||||
|
||||
@Override
|
||||
public NamedEntity get(final Long id) {
|
||||
final User user = DefaultUserManager.this.find(id);
|
||||
if (user != null) {
|
||||
return new NamedEntity() {
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return user.getName();
|
||||
}
|
||||
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedEntity get(String name) {
|
||||
final User user = find(name);
|
||||
if (user != null) {
|
||||
return new NamedEntity() {
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return user.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return user.getName();
|
||||
}
|
||||
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.pmease.gitop.core.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.persistence.Column;
|
||||
@ -78,4 +79,8 @@ public class MergeRequest extends AbstractEntity {
|
||||
this.updates = updates;
|
||||
}
|
||||
|
||||
public Collection<String> getTouchedFiles() {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -26,6 +26,8 @@ import com.pmease.gitop.core.model.permission.object.UserBelonging;
|
||||
usage=org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE)
|
||||
public class User extends AbstractUser implements ProtectedObject {
|
||||
|
||||
private String description;
|
||||
|
||||
@OneToMany(mappedBy="user")
|
||||
private Collection<TeamMembership> teamMemberships = new ArrayList<TeamMembership>();
|
||||
|
||||
@ -41,6 +43,14 @@ public class User extends AbstractUser implements ProtectedObject {
|
||||
@OneToMany(mappedBy="owner")
|
||||
private Collection<Team> teams = new ArrayList<Team>();
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Collection<TeamMembership> getTeamMemberships() {
|
||||
return teamMemberships;
|
||||
}
|
||||
@ -93,5 +103,5 @@ public class User extends AbstractUser implements ProtectedObject {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import com.pmease.commons.util.trimmable.Trimmable;
|
||||
public abstract class AbstractGateKeeper implements GateKeeper {
|
||||
|
||||
@Override
|
||||
public Trimmable trim() {
|
||||
public Trimmable trim(Object context) {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
package com.pmease.gitop.core.model.gatekeeper;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
|
||||
import com.pmease.gitop.core.model.MergeRequest;
|
||||
import com.pmease.gitop.core.model.permission.ObjectPermission;
|
||||
|
||||
public class AllowAuthorizedUsers extends AbstractGateKeeper {
|
||||
|
||||
@Override
|
||||
public CheckResult check(MergeRequest mergeRequest) {
|
||||
Collection<String> touchedFiles = mergeRequest.getTouchedFiles();
|
||||
if (!touchedFiles.isEmpty()) {
|
||||
for (String filePath: touchedFiles) {
|
||||
ObjectPermission requiredPermission = ObjectPermission.ofBranchWrite(
|
||||
mergeRequest.getTargetBranch().getRepository(),
|
||||
mergeRequest.getTargetBranch().getName(),
|
||||
filePath);
|
||||
if (!SecurityUtils.getSubject().isPermitted(requiredPermission))
|
||||
return CheckResult.REJECT;
|
||||
}
|
||||
|
||||
return CheckResult.ACCEPT;
|
||||
} else {
|
||||
ObjectPermission requiredPermission = ObjectPermission.ofBranchWrite(
|
||||
mergeRequest.getTargetBranch().getRepository(), mergeRequest.getTargetBranch().getName(), null);
|
||||
if (SecurityUtils.getSubject().isPermitted(requiredPermission))
|
||||
return CheckResult.ACCEPT;
|
||||
else
|
||||
return CheckResult.REJECT;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.pmease.gitop.core.model.gatekeeper;
|
||||
|
||||
import com.pmease.gitop.core.model.MergeRequest;
|
||||
|
||||
public class AllowRepositoryOwner extends AbstractGateKeeper {
|
||||
|
||||
@Override
|
||||
public CheckResult check(MergeRequest mergeRequest) {
|
||||
if (mergeRequest.getUser().getId().equals(mergeRequest.getTargetBranch().getRepository().getUser().getId()))
|
||||
return CheckResult.ACCEPT;
|
||||
else
|
||||
return CheckResult.REJECT;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package com.pmease.gitop.core.model.gatekeeper;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.pmease.commons.loader.AppLoader;
|
||||
import com.pmease.commons.util.namedentity.EntityLoader;
|
||||
import com.pmease.commons.util.namedentity.EntityMatcher;
|
||||
import com.pmease.commons.util.namedentity.EntityPatternSet;
|
||||
import com.pmease.commons.util.pattern.PatternSetMatcher;
|
||||
import com.pmease.commons.util.pattern.WildcardStringMatcher;
|
||||
import com.pmease.gitop.core.entitymanager.TeamManager;
|
||||
import com.pmease.gitop.core.model.MergeRequest;
|
||||
import com.pmease.gitop.core.model.Repository;
|
||||
import com.pmease.gitop.core.model.TeamMembership;
|
||||
|
||||
public class AllowSpecifiedTeams implements GateKeeper {
|
||||
|
||||
private String teamPatterns;
|
||||
|
||||
public String getTeamPatterns() {
|
||||
return teamPatterns;
|
||||
}
|
||||
|
||||
public void setTeamPatterns(String teamPatterns) {
|
||||
this.teamPatterns = teamPatterns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CheckResult check(MergeRequest mergeRequest) {
|
||||
Repository repository = mergeRequest.getTargetBranch().getRepository();
|
||||
EntityMatcher entityMatcher = new EntityMatcher(getEntityLoader(repository), new WildcardStringMatcher());
|
||||
PatternSetMatcher patternSetMatcher = new PatternSetMatcher(entityMatcher);
|
||||
|
||||
for (TeamMembership each: mergeRequest.getUser().getTeamMemberships()) {
|
||||
if (patternSetMatcher.matches(getTeamPatterns(), each.getTeam().getName()))
|
||||
return CheckResult.ACCEPT;
|
||||
}
|
||||
|
||||
return CheckResult.REJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object trim(Object context) {
|
||||
Preconditions.checkArgument(context instanceof Repository);
|
||||
|
||||
Repository repository = (Repository) context;
|
||||
EntityPatternSet patternSet = asEntityPatternSet(repository);
|
||||
patternSet.trim(repository);
|
||||
|
||||
if (patternSet.getStored().isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
setTeamPatterns(patternSet.toString());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private EntityLoader getEntityLoader(Repository repository) {
|
||||
TeamManager teamManager = AppLoader.getInstance(TeamManager.class);
|
||||
return teamManager.asEntityLoader(repository.getOwner());
|
||||
}
|
||||
|
||||
private EntityPatternSet asEntityPatternSet(Repository repository) {
|
||||
EntityLoader entityLoader = getEntityLoader(repository);
|
||||
return EntityPatternSet.fromStored(teamPatterns, entityLoader);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package com.pmease.gitop.core.model.gatekeeper;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.pmease.commons.loader.AppLoader;
|
||||
import com.pmease.commons.util.namedentity.EntityLoader;
|
||||
import com.pmease.commons.util.namedentity.EntityMatcher;
|
||||
import com.pmease.commons.util.namedentity.EntityPatternSet;
|
||||
import com.pmease.commons.util.pattern.PatternSetMatcher;
|
||||
import com.pmease.commons.util.pattern.WildcardStringMatcher;
|
||||
import com.pmease.gitop.core.entitymanager.UserManager;
|
||||
import com.pmease.gitop.core.model.MergeRequest;
|
||||
import com.pmease.gitop.core.model.Repository;
|
||||
|
||||
public class AllowSpecifiedUsers implements GateKeeper {
|
||||
|
||||
private String userPatterns;
|
||||
|
||||
public String getUserPatterns() {
|
||||
return userPatterns;
|
||||
}
|
||||
|
||||
public void setUserPatterns(String userPatterns) {
|
||||
this.userPatterns = userPatterns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CheckResult check(MergeRequest mergeRequest) {
|
||||
Repository repository = mergeRequest.getTargetBranch().getRepository();
|
||||
EntityMatcher entityMatcher = new EntityMatcher(getEntityLoader(repository), new WildcardStringMatcher());
|
||||
PatternSetMatcher patternSetMatcher = new PatternSetMatcher(entityMatcher);
|
||||
|
||||
if (patternSetMatcher.matches(getUserPatterns(), mergeRequest.getUser().getName()))
|
||||
return CheckResult.ACCEPT;
|
||||
else
|
||||
return CheckResult.REJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object trim(Object context) {
|
||||
Preconditions.checkArgument(context instanceof Repository);
|
||||
|
||||
Repository repository = (Repository) context;
|
||||
EntityPatternSet patternSet = asEntityPatternSet(repository);
|
||||
patternSet.trim(repository);
|
||||
|
||||
if (patternSet.getStored().isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
setUserPatterns(patternSet.toString());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private EntityLoader getEntityLoader(Repository repository) {
|
||||
UserManager userManager = AppLoader.getInstance(UserManager.class);
|
||||
return userManager.asEntityLoader();
|
||||
}
|
||||
|
||||
private EntityPatternSet asEntityPatternSet(Repository repository) {
|
||||
EntityLoader entityLoader = getEntityLoader(repository);
|
||||
return EntityPatternSet.fromStored(userPatterns, entityLoader);
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,7 +7,6 @@ import javax.validation.constraints.Size;
|
||||
|
||||
import com.pmease.commons.util.trimmable.AndOrConstruct;
|
||||
import com.pmease.commons.util.trimmable.TrimUtils;
|
||||
import com.pmease.commons.util.trimmable.Trimmable;
|
||||
import com.pmease.gitop.core.model.MergeRequest;
|
||||
|
||||
public class AndGateKeeper implements GateKeeper {
|
||||
@ -42,19 +41,20 @@ public class AndGateKeeper implements GateKeeper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trimmable trim() {
|
||||
public Object trim(Object context) {
|
||||
return TrimUtils.trim(new AndOrConstruct() {
|
||||
|
||||
@Override
|
||||
public Trimmable getSelf() {
|
||||
public Object getSelf() {
|
||||
return AndGateKeeper.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Trimmable> getMembers() {
|
||||
public List<?> getMembers() {
|
||||
return getGateKeepers();
|
||||
}
|
||||
});
|
||||
|
||||
}, context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ package com.pmease.gitop.core.model.gatekeeper;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.pmease.commons.util.trimmable.Trimmable;
|
||||
import com.pmease.gitop.core.model.MergeRequest;
|
||||
|
||||
public class NotGateKeeper implements GateKeeper {
|
||||
@ -31,8 +30,8 @@ public class NotGateKeeper implements GateKeeper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trimmable trim() {
|
||||
return gateKeeper.trim();
|
||||
public Object trim(Object context) {
|
||||
return gateKeeper.trim(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ public class OrGateKeeper implements GateKeeper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trimmable trim() {
|
||||
public Object trim(Object context) {
|
||||
return TrimUtils.trim(new AndOrConstruct() {
|
||||
|
||||
@Override
|
||||
@ -54,7 +54,8 @@ public class OrGateKeeper implements GateKeeper {
|
||||
public List<? extends Trimmable> getMembers() {
|
||||
return getGateKeepers();
|
||||
}
|
||||
});
|
||||
|
||||
}, context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.pmease.gitop.core.model.permission;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.shiro.authz.Permission;
|
||||
|
||||
import com.pmease.gitop.core.model.Repository;
|
||||
@ -89,11 +91,34 @@ public class ObjectPermission implements Permission {
|
||||
return new ObjectPermission(new ProtectedBranches(repository, branchName), new Read());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the permission to write anything in a branch.
|
||||
* <p>
|
||||
* @param repository
|
||||
* repository of the branch
|
||||
* @param branchName
|
||||
* name of the branch
|
||||
* @return
|
||||
* permission object
|
||||
*/
|
||||
public static ObjectPermission ofBranchWrite(Repository repository, String branchName) {
|
||||
return new ObjectPermission(new ProtectedBranches(repository, branchName), new WriteToBranch("**"));
|
||||
}
|
||||
|
||||
public static ObjectPermission ofBranchWrite(Repository repository, String branchName, String filePath) {
|
||||
/**
|
||||
* Construct the permission to write a branch with specified files.
|
||||
* <p>
|
||||
* @param repository
|
||||
* repository of the branch
|
||||
* @param branchName
|
||||
* name of the branch
|
||||
* @param filePath
|
||||
* path of file in the branch to be written. <i>null<i> to construct a branch write permission
|
||||
* only allowed to modify commit comment (commit amend) without touching any files.
|
||||
* @return
|
||||
* permission object
|
||||
*/
|
||||
public static ObjectPermission ofBranchWrite(Repository repository, String branchName, @Nullable String filePath) {
|
||||
return new ObjectPermission(new ProtectedBranches(repository, branchName), new WriteToBranch(filePath));
|
||||
}
|
||||
|
||||
|
||||
@ -1,12 +1,21 @@
|
||||
package com.pmease.gitop.core.model.permission.operation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.pmease.commons.util.pattern.WildcardUtils;
|
||||
|
||||
public class WriteToBranch implements PrivilegedOperation {
|
||||
|
||||
private final String filePaths;
|
||||
|
||||
public WriteToBranch(String filePaths) {
|
||||
/**
|
||||
* Construct branch write operation with specified file paths.
|
||||
* <p>
|
||||
* @param filePaths
|
||||
* null to indicate the operation will not touch any files
|
||||
*
|
||||
*/
|
||||
public WriteToBranch(@Nullable String filePaths) {
|
||||
this.filePaths = filePaths;
|
||||
}
|
||||
|
||||
@ -18,7 +27,12 @@ public class WriteToBranch implements PrivilegedOperation {
|
||||
public boolean can(PrivilegedOperation operation) {
|
||||
if (operation instanceof WriteToBranch) {
|
||||
WriteToBranch writeToBranch = (WriteToBranch) operation;
|
||||
return WildcardUtils.matchPath(getFilePaths(), writeToBranch.getFilePaths());
|
||||
if (writeToBranch.getFilePaths() == null)
|
||||
return true;
|
||||
else if (getFilePaths() == null)
|
||||
return false;
|
||||
else
|
||||
return WildcardUtils.matchPath(getFilePaths(), writeToBranch.getFilePaths());
|
||||
} else {
|
||||
return new Read().can(operation);
|
||||
}
|
||||
|
||||
@ -4,11 +4,5 @@
|
||||
<title>Product Home Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<div wicket:id="user"></div>
|
||||
<a wicket:id="login">login</a>
|
||||
<a wicket:id="logout">logout</a>
|
||||
<a wicket:id="check">check</a>
|
||||
|
||||
<a class="btn" href="#"><i class="icon-search"></i> Search</a>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,17 +1,10 @@
|
||||
package com.pmease.gitop.core.web;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authc.UsernamePasswordToken;
|
||||
import org.apache.wicket.markup.head.IHeaderResponse;
|
||||
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
|
||||
import org.apache.wicket.markup.html.WebPage;
|
||||
import org.apache.wicket.markup.html.basic.Label;
|
||||
import org.apache.wicket.markup.html.link.Link;
|
||||
import org.apache.wicket.model.AbstractReadOnlyModel;
|
||||
|
||||
import com.pmease.commons.security.SecurityHelper;
|
||||
import com.pmease.commons.web.asset.bootstrap.BootstrapResourceReference;
|
||||
import com.pmease.gitop.core.model.User;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class HomePage extends WebPage {
|
||||
@ -19,43 +12,6 @@ public class HomePage extends WebPage {
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
add(new Label("user", new AbstractReadOnlyModel<String>() {
|
||||
|
||||
@Override
|
||||
public String getObject() {
|
||||
return SecurityHelper.getUserDisplayName(User.class, "Guest");
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
add(new Link<Void>("login") {
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
SecurityUtils.getSubject().login(new UsernamePasswordToken("admin", "12345"));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
add(new Link<Void>("logout") {
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
SecurityUtils.getSubject().logout();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
add(new Link<Void>("check") {
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
SecurityUtils.getSubject().checkPermission("write");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -6,14 +6,11 @@ import java.util.Properties;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.shiro.authc.credential.PasswordService;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.bio.SocketConnector;
|
||||
import org.eclipse.jetty.server.ssl.SslSocketConnector;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hibernate.criterion.DetachedCriteria;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -23,10 +20,8 @@ import com.pmease.commons.jetty.FileAssetServlet;
|
||||
import com.pmease.commons.jetty.extensionpoints.ServerConfigurator;
|
||||
import com.pmease.commons.jetty.extensionpoints.ServletContextConfigurator;
|
||||
import com.pmease.commons.loader.AbstractPlugin;
|
||||
import com.pmease.commons.persistence.dao.GeneralDao;
|
||||
import com.pmease.commons.util.FileUtils;
|
||||
import com.pmease.commons.util.StringUtils;
|
||||
import com.pmease.gitop.core.model.User;
|
||||
|
||||
public class Product extends AbstractPlugin {
|
||||
|
||||
@ -36,15 +31,9 @@ public class Product extends AbstractPlugin {
|
||||
|
||||
public static final String PRODUCT_NAME = "Gitop";
|
||||
|
||||
private final GeneralDao generalDao;
|
||||
|
||||
private final PasswordService passwordService;
|
||||
|
||||
@Inject
|
||||
public Product(GeneralDao generalDao, PasswordService passwordService) {
|
||||
public Product() {
|
||||
serverProps = FileUtils.loadProperties(new File(Bootstrap.getConfDir(), "server.properties"));
|
||||
this.generalDao = generalDao;
|
||||
this.passwordService = passwordService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -115,16 +104,6 @@ public class Product extends AbstractPlugin {
|
||||
|
||||
@Override
|
||||
public void postStartDependents() {
|
||||
DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
|
||||
criteria.add(Restrictions.eq("loginName", "admin"));
|
||||
if (generalDao.find(criteria) == null) {
|
||||
User user = new User();
|
||||
user.setName("admin");
|
||||
user.setPasswordHash(passwordService.encryptPassword("12345"));
|
||||
user.setFullName("Administrator");
|
||||
generalDao.save(user);
|
||||
}
|
||||
|
||||
logger.info(PRODUCT_NAME + " has been started successfully.");
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user