From 27ae9095639bb228a1b7ff86a3ebe4264abf05be Mon Sep 17 00:00:00 2001
From: mschaefers <mschaefers@scoop-gmbh.de>
Date: Thu, 29 Nov 2012 12:33:09 -0500
Subject: [PATCH] feature: when using LdapUserService one can configure Gitblit to fetch all users from ldap that can possibly login. This allows to see newly generated LDAP users instantly in Gitblit. By now an LDAP user had to log in once to appear in GitBlit.

---
 src/com/gitblit/client/GitblitClient.java |  520 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 504 insertions(+), 16 deletions(-)

diff --git a/src/com/gitblit/client/GitblitClient.java b/src/com/gitblit/client/GitblitClient.java
index 761283e..1101cd6 100644
--- a/src/com/gitblit/client/GitblitClient.java
+++ b/src/com/gitblit/client/GitblitClient.java
@@ -19,18 +19,38 @@
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
 
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccessPermission;
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.Constants.AuthorizationControl;
+import com.gitblit.Constants.PermissionType;
+import com.gitblit.Constants.RegistrantType;
 import com.gitblit.GitBlitException.ForbiddenException;
+import com.gitblit.GitBlitException.NotAllowedException;
 import com.gitblit.GitBlitException.UnauthorizedException;
+import com.gitblit.GitBlitException.UnknownRequestException;
 import com.gitblit.Keys;
 import com.gitblit.models.FederationModel;
+import com.gitblit.models.FeedEntryModel;
+import com.gitblit.models.FeedModel;
+import com.gitblit.models.RegistrantAccessPermission;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.ServerSettings;
 import com.gitblit.models.ServerStatus;
