Several gate keepers.

This commit is contained in:
robin shine 2013-08-18 15:53:04 +08:00
parent 9a79414fa3
commit 58348f4c5c
30 changed files with 506 additions and 148 deletions

View File

@ -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() {

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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();
}

View File

@ -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();

View File

@ -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();
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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

View File

@ -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());

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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;
}
}
};
}
}

View File

@ -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;
}
}
};
}
}

View File

@ -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>();
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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>

View File

@ -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

View File

@ -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.");
}