From c658df9e87d65b08d5482cf04489cb0532ff83dd Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Sat, 13 Oct 2012 10:50:35 -0400 Subject: [PATCH] Merge branch 'master' of https://github.com/Mrbytes/gitblit --- src/com/gitblit/models/UserModel.java | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 300 insertions(+), 34 deletions(-) diff --git a/src/com/gitblit/models/UserModel.java b/src/com/gitblit/models/UserModel.java index 0ede878..8b3fe82 100644 --- a/src/com/gitblit/models/UserModel.java +++ b/src/com/gitblit/models/UserModel.java @@ -17,11 +17,16 @@ import java.io.Serializable; import java.security.Principal; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; +import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.AuthorizationControl; +import com.gitblit.Constants.Unused; +import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.StringUtils; /** @@ -36,6 +41,8 @@ private static final long serialVersionUID = 1L; + public static final UserModel ANONYMOUS = new UserModel(); + // field names are reflectively mapped in EditUser page public String username; public String password; @@ -44,8 +51,12 @@ public String emailAddress; public boolean canAdmin; public boolean canFork; + public boolean canCreate; public boolean excludeFromFederation; + // retained for backwards-compatibility with RPC clients + @Deprecated public final Set<String> repositories = new HashSet<String>(); + public final Map<String, AccessPermission> permissions = new HashMap<String, AccessPermission>(); public final Set<TeamModel> teams = new HashSet<TeamModel>(); // non-persisted fields @@ -54,6 +65,11 @@ public UserModel(String username) { this.username = username; this.isAuthenticated = true; + } + + private UserModel() { + this.username = "$anonymous"; + this.isAuthenticated = false; } /** @@ -65,64 +81,269 @@ */ @Deprecated public boolean canAccessRepository(String repositoryName) { - return canAdmin || repositories.contains(repositoryName.toLowerCase()) + return canAdmin() || repositories.contains(repositoryName.toLowerCase()) || hasTeamAccess(repositoryName); } + @Deprecated + @Unused public boolean canAccessRepository(RepositoryModel repository) { boolean isOwner = !StringUtils.isEmpty(repository.owner) && repository.owner.equals(username); boolean allowAuthenticated = isAuthenticated && AuthorizationControl.AUTHENTICATED.equals(repository.authorizationControl); - return canAdmin || isOwner || repositories.contains(repository.name.toLowerCase()) + return canAdmin() || isOwner || repositories.contains(repository.name.toLowerCase()) || hasTeamAccess(repository.name) || allowAuthenticated; } + @Deprecated + @Unused public boolean hasTeamAccess(String repositoryName) { for (TeamModel team : teams) { - if (team.hasRepository(repositoryName)) { + if (team.hasRepositoryPermission(repositoryName)) { return true; } } return false; } - public boolean canForkRepository(RepositoryModel repository) { + @Deprecated + @Unused + public boolean hasRepository(String name) { + return hasRepositoryPermission(name); + } + + @Deprecated + @Unused + public void addRepository(String name) { + addRepositoryPermission(name); + } + + @Deprecated + @Unused + public void removeRepository(String name) { + removeRepositoryPermission(name); + } + + /** + * Returns true if the user has any type of specified access permission for + * this repository. + * + * @param name + * @return true if user has a specified access permission for the repository + */ + public boolean hasRepositoryPermission(String name) { + String repository = AccessPermission.repositoryFromRole(name).toLowerCase(); + if (permissions.containsKey(repository)) { + // exact repository permission specified + return true; + } else { + // search for regex permission match + for (String key : permissions.keySet()) { + if (name.matches(key)) { + AccessPermission p = permissions.get(key); + if (p != null) { + return true; + } + } + } + } + return false; + } + + /** + * Adds a repository permission to the team. + * <p> + * Role may be formatted as: + * <ul> + * <li> myrepo.git <i>(this is implicitly RW+)</i> + * <li> RW+:myrepo.git + * </ul> + * @param role + */ + public void addRepositoryPermission(String role) { + AccessPermission permission = AccessPermission.permissionFromRole(role); + String repository = AccessPermission.repositoryFromRole(role).toLowerCase(); + repositories.add(repository); + permissions.put(repository, permission); + } + + public AccessPermission removeRepositoryPermission(String name) { + String repository = AccessPermission.repositoryFromRole(name).toLowerCase(); + repositories.remove(repository); + return permissions.remove(repository); + } + + public void setRepositoryPermission(String repository, AccessPermission permission) { + permissions.put(repository.toLowerCase(), permission); + } + + public AccessPermission getRepositoryPermission(RepositoryModel repository) { + if (canAdmin() || repository.isOwner(username) || repository.isUsersPersonalRepository(username)) { + return AccessPermission.REWIND; + } + if (AuthorizationControl.AUTHENTICATED.equals(repository.authorizationControl) && isAuthenticated) { + // AUTHENTICATED is a shortcut for authorizing all logged-in users RW access + return AccessPermission.REWIND; + } + + // determine best permission available based on user's personal permissions + // and the permissions of teams of which the user belongs + AccessPermission permission = AccessPermission.NONE; + if (permissions.containsKey(repository.name.toLowerCase())) { + // exact repository permission specified + AccessPermission p = permissions.get(repository.name.toLowerCase()); + if (p != null) { + permission = p; + } + } else { + // search for regex permission match + for (String key : permissions.keySet()) { + if (repository.name.matches(key)) { + AccessPermission p = permissions.get(key); + if (p != null) { + permission = p; + } + } + } + } + + for (TeamModel team : teams) { + AccessPermission p = team.getRepositoryPermission(repository); + if (permission == null || p.exceeds(permission)) { + // use team permission + permission = p; + } + } + return permission; + } + + private boolean canAccess(RepositoryModel repository, AccessRestrictionType ifRestriction, AccessPermission requirePermission) { + if (repository.accessRestriction.atLeast(ifRestriction)) { + AccessPermission permission = getRepositoryPermission(repository); + return permission.atLeast(requirePermission); + } + return true; + } + + public boolean canView(RepositoryModel repository) { + return canAccess(repository, AccessRestrictionType.VIEW, AccessPermission.VIEW); + } + + public boolean canClone(RepositoryModel repository) { + return canAccess(repository, AccessRestrictionType.CLONE, AccessPermission.CLONE); + } + + public boolean canPush(RepositoryModel repository) { + if (repository.isFrozen) { + return false; + } + return canAccess(repository, AccessRestrictionType.PUSH, AccessPermission.PUSH); + } + + public boolean canCreateRef(RepositoryModel repository) { + if (repository.isFrozen) { + return false; + } + return canAccess(repository, AccessRestrictionType.PUSH, AccessPermission.CREATE); + } + + public boolean canDeleteRef(RepositoryModel repository) { + if (repository.isFrozen) { + return false; + } + return canAccess(repository, AccessRestrictionType.PUSH, AccessPermission.DELETE); + } + + public boolean canRewindRef(RepositoryModel repository) { + if (repository.isFrozen) { + return false; + } + return canAccess(repository, AccessRestrictionType.PUSH, AccessPermission.REWIND); + } + + public boolean canFork(RepositoryModel repository) { + if (repository.isUsersPersonalRepository(username)) { + // can not fork your own repository + return false; + } + if (canAdmin() || repository.isOwner(username)) { + return true; + } + if (!repository.allowForks) { + return false; + } + if (!isAuthenticated || !canFork()) { + return false; + } + return canClone(repository); + } + + public boolean canDelete(RepositoryModel model) { + return canAdmin() || model.isUsersPersonalRepository(username); + } + + public boolean canEdit(RepositoryModel model) { + return canAdmin() || model.isUsersPersonalRepository(username) || model.isOwner(username); + } + + /** + * This returns true if the user has fork privileges or the user has fork + * privileges because of a team membership. + * + * @return true if the user can fork + */ + public boolean canFork() { + if (canFork) { + return true; + } + if (!ArrayUtils.isEmpty(teams)) { + for (TeamModel team : teams) { + if (team.canFork) { + return true; + } + } + } + return false; + } + + /** + * This returns true if the user has admin privileges or the user has admin + * privileges because of a team membership. + * + * @return true if the user can admin + */ + public boolean canAdmin() { if (canAdmin) { return true; } - if (!canFork) { - // user has been prohibited from forking - return false; + if (!ArrayUtils.isEmpty(teams)) { + for (TeamModel team : teams) { + if (team.canAdmin) { + return true; + } + } } - if (!isAuthenticated) { - // unauthenticated user model - return false; - } - if (("~" + username).equalsIgnoreCase(repository.projectPath)) { - // this repository is already a personal repository - return false; - } - if (!repository.allowForks) { - // repository prohibits forks - return false; - } - if (repository.accessRestriction.atLeast(AccessRestrictionType.CLONE)) { - return canAccessRepository(repository); - } - // repository is not clone-restricted - return true; + return false; } - public boolean hasRepository(String name) { - return repositories.contains(name.toLowerCase()); - } - - public void addRepository(String name) { - repositories.add(name.toLowerCase()); - } - - public void removeRepository(String name) { - repositories.remove(name.toLowerCase()); + /** + * This returns true if the user has create privileges or the user has create + * privileges because of a team membership. + * + * @return true if the user can admin + */ + public boolean canCreate() { + if (canCreate) { + return true; + } + if (!ArrayUtils.isEmpty(teams)) { + for (TeamModel team : teams) { + if (team.canCreate) { + return true; + } + } + } + return false; } public boolean isTeamMember(String teamname) { @@ -157,6 +378,23 @@ } return displayName; } + + public String getPersonalPath() { + return "~" + username; + } + + @Override + public int hashCode() { + return username.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof UserModel) { + return username.equals(((UserModel) o).username); + } + return false; + } @Override public String toString() { @@ -167,4 +405,32 @@ public int compareTo(UserModel o) { return username.compareTo(o.username); } + + /** + * Returns true if the name/email pair match this user account. + * + * @param name + * @param email + * @return true, if the name and email address match this account + */ + public boolean is(String name, String email) { + // at a minimum a usename or display name must be supplied + if (StringUtils.isEmpty(name)) { + return false; + } + boolean nameVerified = name.equalsIgnoreCase(username) || name.equalsIgnoreCase(getDisplayName()); + boolean emailVerified = false; + if (StringUtils.isEmpty(emailAddress)) { + // user account has not specified an email address + // rely on username/displayname verification + emailVerified = true; + } else { + // user account has specified an email address + // require email address verification + if (!StringUtils.isEmpty(email)) { + emailVerified = email.equalsIgnoreCase(emailAddress); + } + } + return nameVerified && emailVerified; + } } -- Gitblit v1.9.1