+import com.gitblit.models.TeamModel;
 import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
 import com.gitblit.utils.RpcUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.SyndicationUtils;
 
 /**
  * GitblitClient is a object that retrieves data from a Gitblit server, caches
@@ -43,11 +63,17 @@
 
 	private static final long serialVersionUID = 1L;
 
+	private static final Date NEVER = new Date(0);
+
+	protected final GitblitRegistration reg;
+
 	public final String url;
 
 	public final String account;
 
 	private final char[] password;
+
+	private volatile int protocolVersion;
 
 	private volatile boolean allowManagement;
 
@@ -59,42 +85,72 @@
 
 	private final List<UserModel> allUsers;
 
+	private final List<TeamModel> allTeams;
+
 	private final List<FederationModel> federationRegistrations;
+
+	private final List<FeedModel> availableFeeds;
+
+	private final List<FeedEntryModel> syndicatedEntries;
+
+	private final Set<String> subscribedRepositories;
 
 	private ServerStatus status;
 
-	public GitblitClient(String url, String account, char[] password) {
-		this.url = url;
-		this.account = account;
-		this.password = password;
+	public GitblitClient(GitblitRegistration reg) {
+		this.reg = reg;
+		this.url = reg.url;
+		this.account = reg.account;
+		this.password = reg.password;
 
 		this.allUsers = new ArrayList<UserModel>();
+		this.allTeams = new ArrayList<TeamModel>();
 		this.allRepositories = new ArrayList<RepositoryModel>();
 		this.federationRegistrations = new ArrayList<FederationModel>();
+		this.availableFeeds = new ArrayList<FeedModel>();
+		this.syndicatedEntries = new ArrayList<FeedEntryModel>();
+		this.subscribedRepositories = new HashSet<String>();
 	}
 
 	public void login() throws IOException {
+		protocolVersion = RpcUtils.getProtocolVersion(url, account, password);
+		refreshSettings();
+		refreshAvailableFeeds();
 		refreshRepositories();
+		refreshSubscribedFeeds(0);
 
 		try {
+			// credentials may not have administrator access
+			// or server may have disabled rpc management
 			refreshUsers();
+			if (protocolVersion > 1) {
+				refreshTeams();
+			}
 			allowManagement = true;
 		} catch (UnauthorizedException e) {
 		} catch (ForbiddenException e) {
+		} catch (NotAllowedException e) {
+		} catch (UnknownRequestException e) {
 		} catch (IOException e) {
-			System.err.println(e.getMessage());
+			e.printStackTrace();
 		}
 
 		try {
-			refreshSettings();
+			// credentials may not have administrator access
+			// or server may have disabled rpc administration
 			refreshStatus();
 			allowAdministration = true;
 		} catch (UnauthorizedException e) {
 		} catch (ForbiddenException e) {
+		} catch (NotAllowedException e) {
+		} catch (UnknownRequestException e) {
 		} catch (IOException e) {
-			System.err.println(e.getMessage());
+			e.printStackTrace();
 		}
+	}
 
+	public int getProtocolVersion() {
+		return protocolVersion;
 	}
 
 	public boolean allowManagement() {
@@ -107,6 +163,156 @@
 
 	public boolean isOwner(RepositoryModel model) {
 		return account != null && account.equalsIgnoreCase(model.owner);
+	}
+
+	public String getURL(String action, String repository, String objectId) {
+		boolean mounted = settings.get(Keys.web.mountParameters).getBoolean(true);
+		StringBuilder sb = new StringBuilder();
+		sb.append(url);
+		sb.append('/');
+		sb.append(action);
+		sb.append('/');
+		if (mounted) {
+			// mounted url/action/repository/objectId
+			sb.append(StringUtils.encodeURL(repository));
+			if (!StringUtils.isEmpty(objectId)) {
+				sb.append('/');
+				sb.append(objectId);
+			}
+			return sb.toString();
+		} else {
+			// parameterized url/action/&r=repository&h=objectId
+			sb.append("?r=");
+			sb.append(repository);
+			if (!StringUtils.isEmpty(objectId)) {
+				sb.append("&h=");
+				sb.append(objectId);
+			}
+			return sb.toString();
+		}
+	}
+	
+	public AccessRestrictionType getDefaultAccessRestriction() {
+		String restriction = null;
+		if (settings.hasKey(Keys.git.defaultAccessRestriction)) {
+			restriction = settings.get(Keys.git.defaultAccessRestriction).currentValue;
+		}
+		return AccessRestrictionType.fromName(restriction);
+	}
+
+	public AuthorizationControl getDefaultAuthorizationControl() {
+		String authorization = null;
+		if (settings.hasKey(Keys.git.defaultAuthorizationControl)) {
+			authorization = settings.get(Keys.git.defaultAuthorizationControl).currentValue;
+		}
+		return AuthorizationControl.fromName(authorization);
+	}
+
+	/**
+	 * Returns the list of pre-receive scripts the repository inherited from the
+	 * global settings and team affiliations.
+	 * 
+	 * @param repository
+	 *            if null only the globally specified scripts are returned
+	 * @return a list of scripts
+	 */
+	public List<String> getPreReceiveScriptsInherited(RepositoryModel repository) {
+		Set<String> scripts = new LinkedHashSet<String>();
+		// Globals
+		for (String script : settings.get(Keys.groovy.preReceiveScripts).getStrings()) {
+			if (script.endsWith(".groovy")) {
+				scripts.add(script.substring(0, script.lastIndexOf('.')));
+			} else {
+				scripts.add(script);
+			}
+		}
+
+		// Team Scripts
+		if (repository != null) {
+			for (String teamname : getPermittedTeamnames(repository)) {
+				TeamModel team = getTeamModel(teamname);
+				if (!ArrayUtils.isEmpty(team.preReceiveScripts)) {
+					scripts.addAll(team.preReceiveScripts);
+				}
+			}
+		}
+		return new ArrayList<String>(scripts);
+	}
+
+	/**
+	 * Returns the list of all available Groovy pre-receive push hook scripts
+	 * that are not already inherited by the repository. Script files must have
+	 * .groovy extension
+	 * 
+	 * @param repository
+	 *            optional parameter
+	 * @return list of available hook scripts
+	 */
+	public List<String> getPreReceiveScriptsUnused(RepositoryModel repository) {
+		Set<String> inherited = new TreeSet<String>(getPreReceiveScriptsInherited(repository));
+
+		// create list of available scripts by excluding inherited scripts
+		List<String> scripts = new ArrayList<String>();
+		for (String script : settings.pushScripts) {
+			if (!inherited.contains(script)) {
+				scripts.add(script);
+			}
+		}
+		return scripts;
+	}
+
+	/**
+	 * Returns the list of post-receive scripts the repository inherited from
+	 * the global settings and team affiliations.
+	 * 
+	 * @param repository
+	 *            if null only the globally specified scripts are returned
+	 * @return a list of scripts
+	 */
+	public List<String> getPostReceiveScriptsInherited(RepositoryModel repository) {
+		Set<String> scripts = new LinkedHashSet<String>();
+		// Global Scripts
+		for (String script : settings.get(Keys.groovy.postReceiveScripts).getStrings()) {
+			if (script.endsWith(".groovy")) {
+				scripts.add(script.substring(0, script.lastIndexOf('.')));
+			} else {
+				scripts.add(script);
+			}
+		}
+		// Team Scripts
+		if (repository != null) {
+			for (String teamname : getPermittedTeamnames(repository)) {
+				TeamModel team = getTeamModel(teamname);
+				if (!ArrayUtils.isEmpty(team.postReceiveScripts)) {
+					scripts.addAll(team.postReceiveScripts);
+				}
+			}
+		}
+		return new ArrayList<String>(scripts);
+	}
+
+	/**
+	 * Returns the list of unused Groovy post-receive push hook scripts that are
+	 * not already inherited by the repository. Script files must have .groovy
+	 * extension
+	 * 
+	 * @param repository
+	 *            optional parameter
+	 * @return list of available hook scripts
+	 */
+	public List<String> getPostReceiveScriptsUnused(RepositoryModel repository) {
+		Set<String> inherited = new TreeSet<String>(getPostReceiveScriptsInherited(repository));
+
+		// create list of available scripts by excluding inherited scripts
+		List<String> scripts = new ArrayList<String>();
+		if (!ArrayUtils.isEmpty(settings.pushScripts)) {			
+			for (String script : settings.pushScripts) {
+				if (!inherited.contains(script)) {
+					scripts.add(script);
+				}
+			}
+		}
+		return scripts;
 	}
 
 	public ServerSettings getSettings() {
@@ -127,6 +333,7 @@
 		allRepositories.clear();
 		allRepositories.addAll(repositories.values());
 		Collections.sort(allRepositories);
+		markSubscribedFeeds();
 		return allRepositories;
 	}
 
@@ -134,17 +341,131 @@
 		List<UserModel> users = RpcUtils.getUsers(url, account, password);
 		allUsers.clear();
 		allUsers.addAll(users);
+		Collections.sort(users);
 		return allUsers;
+	}
+
+	public List<TeamModel> refreshTeams() throws IOException {
+		List<TeamModel> teams = RpcUtils.getTeams(url, account, password);
+		allTeams.clear();
+		allTeams.addAll(teams);
+		Collections.sort(teams);
+		return allTeams;
 	}
 
 	public ServerSettings refreshSettings() throws IOException {
 		settings = RpcUtils.getSettings(url, account, password);
 		return settings;
 	}
-	
+
 	public ServerStatus refreshStatus() throws IOException {
 		status = RpcUtils.getStatus(url, account, password);
 		return status;
+	}
+
+	public List<String> getBranches(String repository) {
+		List<FeedModel> feeds = getAvailableFeeds(repository);
+		List<String> branches = new ArrayList<String>();
+		for (FeedModel feed : feeds) {
+			branches.add(feed.branch);
+		}
+		Collections.sort(branches);
+		return branches;
+	}
+
+	public List<FeedModel> getAvailableFeeds() {
+		return availableFeeds;
+	}
+
+	public List<FeedModel> getAvailableFeeds(RepositoryModel repository) {
+		return getAvailableFeeds(repository.name);
+	}
+
+	public List<FeedModel> getAvailableFeeds(String repository) {
+		List<FeedModel> repositoryFeeds = new ArrayList<FeedModel>();
+		if (repository == null) {
+			return repositoryFeeds;
+		}
+		for (FeedModel feed : availableFeeds) {
+			if (feed.repository.equalsIgnoreCase(repository)) {
+				repositoryFeeds.add(feed);
+			}
+		}
+		return repositoryFeeds;
+	}
+
+	public List<FeedModel> refreshAvailableFeeds() throws IOException {
+		List<FeedModel> feeds = RpcUtils.getBranchFeeds(url, account, password);
+		availableFeeds.clear();
+		availableFeeds.addAll(feeds);
+		markSubscribedFeeds();
+		return availableFeeds;
+	}
+
+	public List<FeedEntryModel> refreshSubscribedFeeds(int page) throws IOException {
+		Set<FeedEntryModel> allEntries = new HashSet<FeedEntryModel>();
+		if (reg.feeds.size() > 0) {
+			for (FeedModel feed : reg.feeds) {
+				feed.lastRefreshDate = feed.currentRefreshDate;
+				feed.currentRefreshDate = new Date();
+				List<FeedEntryModel> entries = SyndicationUtils.readFeed(url, feed.repository,
+						feed.branch, -1, page, account, password);
+				allEntries.addAll(entries);
+			}
+		}
+		reg.cacheFeeds();
+		syndicatedEntries.clear();
+		syndicatedEntries.addAll(allEntries);
+		Collections.sort(syndicatedEntries);
+		return syndicatedEntries;
+	}
+
+	public void updateSubscribedFeeds(List<FeedModel> list) {
+		reg.updateSubscribedFeeds(list);
+		markSubscribedFeeds();
+	}
+
+	private void markSubscribedFeeds() {
+		subscribedRepositories.clear();
+		for (FeedModel feed : availableFeeds) {
+			// mark feed in the available list as subscribed
+			feed.subscribed = reg.feeds.contains(feed);
+			if (feed.subscribed) {
+				subscribedRepositories.add(feed.repository.toLowerCase());
+			}
+		}
+	}
+
+	public Date getLastFeedRefresh(String repository, String branch) {
+		FeedModel feed = new FeedModel();
+		feed.repository = repository;
+		feed.branch = branch;
+		if (reg.feeds.contains(feed)) {
+			int idx = reg.feeds.indexOf(feed);
+			feed = reg.feeds.get(idx);
+			return feed.lastRefreshDate;
+		}
+		return NEVER;
+	}
+
+	public boolean isSubscribed(RepositoryModel repository) {
+		return subscribedRepositories.contains(repository.name.toLowerCase());
+	}
+
+	public List<FeedEntryModel> getSyndicatedEntries() {
+		return syndicatedEntries;
+	}
+
+	public List<FeedEntryModel> log(String repository, String branch, int numberOfEntries, int page)
+			throws IOException {
+		return SyndicationUtils.readFeed(url, repository, branch, numberOfEntries, page, account,
+				password);
+	}
+
+	public List<FeedEntryModel> search(String repository, String branch, String fragment,
+			Constants.SearchType type, int numberOfEntries, int page) throws IOException {
+		return SyndicationUtils.readSearchFeed(url, repository, branch, fragment, type,
+				numberOfEntries, page, account, password);
 	}
 
 	public List<FederationModel> refreshFederationRegistrations() throws IOException {
@@ -156,6 +477,15 @@
 
 	public List<UserModel> getUsers() {
 		return allUsers;
+	}
+	
+	public UserModel getUser(String username) {
+		for (UserModel user : getUsers()) {
+			if (user.username.equalsIgnoreCase(username)) {
+				return user;
+			}
+		}
+		return null;
 	}
 
 	public List<String> getUsernames() {
@@ -170,11 +500,123 @@
 	public List<String> getPermittedUsernames(RepositoryModel repository) {
 		List<String> usernames = new ArrayList<String>();
 		for (UserModel user : this.allUsers) {
-			if (user.repositories.contains(repository.name)) {
+			if (user.hasRepositoryPermission(repository.name)) {
 				usernames.add(user.username);
 			}
 		}
 		return usernames;
+	}
+	
+	/**
+	 * Returns the effective list of permissions for this user, taking into account
+	 * team memberships, ownerships.
+	 * 
+	 * @param user
+	 * @return the effective list of permissions for the user
+	 */
+	public List<RegistrantAccessPermission> getUserAccessPermissions(UserModel user) {
+		Set<RegistrantAccessPermission> set = new LinkedHashSet<RegistrantAccessPermission>();
+		set.addAll(user.getRepositoryPermissions());
+		// Flag missing repositories
+		for (RegistrantAccessPermission permission : set) {
+			if (permission.mutable && PermissionType.EXPLICIT.equals(permission.permissionType)) {
+				RepositoryModel rm = getRepository(permission.registrant);
+				if (rm == null) {
+					permission.permissionType = PermissionType.MISSING;
+					permission.mutable = false;
+					continue;
+				}
+			}
+		}
+
+		// TODO reconsider ownership as a user property
+		// manually specify personal repository ownerships
+		for (RepositoryModel rm : allRepositories) {
+			if (rm.isUsersPersonalRepository(user.username) || rm.isOwner(user.username)) {
+				RegistrantAccessPermission rp = new RegistrantAccessPermission(rm.name, AccessPermission.REWIND,
+						PermissionType.OWNER, RegistrantType.REPOSITORY, null, false);
+				// user may be owner of a repository to which they've inherited
+				// a team permission, replace any existing perm with owner perm
+				set.remove(rp);
+				set.add(rp);
+			}
+		}
+		
+		List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>(set);
+		Collections.sort(list);
+		return list;
+	}
+	
+	public List<RegistrantAccessPermission> getUserAccessPermissions(RepositoryModel repository) {
+		List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>();
+		if (AccessRestrictionType.NONE.equals(repository.accessRestriction)) {
+			// no permissions needed, REWIND for everyone!
+			return list;
+		}
+		if (AuthorizationControl.AUTHENTICATED.equals(repository.authorizationControl)) {
+			// no permissions needed, REWIND for authenticated!
+			return list;
+		}
+		// NAMED users and teams
+		for (UserModel user : allUsers) {
+			RegistrantAccessPermission ap = user.getRepositoryPermission(repository);
+			if (ap.permission.exceeds(AccessPermission.NONE)) {
+				list.add(ap);
+			}
+		}
+		return list;
+	}
+
+	public boolean setUserAccessPermissions(RepositoryModel repository, List<RegistrantAccessPermission> permissions) throws IOException {
+		return RpcUtils.setRepositoryMemberPermissions(repository, permissions, url, account, password);
+	}
+
+	public List<TeamModel> getTeams() {
+		return allTeams;
+	}
+
+	public List<String> getTeamnames() {
+		List<String> teamnames = new ArrayList<String>();
+		for (TeamModel team : this.allTeams) {
+			teamnames.add(team.name);
+		}
+		Collections.sort(teamnames);
+		return teamnames;
+	}
+
+	public List<String> getPermittedTeamnames(RepositoryModel repository) {
+		List<String> teamnames = new ArrayList<String>();
+		for (TeamModel team : this.allTeams) {
+			if (team.hasRepositoryPermission(repository.name)) {
+				teamnames.add(team.name);
+			}
+		}
+		return teamnames;
+	}
+	
+	public List<RegistrantAccessPermission> getTeamAccessPermissions(RepositoryModel repository) {
+		List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>();
+		for (TeamModel team : allTeams) {
+			RegistrantAccessPermission ap = team.getRepositoryPermission(repository);
+			if (ap.permission.exceeds(AccessPermission.NONE)) {
+				list.add(ap);
+			}
+		}
+		Collections.sort(list);
+		return list;
+	}
+
+	public boolean setTeamAccessPermissions(RepositoryModel repository, List<RegistrantAccessPermission> permissions) throws IOException {
+		return RpcUtils.setRepositoryTeamPermissions(repository, permissions, url, account, password);
+	}
+
+	public TeamModel getTeamModel(String name) {
+		for (TeamModel team : allTeams) {
+			if (team.name.equalsIgnoreCase(name)) {
+				return team;
+			}
+		}
+		return null;
 	}
 
 	public List<String> getFederationSets() {
@@ -184,31 +626,65 @@
 	public List<RepositoryModel> getRepositories() {
 		return allRepositories;
 	}
+	
+	public RepositoryModel getRepository(String name) {
+		for (RepositoryModel repository : allRepositories) {
+			if (repository.name.equalsIgnoreCase(name)) {
+				return repository;
+			}
+		}
+		return null;
+	}
 
-	public boolean createRepository(RepositoryModel repository, List<String> permittedUsers)
+	public boolean createRepository(RepositoryModel repository, List<RegistrantAccessPermission> userPermissions)
 			throws IOException {
+		return createRepository(repository, userPermissions, null);
+	}
+
+	public boolean createRepository(RepositoryModel repository, List<RegistrantAccessPermission> userPermissions,
+			List<RegistrantAccessPermission> teamPermissions) throws IOException {
 		boolean success = true;
 		success &= RpcUtils.createRepository(repository, url, account, password);
-		if (permittedUsers.size() > 0) {
+		if (userPermissions != null && userPermissions.size() > 0) {
 			// if new repository has named members, set them
-			success &= RpcUtils.setRepositoryMembers(repository, permittedUsers, url, account,
+			success &= RpcUtils.setRepositoryMemberPermissions(repository, userPermissions, url, account,
+					password);
+		}
+		if (teamPermissions != null && teamPermissions.size() > 0) {
+			// if new repository has named teams, set them
+			success &= RpcUtils.setRepositoryTeamPermissions(repository, teamPermissions, url, account,
 					password);
 		}
 		return success;
 	}
 
 	public boolean updateRepository(String name, RepositoryModel repository,
-			List<String> permittedUsers) throws IOException {
+			List<RegistrantAccessPermission> userPermissions) throws IOException {
+		return updateRepository(name, repository, userPermissions, null);
+	}
+
+	public boolean updateRepository(String name, RepositoryModel repository,
+			List<RegistrantAccessPermission> userPermissions,	List<RegistrantAccessPermission> teamPermissions) throws IOException {
 		boolean success = true;
 		success &= RpcUtils.updateRepository(name, repository, url, account, password);
-		// always set the repository members
-		success &= RpcUtils
-				.setRepositoryMembers(repository, permittedUsers, url, account, password);
+		// set the repository members
+		if (userPermissions != null) {
+			success &= RpcUtils.setRepositoryMemberPermissions(repository, userPermissions, url, account,
+					password);
+		}
+		if (teamPermissions != null) {
+			success &= RpcUtils.setRepositoryTeamPermissions(repository, teamPermissions, url, account,
+					password);
+		}
 		return success;
 	}
 
 	public boolean deleteRepository(RepositoryModel repository) throws IOException {
 		return RpcUtils.deleteRepository(repository, url, account, password);
+	}
+	
+	public boolean clearRepositoryCache() throws IOException {
+		return RpcUtils.clearRepositoryCache(url, account, password);
 	}
 
 	public boolean createUser(UserModel user) throws IOException {
@@ -223,6 +699,18 @@
 		return RpcUtils.deleteUser(user, url, account, password);
 	}
 
+	public boolean createTeam(TeamModel team) throws IOException {
+		return RpcUtils.createTeam(team, url, account, password);
+	}
+
+	public boolean updateTeam(String name, TeamModel team) throws IOException {
+		return RpcUtils.updateTeam(name, team, url, account, password);
+	}
+
+	public boolean deleteTeam(TeamModel team) throws IOException {
+		return RpcUtils.deleteTeam(team, url, account, password);
+	}
+
 	public boolean updateSettings(Map<String, String> newSettings) throws IOException {
 		return RpcUtils.updateSettings(newSettings, url, account, password);
 	}

--
Gitblit v1.9.1