You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

www.github.com/gitblit/gitblit.git

Package Overview
Dependencies
Versions
37
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

www.github.com/gitblit/gitblit.git - go Package Compare versions

Comparing version
v0.5.2
to
v0.6.0
+83
distrib/federation.properties
#
# Git Repository Settings
#
# Base folder for repositories
# Use forward slashes even on Windows!!
# e.g. c:/gitrepos
#
# SINCE 0.5.0
# RESTART REQUIRED
git.repositoriesFolder = git
# Search the repositories folder subfolders for other repositories.
# Repositories MAY NOT be nested (i.e. one repository within another)
# but they may be grouped together in subfolders.
# e.g. c:/gitrepos/libraries/mylibrary.git
# c:/gitrepos/libraries/myotherlibrary.git
#
# SINCE 0.5.0
git.searchRepositoriesSubfolders = true
# Your federation name is used for federation status acknowledgments. If it is
# unset, and you elect to send a status acknowledgment, your Gitblit instance
# will be identified by its hostname, if available, else your internal ip address.
# The source Gitblit instance will also append your external IP address to your
# identification to differentiate multiple pulling systems behind a single proxy.
#
# SINCE 0.6.0
federation.name =
# Federation pull registrations
# Registrations are read once, at startup.
#
# RESTART REQUIRED
#
# frequency:
# The shortest frequency allowed is every 5 minutes
# Decimal frequency values are cast to integers
# Frequency values may be specified in mins, hours, or days
# Values that can not be parsed or are unspecified default to *federation.defaultFrequency*
#
# folder:
# if unspecified, the folder is *git.repositoriesFolder*
# if specified, the folder is relative to *git.repositoriesFolder*
#
# bare:
# if true, each repository will be created as a *bare* repository and will not
# have a working directory.
#
# if false, each repository will be created as a normal repository suitable
# for local work.
#
# mirror:
# if true, each repository HEAD is reset to *origin/master* after each pull.
# The repository will be flagged *isFrozen* after the initial clone.
#
# if false, each repository HEAD will point to the FETCH_HEAD of the initial
# clone from the origin until pushed to or otherwise manipulated.
#
# mergeAccounts:
# if true, remote accounts and their permissions are merged into your
# users.properties file
#
# notifyOnError:
# if true and the mail configuration is properly set, administrators will be
# notified by email of pull failures
#
# include and exclude:
# Space-delimited list of repositories to include or exclude from pull
# may be * wildcard to include or exclude all
# may use fuzzy match (e.g. org.eclipse.*)
#
# (Nearly) Perfect Mirror example
#
#federation.example1.url = https://go.gitblit.com
#federation.example1.token = 6f3b8a24bf970f17289b234284c94f43eb42f0e4
#federation.example1.frequency = 120 mins
#federation.example1.folder =
#federation.example1.bare = true
#federation.example1.mirror = true
#federation.example1.mergeAccounts = true

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
/**
* The DownloadZipFilter is an AccessRestrictionFilter which ensures that zip
* requests for view-restricted repositories have proper authentication
* credentials and are authorized.
*
* @author James Moger
*
*/
public class DownloadZipFilter extends AccessRestrictionFilter {
/**
* Extract the repository name from the url.
*
* @param url
* @return repository name
*/
@Override
protected String extractRepositoryName(String url) {
int a = url.indexOf("r=");
String repository = url.substring(a + 2);
if (repository.indexOf('&') > -1) {
repository = repository.substring(0, repository.indexOf('&'));
}
return repository;
}
/**
* Analyze the url and returns the action of the request.
*
* @param url
* @return action of the request
*/
@Override
protected String getUrlRequestAction(String url) {
return "DOWNLOAD";
}
/**
* Determine if the repository requires authentication.
*
* @param repository
* @return true if authentication required
*/
@Override
protected boolean requiresAuthentication(RepositoryModel repository) {
return repository.accessRestriction.atLeast(AccessRestrictionType.VIEW);
}
/**
* Determine if the user can access the repository and perform the specified
* action.
*
* @param repository
* @param user
* @param action
* @return true if user may execute the action on the repository
*/
@Override
protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {
return user.canAccessRepository(repository.name);
}
}
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.Parameters;
import com.gitblit.models.FederationModel;
import com.gitblit.utils.FederationUtils;
import com.gitblit.utils.StringUtils;
/**
* Command-line client to pull federated Gitblit repositories.
*
* @author James Moger
*
*/
public class FederationClient {
public static void main(String[] args) {
Params params = new Params();
JCommander jc = new JCommander(params);
try {
jc.parse(args);
} catch (ParameterException t) {
usage(jc, t);
}
IStoredSettings settings = new FileSettings(params.registrationsFile);
List<FederationModel> registrations = new ArrayList<FederationModel>();
if (StringUtils.isEmpty(params.url)) {
registrations.addAll(FederationUtils.getFederationRegistrations(settings));
} else {
if (StringUtils.isEmpty(params.token)) {
System.out.println("Must specify --token parameter!");
System.exit(0);
}
FederationModel model = new FederationModel("Gitblit");
model.url = params.url;
model.token = params.token;
model.mirror = params.mirror;
model.bare = params.bare;
model.frequency = params.frequency;
model.folder = "";
registrations.add(model);
}
if (registrations.size() == 0) {
System.out.println("No Federation Registrations! Nothing to do.");
System.exit(0);
}
System.out.println("Gitblit Federation Client v" + Constants.VERSION + " (" + Constants.VERSION_DATE + ")");
// command-line specified repositories folder
if (!StringUtils.isEmpty(params.repositoriesFolder)) {
settings.overrideSetting(Keys.git.repositoriesFolder, new File(
params.repositoriesFolder).getAbsolutePath());
}
// configure the Gitblit singleton for minimal, non-server operation
GitBlit.self().configureContext(settings, false);
FederationPullExecutor executor = new FederationPullExecutor(registrations, params.isDaemon);
executor.run();
if (!params.isDaemon) {
System.out.println("Finished.");
System.exit(0);
}
}
private static void usage(JCommander jc, ParameterException t) {
System.out.println(Constants.getGitBlitVersion());
System.out.println();
if (t != null) {
System.out.println(t.getMessage());
System.out.println();
}
if (jc != null) {
jc.usage();
}
System.exit(0);
}
/**
* JCommander Parameters class for FederationClient.
*/
@Parameters(separators = " ")
private static class Params {
@Parameter(names = { "--registrations" }, description = "Gitblit Federation Registrations File", required = false)
public String registrationsFile = "federation.properties";
@Parameter(names = { "--daemon" }, description = "Runs in daemon mode to schedule and pull repositories", required = false)
public boolean isDaemon;
@Parameter(names = { "--url" }, description = "URL of Gitblit instance to mirror from", required = false)
public String url;
@Parameter(names = { "--mirror" }, description = "Mirror repositories", required = false)
public boolean mirror;
@Parameter(names = { "--bare" }, description = "Create bare repositories", required = false)
public boolean bare;
@Parameter(names = { "--token" }, description = "Federation Token", required = false)
public String token;
@Parameter(names = { "--frequency" }, description = "Period to wait between pull attempts (requires --daemon)", required = false)
public String frequency = "60 mins";
@Parameter(names = { "--repositoriesFolder" }, description = "Destination folder for cloned repositories", required = false)
public String repositoriesFolder;
}
}
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import com.gitblit.build.Build;
/**
* Downloads dependencies and launches command-line Federation client.
*
* @author James Moger
*
*/
public class FederationClientLauncher {
public static void main(String[] args) {
// download federation client runtime dependencies
Build.federationClient();
File libFolder = new File("ext");
List<File> jars = Launcher.findJars(libFolder.getAbsoluteFile());
// sort the jars by name and then reverse the order so the newer version
// of the library gets loaded in the event that this is an upgrade
Collections.sort(jars);
Collections.reverse(jars);
for (File jar : jars) {
try {
Launcher.addJarFile(jar);
} catch (IOException e) {
}
}
FederationClient.main(args);
}
}
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit;
import static org.eclipse.jgit.lib.Constants.DOT_GIT_EXT;
import java.io.File;
import java.io.FileOutputStream;
import java.net.InetAddress;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.Constants.FederationPullStatus;
import com.gitblit.Constants.FederationStrategy;
import com.gitblit.models.FederationModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.FederationUtils;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.JGitUtils.CloneResult;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
/**
* FederationPullExecutor pulls repository updates and, optionally, user
* accounts and server settings from registered Gitblit instances.
*/
public class FederationPullExecutor implements Runnable {
private final Logger logger = LoggerFactory.getLogger(FederationPullExecutor.class);
private final List<FederationModel> registrations;
private final boolean isDaemon;
/**
* Constructor for specifying a single federation registration. This
* constructor is used to schedule the next pull execution.
*
* @param registration
*/
private FederationPullExecutor(FederationModel registration) {
this(Arrays.asList(registration), true);
}
/**
* Constructor to specify a group of federation registrations. This is
* normally used at startup to pull and then schedule the next update based
* on each registrations frequency setting.
*
* @param registrations
* @param isDaemon
* if true, registrations are rescheduled in perpetuity. if false,
* the federation pull operation is executed once.
*/
public FederationPullExecutor(List<FederationModel> registrations, boolean isDaemon) {
this.registrations = registrations;
this.isDaemon = isDaemon;
}
/**
* Run method for this pull executor.
*/
@Override
public void run() {
for (FederationModel registration : registrations) {
FederationPullStatus was = registration.getLowestStatus();
try {
Date now = new Date(System.currentTimeMillis());
pull(registration);
sendStatusAcknowledgment(registration);
registration.lastPull = now;
FederationPullStatus is = registration.getLowestStatus();
if (is.ordinal() < was.ordinal()) {
// the status for this registration has downgraded
logger.warn("Federation pull status of {0} is now {1}", registration.name,
is.name());
if (registration.notifyOnError) {
String message = "Federation pull of " + registration.name + " @ "
+ registration.url + " is now at " + is.name();
GitBlit.self()
.notifyAdministrators(
"Pull Status of " + registration.name + " is " + is.name(),
message);
}
}
} catch (Throwable t) {
logger.error(MessageFormat.format(
"Failed to pull from federated gitblit ({0} @ {1})", registration.name,
registration.url), t);
} finally {
if (isDaemon) {
schedule(registration);
}
}
}
}
/**
* Mirrors a repository and, optionally, the server's users, and/or
* configuration settings from a origin Gitblit instance.
*
* @param registration
* @throws Exception
*/
private void pull(FederationModel registration) throws Exception {
Map<String, RepositoryModel> repositories = FederationUtils.getRepositories(registration,
true);
String registrationFolder = registration.folder.toLowerCase().trim();
// confirm valid characters in server alias
Character c = StringUtils.findInvalidCharacter(registrationFolder);
if (c != null) {
logger.error(MessageFormat
.format("Illegal character ''{0}'' in folder name ''{1}'' of federation registration {2}!",
c, registrationFolder, registration.name));
return;
}
File repositoriesFolder = new File(GitBlit.getString(Keys.git.repositoriesFolder, "git"));
File registrationFolderFile = new File(repositoriesFolder, registrationFolder);
registrationFolderFile.mkdirs();
// Clone/Pull the repository
for (Map.Entry<String, RepositoryModel> entry : repositories.entrySet()) {
String cloneUrl = entry.getKey();
RepositoryModel repository = entry.getValue();
if (!repository.hasCommits) {
logger.warn(MessageFormat.format(
"Skipping federated repository {0} from {1} @ {2}. Repository is EMPTY.",
repository.name, registration.name, registration.url));
registration.updateStatus(repository, FederationPullStatus.SKIPPED);
continue;
}
// Determine local repository name
String repositoryName;
if (StringUtils.isEmpty(registrationFolder)) {
repositoryName = repository.name;
} else {
repositoryName = registrationFolder + "/" + repository.name;
}
if (registration.bare) {
// bare repository, ensure .git suffix
if (!repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {
repositoryName += DOT_GIT_EXT;
}
} else {
// normal repository, strip .git suffix
if (repositoryName.toLowerCase().endsWith(DOT_GIT_EXT)) {
repositoryName = repositoryName.substring(0, repositoryName.indexOf(DOT_GIT_EXT));
}
}
// confirm that the origin of any pre-existing repository matches
// the clone url
String fetchHead = null;
Repository existingRepository = GitBlit.self().getRepository(repositoryName);
if (existingRepository != null) {
StoredConfig config = existingRepository.getConfig();
config.load();
String origin = config.getString("remote", "origin", "url");
RevCommit commit = JGitUtils.getCommit(existingRepository, "refs/remotes/origin/master");
if (commit != null) {
fetchHead = commit.getName();
}
existingRepository.close();
if (!origin.startsWith(registration.url)) {
logger.warn(MessageFormat
.format("Skipping federated repository {0} from {1} @ {2}. Origin does not match, consider EXCLUDING.",
repository.name, registration.name, registration.url));
registration.updateStatus(repository, FederationPullStatus.SKIPPED);
continue;
}
}
// clone/pull this repository
CredentialsProvider credentials = new UsernamePasswordCredentialsProvider(
Constants.FEDERATION_USER, registration.token);
logger.info(MessageFormat.format("Pulling federated repository {0} from {1} @ {2}",
repository.name, registration.name, registration.url));
CloneResult result = JGitUtils.cloneRepository(registrationFolderFile, repository.name,
cloneUrl, registration.bare, credentials);
Repository r = GitBlit.self().getRepository(repositoryName);
RepositoryModel rm = GitBlit.self().getRepositoryModel(repositoryName);
repository.isFrozen = registration.mirror;
if (result.createdRepository) {
// default local settings
repository.federationStrategy = FederationStrategy.EXCLUDE;
repository.isFrozen = registration.mirror;
repository.showRemoteBranches = !registration.mirror;
logger.info(MessageFormat.format(" cloning {0}", repository.name));
registration.updateStatus(repository, FederationPullStatus.MIRRORED);
} else {
// fetch and update
boolean fetched = false;
RevCommit commit = JGitUtils.getCommit(r, "refs/remotes/origin/master");
String origin = commit.getName();
fetched = fetchHead == null || !fetchHead.equals(origin);
if (registration.mirror) {
// mirror
if (fetched) {
// reset the local HEAD to origin/master
Ref ref = JGitUtils.resetHEAD(r, "origin/master");
logger.info(MessageFormat.format(" resetting HEAD of {0} to {1}",
repository.name, ref.getObjectId().getName()));
registration.updateStatus(repository, FederationPullStatus.MIRRORED);
} else {
// indicate no commits pulled
registration.updateStatus(repository, FederationPullStatus.NOCHANGE);
}
} else {
// non-mirror
if (fetched) {
// indicate commits pulled to origin/master
registration.updateStatus(repository, FederationPullStatus.PULLED);
} else {
// indicate no commits pulled
registration.updateStatus(repository, FederationPullStatus.NOCHANGE);
}
}
// preserve local settings
repository.isFrozen = rm.isFrozen;
repository.federationStrategy = rm.federationStrategy;
// merge federation sets
Set<String> federationSets = new HashSet<String>();
if (rm.federationSets != null) {
federationSets.addAll(rm.federationSets);
}
if (repository.federationSets != null) {
federationSets.addAll(repository.federationSets);
}
repository.federationSets = new ArrayList<String>(federationSets);
}
// only repositories that are actually _cloned_ from the origin
// Gitblit repository are marked as federated. If the origin
// is from somewhere else, these repositories are not considered
// "federated" repositories.
repository.isFederated = cloneUrl.startsWith(registration.url);
GitBlit.self().updateConfiguration(r, repository);
r.close();
}
try {
// Pull USERS
Collection<UserModel> users = FederationUtils.getUsers(registration);
if (users != null && users.size() > 0) {
File realmFile = new File(registrationFolderFile, registration.name
+ "_users.properties");
realmFile.delete();
FileUserService userService = new FileUserService(realmFile);
for (UserModel user : users) {
userService.updateUserModel(user.username, user);
// merge the origin permissions and origin accounts into
// the user accounts of this Gitblit instance
if (registration.mergeAccounts) {
// reparent all repository permissions if the local
// repositories are stored within subfolders
if (!StringUtils.isEmpty(registrationFolder)) {
List<String> permissions = new ArrayList<String>(user.repositories);
user.repositories.clear();
for (String permission : permissions) {
user.addRepository(registrationFolder + "/" + permission);
}
}
// insert new user or update local user
UserModel localUser = GitBlit.self().getUserModel(user.username);
if (localUser == null) {
// create new local user
GitBlit.self().updateUserModel(user.username, user, true);
} else {
// update repository permissions of local user
for (String repository : user.repositories) {
localUser.addRepository(repository);
}
localUser.password = user.password;
localUser.canAdmin = user.canAdmin;
GitBlit.self().updateUserModel(localUser.username, localUser, false);
}
}
}
}
} catch (Exception e) {
// a 403 error code is normal for a PULL_REPOSITORIES token
if (!e.getMessage().contains("403")) {
logger.warn(MessageFormat.format(
"Failed to retrieve USERS from federated gitblit ({0} @ {1})",
registration.name, registration.url), e);
}
}
try {
// Pull SETTINGS
Map<String, String> settings = FederationUtils.getSettings(registration);
if (settings != null && settings.size() > 0) {
Properties properties = new Properties();
properties.putAll(settings);
FileOutputStream os = new FileOutputStream(new File(registrationFolderFile,
registration.name + "_" + Constants.PROPERTIES_FILE));
properties.store(os, null);
os.close();
}
} catch (Exception e) {
// a 403 error code is normal for a PULL_REPOSITORIES token
if (!e.getMessage().contains("403")) {
logger.warn(MessageFormat.format(
"Failed to retrieve SETTINGS from federated gitblit ({0} @ {1})",
registration.name, registration.url), e);
}
}
}
/**
* Sends a status acknowledgment to the origin Gitblit instance. This
* includes the results of the federated pull.
*
* @param registration
* @throws Exception
*/
private void sendStatusAcknowledgment(FederationModel registration) throws Exception {
if (!registration.sendStatus) {
// skip status acknowledgment
return;
}
InetAddress addr = InetAddress.getLocalHost();
String federationName = GitBlit.getString(Keys.federation.name, null);
if (StringUtils.isEmpty(federationName)) {
federationName = addr.getHostName();
}
FederationUtils.acknowledgeStatus(addr.getHostAddress(), registration);
logger.info(MessageFormat.format("Pull status sent to {0}", registration.url));
}
/**
* Schedules the next check of the federated Gitblit instance.
*
* @param registration
*/
private void schedule(FederationModel registration) {
// schedule the next pull
int mins = TimeUtils.convertFrequencyToMinutes(registration.frequency);
registration.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L));
GitBlit.self().executor()
.schedule(new FederationPullExecutor(registration), mins, TimeUnit.MINUTES);
logger.info(MessageFormat.format(
"Next pull of {0} @ {1} scheduled for {2,date,yyyy-MM-dd HH:mm}",
registration.name, registration.url, registration.nextPull));
}
}
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit;
import java.io.BufferedReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.Constants.FederationRequest;
import com.gitblit.Constants.FederationToken;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.FederationUtils;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* Handles federation requests.
*
* @author James Moger
*
*/
public class FederationServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private transient Logger logger = LoggerFactory.getLogger(FederationServlet.class);
public FederationServlet() {
super();
}
/**
* Returns an url to this servlet for the specified parameters.
*
* @param sourceURL
* the url of the source gitblit instance
* @param token
* the federation token of the source gitblit instance
* @param req
* the pull type request
*/
public static String asFederationLink(String sourceURL, String token, FederationRequest req) {
return asFederationLink(sourceURL, null, token, req, null);
}
/**
*
* @param remoteURL
* the url of the remote gitblit instance
* @param tokenType
* the type of federation token of a gitblit instance
* @param token
* the federation token of a gitblit instance
* @param req
* the pull type request
* @param myURL
* the url of this gitblit instance
* @return
*/
public static String asFederationLink(String remoteURL, FederationToken tokenType,
String token, FederationRequest req, String myURL) {
if (remoteURL.length() > 0 && remoteURL.charAt(remoteURL.length() - 1) == '/') {
remoteURL = remoteURL.substring(0, remoteURL.length() - 1);
}
if (req == null) {
req = FederationRequest.PULL_REPOSITORIES;
}
return remoteURL + Constants.FEDERATION_PATH + "?req=" + req.name().toLowerCase()
+ (token == null ? "" : ("&token=" + token))
+ (tokenType == null ? "" : ("&tokenType=" + tokenType.name().toLowerCase()))
+ (myURL == null ? "" : ("&url=" + StringUtils.encodeURL(myURL)));
}
/**
* Returns the list of repositories for federation requests.
*
* @param request
* @param response
* @throws javax.servlet.ServletException
* @throws java.io.IOException
*/
private void processRequest(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
java.io.IOException {
FederationRequest reqType = FederationRequest.fromName(request.getParameter("req"));
logger.info(MessageFormat.format("Federation {0} request from {1}", reqType,
request.getRemoteAddr()));
if (FederationRequest.POKE.equals(reqType)) {
// Gitblit always responds to POKE requests to verify a connection
logger.info("Received federation POKE from " + request.getRemoteAddr());
return;
}
if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true)) {
logger.warn(Keys.git.enableGitServlet + " must be set TRUE for federation requests.");
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
String uuid = GitBlit.getString(Keys.federation.passphrase, "");
if (StringUtils.isEmpty(uuid)) {
logger.warn(Keys.federation.passphrase
+ " is not properly set! Federation request denied.");
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
if (FederationRequest.PROPOSAL.equals(reqType)) {
// Receive a gitblit federation proposal
BufferedReader reader = request.getReader();
StringBuilder json = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
json.append(line);
}
reader.close();
// check to see if we have proposal data
if (json.length() == 0) {
logger.error(MessageFormat.format("Failed to receive proposal data from {0}",
request.getRemoteAddr()));
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
// deserialize the proposal
Gson gson = new Gson();
FederationProposal proposal = gson.fromJson(json.toString(), FederationProposal.class);
// reject proposal, if not receipt prohibited
if (!GitBlit.getBoolean(Keys.federation.allowProposals, false)) {
logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}",
proposal.tokenType.name(), proposal.url));
response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
return;
}
// poke the origin Gitblit instance that is proposing federation
boolean poked = false;
try {
poked = FederationUtils.poke(proposal.url);
} catch (Exception e) {
logger.error("Failed to poke origin", e);
}
if (!poked) {
logger.error(MessageFormat.format("Failed to send federation poke to {0}",
proposal.url));
response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
return;
}
String url = HttpUtils.getGitblitURL(request);
GitBlit.self().submitFederationProposal(proposal, url);
logger.info(MessageFormat.format(
"Submitted {0} federation proposal to pull {1} repositories from {2}",
proposal.tokenType.name(), proposal.repositories.size(), proposal.url));
response.setStatus(HttpServletResponse.SC_OK);
return;
}
if (FederationRequest.STATUS.equals(reqType)) {
// Receive a gitblit federation status acknowledgment
String remoteId = StringUtils.decodeFromHtml(request.getParameter("url"));
String identification = MessageFormat.format("{0} ({1})", remoteId,
request.getRemoteAddr());
BufferedReader reader = request.getReader();
StringBuilder json = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
json.append(line);
}
reader.close();
// check to see if we have repository data
if (json.length() == 0) {
logger.error(MessageFormat.format(
"Failed to receive pulled repositories list from {0}", identification));
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
// deserialize the status data
Gson gson = new Gson();
FederationModel results = gson.fromJson(json.toString(), FederationModel.class);
// setup the last and netx pull dates
results.lastPull = new Date();
int mins = TimeUtils.convertFrequencyToMinutes(results.frequency);
results.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L));
// acknowledge the receipt of status
GitBlit.self().acknowledgeFederationStatus(identification, results);
logger.info(MessageFormat.format(
"Received status of {0} federated repositories from {1}", results
.getStatusList().size(), identification));
response.setStatus(HttpServletResponse.SC_OK);
return;
}
// Determine the federation tokens for this gitblit instance
String token = request.getParameter("token");
List<String> tokens = GitBlit.self().getFederationTokens();
if (!tokens.contains(token)) {
logger.warn(MessageFormat.format(
"Received Federation token ''{0}'' does not match the server tokens", token));
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
Object result = null;
if (FederationRequest.PULL_REPOSITORIES.equals(reqType)) {
String gitblitUrl = HttpUtils.getGitblitURL(request);
result = GitBlit.self().getRepositories(gitblitUrl, token);
} else {
if (FederationRequest.PULL_SETTINGS.equals(reqType)) {
// pull settings
if (!GitBlit.self().validateFederationRequest(reqType, token)) {
// invalid token to pull users or settings
logger.warn(MessageFormat.format(
"Federation token from {0} not authorized to pull SETTINGS",
request.getRemoteAddr()));
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
Map<String, String> settings = new HashMap<String, String>();
List<String> keys = GitBlit.getAllKeys(null);
for (String key : keys) {
settings.put(key, GitBlit.getString(key, ""));
}
result = settings;
} else if (FederationRequest.PULL_USERS.equals(reqType)) {
// pull users
if (!GitBlit.self().validateFederationRequest(reqType, token)) {
// invalid token to pull users or settings
logger.warn(MessageFormat.format(
"Federation token from {0} not authorized to pull USERS",
request.getRemoteAddr()));
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
List<String> usernames = GitBlit.self().getAllUsernames();
List<UserModel> users = new ArrayList<UserModel>();
for (String username : usernames) {
UserModel user = GitBlit.self().getUserModel(username);
if (!user.excludeFromFederation) {
users.add(user);
}
}
result = users;
}
}
if (result != null) {
// Send JSON response
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(result);
response.getWriter().append(json);
}
}
@Override
protected void doPost(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
java.io.IOException {
processRequest(request, response);
}
@Override
protected void doGet(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
java.io.IOException {
processRequest(request, response);
}
}
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.Message.RecipientType;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.utils.StringUtils;
/**
* The mail executor handles sending email messages asynchronously from queue.
*
* @author James Moger
*
*/
public class MailExecutor implements Runnable {
private final Logger logger = LoggerFactory.getLogger(MailExecutor.class);
private final Queue<Message> queue = new ConcurrentLinkedQueue<Message>();
private final Set<Message> failures = Collections.synchronizedSet(new HashSet<Message>());
private final Session session;
private final IStoredSettings settings;
public MailExecutor(IStoredSettings settings) {
this.settings = settings;
final String mailUser = settings.getString(Keys.mail.username, null);
final String mailPassword = settings.getString(Keys.mail.password, null);
boolean authenticate = !StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword);
String server = settings.getString(Keys.mail.server, "");
if (StringUtils.isEmpty(server)) {
session = null;
return;
}
int port = settings.getInteger(Keys.mail.port, 25);
boolean isGMail = false;
if (server.equals("smtp.gmail.com")) {
port = 465;
isGMail = true;
}
Properties props = new Properties();
props.setProperty("mail.smtp.host", server);
props.setProperty("mail.smtp.port", String.valueOf(port));
props.setProperty("mail.smtp.auth", String.valueOf(authenticate));
props.setProperty("mail.smtp.auths", String.valueOf(authenticate));
if (isGMail) {
props.setProperty("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.socketFactory.port", String.valueOf(port));
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");
}
if (!StringUtils.isEmpty(mailUser) && !StringUtils.isEmpty(mailPassword)) {
// SMTP requires authentication
session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
PasswordAuthentication passwordAuthentication = new PasswordAuthentication(
mailUser, mailPassword);
return passwordAuthentication;
}
});
} else {
// SMTP does not require authentication
session = Session.getInstance(props);
}
}
/**
* Indicates if the mail executor can send emails.
*
* @return true if the mail executor is ready to send emails
*/
public boolean isReady() {
return session != null;
}
/**
* Creates a message for the administrators.
*
* @returna message
*/
public Message createMessageForAdministrators() {
List<String> toAddresses = settings.getStrings(Keys.mail.adminAddresses);
if (toAddresses.size() == 0) {
logger.warn("Can not notify administrators because no email addresses are defined!");
return null;
}
return createMessage(toAddresses);
}
/**
* Create a message.
*
* @param toAddresses
* @return a message
*/
public Message createMessage(String... toAddresses) {
return createMessage(Arrays.asList(toAddresses));
}
/**
* Create a message.
*
* @param toAddresses
* @return a message
*/
public Message createMessage(List<String> toAddresses) {
MimeMessage message = new MimeMessage(session);
try {
String fromAddress = settings.getString(Keys.mail.fromAddress, null);
if (StringUtils.isEmpty(fromAddress)) {
fromAddress = "gitblit@gitblit.com";
}
InternetAddress from = new InternetAddress(fromAddress, "Gitblit");
message.setFrom(from);
InternetAddress[] tos = new InternetAddress[toAddresses.size()];
for (int i = 0; i < toAddresses.size(); i++) {
tos[i] = new InternetAddress(toAddresses.get(i));
}
message.setRecipients(Message.RecipientType.TO, tos);
message.setSentDate(new Date());
} catch (Exception e) {
logger.error("Failed to properly create message", e);
}
return message;
}
/**
* Returns the status of the mail queue.
*
* @return true, if the queue is empty
*/
public boolean hasEmptyQueue() {
return queue.isEmpty();
}
/**
* Queue's an email message to be sent.
*
* @param message
* @return true if the message was queued
*/
public boolean queue(Message message) {
if (!isReady()) {
return false;
}
try {
message.saveChanges();
} catch (Throwable t) {
logger.error("Failed to save changes to message!", t);
}
queue.add(message);
return true;
}
@Override
public void run() {
if (!queue.isEmpty()) {
if (session != null) {
// send message via mail server
Message message = null;
while ((message = queue.peek()) != null) {
try {
if (settings.getBoolean(Keys.mail.debug, false)) {
logger.info("send: "
+ StringUtils.trimString(
message.getSubject()
+ " => "
+ message.getRecipients(RecipientType.TO)[0]
.toString(), 60));
}
Transport.send(message);
queue.remove();
failures.remove(message);
} catch (Throwable e) {
if (!failures.contains(message)) {
logger.error("Failed to send message", e);
failures.add(message);
}
}
}
}
} else {
// log message to console and drop
if (!queue.isEmpty()) {
Message message = null;
while ((message = queue.peek()) != null) {
try {
logger.info("drop: "
+ StringUtils.trimString(
(message.getSubject())
+ " => "
+ message.getRecipients(RecipientType.TO)[0]
.toString(), 60));
queue.remove();
failures.remove(message);
} catch (Throwable e) {
if (!failures.contains(message)) {
logger.error("Failed to remove message from queue");
failures.add(message);
}
}
}
}
}
}
}
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.models;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.gitblit.Constants.FederationPullStatus;
import com.gitblit.utils.StringUtils;
/**
* Represents a federated server registration. Gitblit federation allows one
* Gitblit instance to pull the repositories and configuration from another
* Gitblit instance. This is a backup operation and can be considered something
* like svn-sync.
*
*/
public class FederationModel implements Serializable, Comparable<FederationModel> {
private static final long serialVersionUID = 1L;
public String name;
public String url;
public String token;
public String frequency;
public String folder;
public boolean bare;
public boolean mirror;
public boolean mergeAccounts;
public boolean sendStatus;
public boolean notifyOnError;
public List<String> exclusions = new ArrayList<String>();
public List<String> inclusions = new ArrayList<String>();
public Date lastPull;
public Date nextPull;
private Map<String, FederationPullStatus> results = new ConcurrentHashMap<String, FederationPullStatus>();
/**
* The constructor for a remote server configuration.
*
* @param serverName
*/
public FederationModel(String serverName) {
this.name = serverName;
bare = true;
mirror = true;
this.lastPull = new Date(0);
this.nextPull = new Date(0);
}
public boolean isIncluded(RepositoryModel repository) {
// if exclusions has the all wildcard, then check for specific
// inclusions
if (exclusions.contains("*")) {
for (String name : inclusions) {
if (StringUtils.fuzzyMatch(repository.name, name)) {
results.put(repository.name, FederationPullStatus.PENDING);
return true;
}
}
results.put(repository.name, FederationPullStatus.EXCLUDED);
return false;
}
// named exclusions
for (String name : exclusions) {
if (StringUtils.fuzzyMatch(repository.name, name)) {
results.put(repository.name, FederationPullStatus.EXCLUDED);
return false;
}
}
// included by default
results.put(repository.name, FederationPullStatus.PENDING);
return true;
}
/**
* Updates the pull status of a particular repository in this federation
* registration.
*
* @param repository
* @param status
*/
public void updateStatus(RepositoryModel repository, FederationPullStatus status) {
if (!results.containsKey(repository)) {
results.put(repository.name, FederationPullStatus.PENDING);
}
if (status != null) {
results.put(repository.name, status);
}
}
public List<RepositoryStatus> getStatusList() {
List<RepositoryStatus> list = new ArrayList<RepositoryStatus>();
for (Map.Entry<String, FederationPullStatus> entry : results.entrySet()) {
list.add(new RepositoryStatus(entry.getKey(), entry.getValue()));
}
return list;
}
/**
* Iterates over the current pull results and returns the lowest pull
* status.
*
* @return the lowest pull status of the registration
*/
public FederationPullStatus getLowestStatus() {
if (results.size() == 0) {
return FederationPullStatus.PENDING;
}
FederationPullStatus status = FederationPullStatus.MIRRORED;
for (FederationPullStatus result : results.values()) {
if (result.ordinal() < status.ordinal()) {
status = result;
}
}
return status;
}
/**
* Returns true if this registration represents the result data sent by a
* pulling Gitblit instance.
*
* @return true, if this is result data
*/
public boolean isResultData() {
return !url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://");
}
@Override
public String toString() {
return "Federated " + name + " (" + url + ")";
}
@Override
public int compareTo(FederationModel o) {
boolean r1 = isResultData();
boolean r2 = o.isResultData();
if ((r1 && r2) || (!r1 && !r2)) {
// sort registrations and results by name
return name.compareTo(o.name);
}
// sort registrations first
if (r1) {
return 1;
}
return -1;
}
/**
* Class that encapsulates a point-in-time pull result.
*
*/
public static class RepositoryStatus implements Serializable, Comparable<RepositoryStatus> {
private static final long serialVersionUID = 1L;
public final String name;
public final FederationPullStatus status;
RepositoryStatus(String name, FederationPullStatus status) {
this.name = name;
this.status = status;
}
@Override
public int compareTo(RepositoryStatus o) {
if (status.equals(o.status)) {
// sort root repositories first, alphabetically
// then sort grouped repositories, alphabetically
int s1 = name.indexOf('/');
int s2 = o.name.indexOf('/');
if (s1 == -1 && s2 == -1) {
// neither grouped
return name.compareTo(o.name);
} else if (s1 > -1 && s2 > -1) {
// both grouped
return name.compareTo(o.name);
} else if (s1 == -1) {
return -1;
} else if (s2 == -1) {
return 1;
}
return 0;
}
return status.compareTo(o.status);
}
}
}
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.models;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
import com.gitblit.Constants.FederationToken;
/**
* Represents a proposal from a Gitblit instance to pull its repositories.
*/
public class FederationProposal implements Serializable {
private static final long serialVersionUID = 1L;
public Date received;
public String name;
public String url;
public FederationToken tokenType;
public String token;
public String message;
public Map<String, RepositoryModel> repositories;
/**
* The constructor for a federation proposal.
*
* @param url
* the url of the source Gitblit instance
* @param tokenType
* the type of token from the source Gitblit instance
* @param token
* the federation token from the source Gitblit instance
* @param repositories
* the map of repositories to be pulled from the source Gitblit
* instance keyed by the repository clone url
*/
public FederationProposal(String url, FederationToken tokenType, String token,
Map<String, RepositoryModel> repositories) {
this.received = new Date();
this.url = url;
this.tokenType = tokenType;
this.token = token;
this.message = "";
this.repositories = repositories;
try {
// determine server name and set that as the proposal name
name = url.substring(url.indexOf("//") + 2);
if (name.contains("/")) {
name = name.substring(0, name.indexOf('/'));
}
name = name.replace(".", "").replace(";", "").replace(":", "").replace("-", "");
} catch (Exception e) {
name = Long.toHexString(System.currentTimeMillis());
}
}
@Override
public String toString() {
return "Federation Proposal (" + url + ")";
}
}
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.utils;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.Constants.FederationProposalResult;
import com.gitblit.Constants.FederationRequest;
import com.gitblit.FederationServlet;
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
/**
* Utility methods for federation functions.
*
* @author James Moger
*
*/
public class FederationUtils {
public static final String CHARSET;
public static final Type REPOSITORIES_TYPE = new TypeToken<Map<String, RepositoryModel>>() {
}.getType();
public static final Type SETTINGS_TYPE = new TypeToken<Map<String, String>>() {
}.getType();
public static final Type USERS_TYPE = new TypeToken<Collection<UserModel>>() {
}.getType();
public static final Type RESULTS_TYPE = new TypeToken<List<FederationModel>>() {
}.getType();
private static final SSLContext SSL_CONTEXT;
private static final DummyHostnameVerifier HOSTNAME_VERIFIER;
private static final Logger LOGGER = LoggerFactory.getLogger(FederationUtils.class);
static {
SSLContext context = null;
try {
context = SSLContext.getInstance("SSL");
context.init(null, new TrustManager[] { new DummyTrustManager() }, new SecureRandom());
} catch (Throwable t) {
t.printStackTrace();
}
SSL_CONTEXT = context;
HOSTNAME_VERIFIER = new DummyHostnameVerifier();
CHARSET = "UTF-8";
}
/**
* Returns the list of federated gitblit instances that this instance will
* try to pull.
*
* @return list of registered gitblit instances
*/
public static List<FederationModel> getFederationRegistrations(IStoredSettings settings) {
List<FederationModel> federationRegistrations = new ArrayList<FederationModel>();
List<String> keys = settings.getAllKeys(Keys.federation._ROOT);
keys.remove(Keys.federation.name);
keys.remove(Keys.federation.passphrase);
keys.remove(Keys.federation.allowProposals);
keys.remove(Keys.federation.proposalsFolder);
keys.remove(Keys.federation.defaultFrequency);
keys.remove(Keys.federation.sets);
Collections.sort(keys);
Map<String, FederationModel> federatedModels = new HashMap<String, FederationModel>();
for (String key : keys) {
String value = key.substring(Keys.federation._ROOT.length() + 1);
List<String> values = StringUtils.getStringsFromValue(value, "\\.");
String server = values.get(0);
if (!federatedModels.containsKey(server)) {
federatedModels.put(server, new FederationModel(server));
}
String setting = values.get(1);
if (setting.equals("url")) {
// url of the origin Gitblit instance
federatedModels.get(server).url = settings.getString(key, "");
} else if (setting.equals("token")) {
// token for the origin Gitblit instance
federatedModels.get(server).token = settings.getString(key, "");
} else if (setting.equals("frequency")) {
// frequency of the pull operation
federatedModels.get(server).frequency = settings.getString(key, "");
} else if (setting.equals("folder")) {
// destination folder of the pull operation
federatedModels.get(server).folder = settings.getString(key, "");
} else if (setting.equals("bare")) {
// whether pulled repositories should be bare
federatedModels.get(server).bare = settings.getBoolean(key, true);
} else if (setting.equals("mirror")) {
// are the repositories to be true mirrors of the origin
federatedModels.get(server).mirror = settings.getBoolean(key, true);
} else if (setting.equals("mergeAccounts")) {
// merge remote accounts into local accounts
federatedModels.get(server).mergeAccounts = settings.getBoolean(key, false);
} else if (setting.equals("sendStatus")) {
// send a status acknowledgment to source Gitblit instance
// at end of git pull
federatedModels.get(server).sendStatus = settings.getBoolean(key, false);
} else if (setting.equals("notifyOnError")) {
// notify administrators on federation pull failures
federatedModels.get(server).notifyOnError = settings.getBoolean(key, false);
} else if (setting.equals("exclude")) {
// excluded repositories
federatedModels.get(server).exclusions = settings.getStrings(key);
} else if (setting.equals("include")) {
// included repositories
federatedModels.get(server).inclusions = settings.getStrings(key);
}
}
// verify that registrations have a url and a token
for (FederationModel model : federatedModels.values()) {
if (StringUtils.isEmpty(model.url)) {
LOGGER.warn(MessageFormat.format(
"Dropping federation registration {0}. Missing url.", model.name));
continue;
}
if (StringUtils.isEmpty(model.token)) {
LOGGER.warn(MessageFormat.format(
"Dropping federation registration {0}. Missing token.", model.name));
continue;
}
// set default frequency if unspecified
if (StringUtils.isEmpty(model.frequency)) {
model.frequency = settings.getString(Keys.federation.defaultFrequency, "60 mins");
}
federationRegistrations.add(model);
}
return federationRegistrations;
}
/**
* Sends a federation poke to the Gitblit instance at remoteUrl. Pokes are
* sent by an pulling Gitblit instance to an origin Gitblit instance as part
* of the proposal process. This is to ensure that the pulling Gitblit
* instance has an IP route to the origin instance.
*
* @param remoteUrl
* the remote Gitblit instance to send a federation proposal to
* @param proposal
* a complete federation proposal
* @return true if there is a route to the remoteUrl
*/
public static boolean poke(String remoteUrl) throws Exception {
String url = FederationServlet.asFederationLink(remoteUrl, null, FederationRequest.POKE);
Gson gson = new Gson();
String json = gson.toJson("POKE");
int status = writeJson(url, json);
return status == HttpServletResponse.SC_OK;
}
/**
* Sends a federation proposal to the Gitblit instance at remoteUrl
*
* @param remoteUrl
* the remote Gitblit instance to send a federation proposal to
* @param proposal
* a complete federation proposal
* @return the federation proposal result code
*/
public static FederationProposalResult propose(String remoteUrl, FederationProposal proposal)
throws Exception {
String url = FederationServlet
.asFederationLink(remoteUrl, null, FederationRequest.PROPOSAL);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(proposal);
int status = writeJson(url, json);
switch (status) {
case HttpServletResponse.SC_FORBIDDEN:
// remote Gitblit Federation disabled
return FederationProposalResult.FEDERATION_DISABLED;
case HttpServletResponse.SC_BAD_REQUEST:
// remote Gitblit did not receive any JSON data
return FederationProposalResult.MISSING_DATA;
case HttpServletResponse.SC_METHOD_NOT_ALLOWED:
// remote Gitblit not accepting proposals
return FederationProposalResult.NO_PROPOSALS;
case HttpServletResponse.SC_NOT_ACCEPTABLE:
// remote Gitblit failed to poke this Gitblit instance
return FederationProposalResult.NO_POKE;
case HttpServletResponse.SC_OK:
// received
return FederationProposalResult.ACCEPTED;
default:
return FederationProposalResult.ERROR;
}
}
/**
* Retrieves a map of the repositories at the remote gitblit instance keyed
* by the repository clone url.
*
* @param registration
* @param checkExclusions
* should returned repositories remove registration exclusions
* @return a map of cloneable repositories
* @throws Exception
*/
public static Map<String, RepositoryModel> getRepositories(FederationModel registration,
boolean checkExclusions) throws Exception {
String url = FederationServlet.asFederationLink(registration.url, registration.token,
FederationRequest.PULL_REPOSITORIES);
Map<String, RepositoryModel> models = readGson(url, REPOSITORIES_TYPE);
if (checkExclusions) {
Map<String, RepositoryModel> includedModels = new HashMap<String, RepositoryModel>();
for (Map.Entry<String, RepositoryModel> entry : models.entrySet()) {
if (registration.isIncluded(entry.getValue())) {
includedModels.put(entry.getKey(), entry.getValue());
}
}
return includedModels;
}
return models;
}
/**
* Tries to pull the gitblit user accounts from the remote gitblit instance.
*
* @param registration
* @return a collection of UserModel objects
* @throws Exception
*/
public static Collection<UserModel> getUsers(FederationModel registration) throws Exception {
String url = FederationServlet.asFederationLink(registration.url, registration.token,
FederationRequest.PULL_USERS);
Collection<UserModel> models = readGson(url, USERS_TYPE);
return models;
}
/**
* Tries to pull the gitblit server settings from the remote gitblit
* instance.
*
* @param registration
* @return a map of the remote gitblit settings
* @throws Exception
*/
public static Map<String, String> getSettings(FederationModel registration) throws Exception {
String url = FederationServlet.asFederationLink(registration.url, registration.token,
FederationRequest.PULL_SETTINGS);
Map<String, String> settings = readGson(url, SETTINGS_TYPE);
return settings;
}
/**
* Send an status acknowledgment to the remote Gitblit server.
*
* @param identification
* identification of this pulling instance
* @param registration
* the source Gitblit instance to receive an acknowledgment
* @param results
* the results of your pull operation
* @return true, if the remote Gitblit instance acknowledged your results
* @throws Exception
*/
public static boolean acknowledgeStatus(String identification, FederationModel registration)
throws Exception {
String url = FederationServlet.asFederationLink(registration.url, null, registration.token,
FederationRequest.STATUS, identification);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(registration);
int status = writeJson(url, json);
return status == HttpServletResponse.SC_OK;
}
/**
* Reads a gson object from the specified url.
*
* @param url
* @param type
* @return
* @throws Exception
*/
public static <X> X readGson(String url, Type type) throws Exception {
String json = readJson(url);
if (StringUtils.isEmpty(json)) {
return null;
}
Gson gson = new Gson();
return gson.fromJson(json, type);
}
/**
* Reads a JSON response.
*
* @param url
* @return the JSON response as a string
* @throws Exception
*/
public static String readJson(String url) throws Exception {
URL urlObject = new URL(url);
URLConnection conn = urlObject.openConnection();
conn.setRequestProperty("Accept-Charset", CHARSET);
conn.setUseCaches(false);
conn.setDoInput(true);
if (conn instanceof HttpsURLConnection) {
HttpsURLConnection secureConn = (HttpsURLConnection) conn;
secureConn.setSSLSocketFactory(SSL_CONTEXT.getSocketFactory());
secureConn.setHostnameVerifier(HOSTNAME_VERIFIER);
}
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is, CHARSET));
StringBuilder json = new StringBuilder();
char[] buffer = new char[4096];
int len = 0;
while ((len = reader.read(buffer)) > -1) {
json.append(buffer, 0, len);
}
is.close();
return json.toString();
}
/**
* Writes a JSON message to the specified url.
*
* @param url
* the url to write to
* @param json
* the json message to send
* @return the http request result code
* @throws Exception
*/
public static int writeJson(String url, String json) throws Exception {
byte[] jsonBytes = json.getBytes(CHARSET);
URL urlObject = new URL(url);
URLConnection conn = urlObject.openConnection();
conn.setRequestProperty("Content-Type", "text/plain;charset=" + CHARSET);
conn.setRequestProperty("Content-Length", "" + jsonBytes.length);
conn.setUseCaches(false);
conn.setDoOutput(true);
if (conn instanceof HttpsURLConnection) {
HttpsURLConnection secureConn = (HttpsURLConnection) conn;
secureConn.setSSLSocketFactory(SSL_CONTEXT.getSocketFactory());
secureConn.setHostnameVerifier(HOSTNAME_VERIFIER);
}
// write json body
OutputStream os = conn.getOutputStream();
os.write(jsonBytes);
os.close();
int status = ((HttpURLConnection) conn).getResponseCode();
return status;
}
/**
* DummyTrustManager trusts all certificates.
*/
private static class DummyTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
/**
* Trusts all hostnames from a certificate, including self-signed certs.
*/
private static class DummyHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
}
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.utils;
import javax.servlet.http.HttpServletRequest;
/**
* Collection of utility methods for http requests.
*
* @author James Moger
*
*/
public class HttpUtils {
/**
* Returns the Gitblit URL based on the request.
*
* @param request
* @return the host url
*/
public static String getGitblitURL(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
sb.append(request.getScheme());
sb.append("://");
sb.append(request.getServerName());
if ((request.getScheme().equals("http") && request.getServerPort() != 80)
|| (request.getScheme().equals("https") && request.getServerPort() != 443)) {
sb.append(":" + request.getServerPort());
}
sb.append(request.getContextPath());
return sb.toString();
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
xml:lang="en"
lang="en">
<body>
<wicket:extend>
<div style="padding-top:20px"></div>
<div style="text-align:center;" wicket:id="feedback">[Feedback Panel]</div>
<!-- registration info -->
<table class="plain">
<tr><th><wicket:message key="gb.url">url</wicket:message></th><td><img style="border:0px;vertical-align:middle;" wicket:id="typeIcon" /> <span wicket:id="url">[url]</span></td></tr>
<tr><th><wicket:message key="gb.token">token</wicket:message></th><td><span class="sha1" wicket:id="token">[token]</span></td></tr>
<tr><th><wicket:message key="gb.folder">folder</wicket:message></th><td><span wicket:id="folder">[folder]</span></td></tr>
<tr><th><wicket:message key="gb.frequency">frequency</wicket:message></th><td><span wicket:id="frequency">[frequency]</span></td></tr>
<tr><th><wicket:message key="gb.lastPull">lastPull</wicket:message></th><td><span wicket:id="lastPull">[lastPull]</span></td></tr>
<tr><th><wicket:message key="gb.nextPull">nextPull</wicket:message></th><td><span wicket:id="nextPull">[nextPull]</span></td></tr>
<tr><th valign="top"><wicket:message key="gb.exclusions">exclusions</wicket:message></th><td><span class="sha1" wicket:id="exclusions">[exclusions]</span></td></tr>
<tr><th valign="top"><wicket:message key="gb.inclusions">inclusions</wicket:message></th><td><span class="sha1" wicket:id="inclusions">[inclusions]</span></td></tr>
</table>
<table class="repositories">
<tr>
<th class="left">
<img style="vertical-align: top; border: 1px solid #888; background-color: white;" src="gitweb-favicon.png"/>
<wicket:message key="gb.repositories">[repositories]</wicket:message>
</th>
<th class="right"><wicket:message key="gb.status">[status]</wicket:message></th>
</tr>
<tbody>
<tr wicket:id="row">
<td class="left"><img style="border:0px;vertical-align:middle;" wicket:id="statusIcon" /><span wicket:id="name">[name]</span></td>
<td class="right"><span wicket:id="status">[status]</span></td>
</tr>
</tbody>
</table>
</wicket:extend>
</body>
</html>
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.wicket.pages;
import java.util.Collections;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationModel.RepositoryStatus;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;
public class FederationRegistrationPage extends BasePage {
public FederationRegistrationPage(PageParameters params) {
super(params);
final boolean showAdmin;
if (GitBlit.getBoolean(Keys.web.authenticateAdminPages, true)) {
boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, false);
showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
} else {
showAdmin = false;
}
setStatelessHint(true);
String url = WicketUtils.getUrlParameter(params);
String name = WicketUtils.getNameParameter(params);
FederationModel registration = GitBlit.self().getFederationRegistration(url, name);
if (registration == null) {
error("Could not find federation registration!", true);
}
setupPage("", registration.isResultData() ? getString("gb.federationResults")
: getString("gb.federationRegistration"));
add(new Label("url", registration.url));
add(WicketUtils.getRegistrationImage("typeIcon", registration, this));
add(new Label("frequency", registration.frequency));
add(new Label("folder", registration.folder));
add(new Label("token", showAdmin ? registration.token : "--"));
add(WicketUtils.createTimestampLabel("lastPull", registration.lastPull, getTimeZone()));
add(WicketUtils.createTimestampLabel("nextPull", registration.nextPull, getTimeZone()));
StringBuilder inclusions = new StringBuilder();
for (String inc : registration.inclusions) {
inclusions.append(inc).append("<br/>");
}
StringBuilder exclusions = new StringBuilder();
for (String ex : registration.exclusions) {
exclusions.append(ex).append("<br/>");
}
add(new Label("inclusions", inclusions.toString()).setEscapeModelStrings(false));
add(new Label("exclusions", exclusions.toString()).setEscapeModelStrings(false));
List<RepositoryStatus> list = registration.getStatusList();
Collections.sort(list);
DataView<RepositoryStatus> dataView = new DataView<RepositoryStatus>("row",
new ListDataProvider<RepositoryStatus>(list)) {
private static final long serialVersionUID = 1L;
private int counter;
@Override
protected void onBeforeRender() {
super.onBeforeRender();
counter = 0;
}
public void populateItem(final Item<RepositoryStatus> item) {
final RepositoryStatus entry = item.getModelObject();
item.add(WicketUtils.getPullStatusImage("statusIcon", entry.status));
item.add(new Label("name", entry.name));
item.add(new Label("status", entry.status.name()));
WicketUtils.setAlternatingBackground(item, counter);
counter++;
}
};
add(dataView);
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
xml:lang="en"
lang="en">
<body>
<wicket:extend>
<div style="padding-top:20px"></div>
<div style="text-align:center;" wicket:id="feedback">[Feedback Panel]</div>
<!-- proposal info -->
<table class="plain">
<tr><th><wicket:message key="gb.received">received</wicket:message></th><td><span wicket:id="received">[received]</span></td></tr>
<tr><th><wicket:message key="gb.url">url</wicket:message></th><td><span wicket:id="url">[url]</span></td></tr>
<tr><th><wicket:message key="gb.message">message</wicket:message></th><td><span wicket:id="message">[message]</span></td></tr>
<tr><th><wicket:message key="gb.type">type</wicket:message></th><td><span wicket:id="tokenType">[token type]</span></td></tr>
<tr><th><wicket:message key="gb.token">token</wicket:message></th><td><span class="sha1" wicket:id="token">[token]</span></td></tr>
<tr><th valign="top"><wicket:message key="gb.proposal">proposal</wicket:message></th><td><span class="sha1" wicket:id="definition">[definition]</span></td></tr>
</table>
<div wicket:id="repositories"></div>
</wicket:extend>
</body>
</html>
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.wicket.pages;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import com.gitblit.Constants.FederationToken;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.RepositoryModel;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.RequiresAdminRole;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.RepositoriesPanel;
@RequiresAdminRole
public class ReviewProposalPage extends BasePage {
private final String PROPS_PATTERN = "{0} = {1}\n";
private final String WEBXML_PATTERN = "\n<context-param>\n\t<param-name>{0}</param-name>\n\t<param-value>{1}</param-value>\n</context-param>\n";
public ReviewProposalPage(PageParameters params) {
super(params);
setupPage("", getString("gb.proposals"));
setStatelessHint(true);
final String token = WicketUtils.getToken(params);
FederationProposal proposal = GitBlit.self().getPendingFederationProposal(token);
if (proposal == null) {
error("Could not find federation proposal!", true);
}
add(new Label("url", proposal.url));
add(new Label("message", proposal.message));
add(WicketUtils.createTimestampLabel("received", proposal.received, getTimeZone()));
add(new Label("token", proposal.token));
add(new Label("tokenType", proposal.tokenType.name()));
boolean go = true;
String p;
if (GitBlit.isGO()) {
// gitblit.properties definition
p = PROPS_PATTERN;
} else {
// web.xml definition
p = WEBXML_PATTERN;
}
// build proposed definition
StringBuilder sb = new StringBuilder();
sb.append(asParam(p, proposal.name, "url", proposal.url));
sb.append(asParam(p, proposal.name, "token", proposal.token));
if (FederationToken.USERS_AND_REPOSITORIES.equals(proposal.tokenType)
|| FederationToken.ALL.equals(proposal.tokenType)) {
sb.append(asParam(p, proposal.name, "mergeAccounts", "false"));
}
sb.append(asParam(p, proposal.name, "frequency",
GitBlit.getString(Keys.federation.defaultFrequency, "60 mins")));
sb.append(asParam(p, proposal.name, "folder", proposal.name));
sb.append(asParam(p, proposal.name, "bare", "true"));
sb.append(asParam(p, proposal.name, "mirror", "true"));
sb.append(asParam(p, proposal.name, "sendStatus", "true"));
sb.append(asParam(p, proposal.name, "notifyOnError", "true"));
sb.append(asParam(p, proposal.name, "exclude", ""));
sb.append(asParam(p, proposal.name, "include", ""));
add(new Label("definition", StringUtils.breakLinesForHtml(StringUtils.escapeForHtml(sb
.toString().trim(), true))).setEscapeModelStrings(false));
List<RepositoryModel> repositories = new ArrayList<RepositoryModel>(
proposal.repositories.values());
RepositoriesPanel repositoriesPanel = new RepositoriesPanel("repositories", false,
repositories, getAccessRestrictions());
add(repositoriesPanel);
}
private String asParam(String pattern, String name, String key, String value) {
return MessageFormat.format(pattern, Keys.federation._ROOT + "." + name + "." + key, value);
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
xml:lang="en"
lang="en">
<wicket:extend>
<body onload="document.getElementById('myUrl').focus();">
<div style="padding-top:20px"></div>
<div style="text-align:center;" wicket:id="feedback">[Feedback Panel]</div>
<!-- proposal info -->
<form wicket:id="editForm">
<table class="plain">
<tr><th><wicket:message key="gb.url">url</wicket:message></th><td class="edit"><input type="text" wicket:id="myUrl" id="myUrl" size="60" /> &nbsp;<i><wicket:message key="gb.myUrlDescription"></wicket:message></i></td></tr>
<tr><th><wicket:message key="gb.destinationUrl">destination url</wicket:message></th><td class="edit"><input type="text" wicket:id="destinationUrl" size="60" /> &nbsp;<i><wicket:message key="gb.destinationUrlDescription"></wicket:message></i></td></tr>
<tr><th valign="top"><wicket:message key="gb.message">message</wicket:message></th><td class="edit"><input type="text" wicket:id="message" size="80" /></td></tr>
<tr><th><wicket:message key="gb.type">type</wicket:message></th><td><span wicket:id="tokenType">[token type]</span></td></tr>
<tr><th><wicket:message key="gb.token">token</wicket:message></th><td><span class="sha1" wicket:id="token">[token]</span></td></tr>
<tr><th></th><td class="editButton"><input type="submit" value="propose" wicket:message="value:gb.sendProposal" wicket:id="save" /> <input type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /></td></tr>
</table>
</form>
<div style="padding-top:10px;" wicket:id="repositories"></div>
</body>
</wicket:extend>
</html>
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.wicket.pages;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.CompoundPropertyModel;
import com.gitblit.Constants.FederationProposalResult;
import com.gitblit.GitBlit;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.RepositoryModel;
import com.gitblit.utils.FederationUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.RequiresAdminRole;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.RepositoriesPanel;
@RequiresAdminRole
public class SendProposalPage extends BasePage {
public String myUrl;
public String destinationUrl;
public String message;
public SendProposalPage(PageParameters params) {
super(params);
setupPage("", getString("gb.sendProposal"));
setStatelessHint(true);
final String token = WicketUtils.getToken(params);
myUrl = WicketUtils.getGitblitURL(getRequest());
destinationUrl = "https://";
// temporary proposal
FederationProposal proposal = GitBlit.self().createFederationProposal(myUrl, token);
if (proposal == null) {
error("Could not create federation proposal!", true);
}
CompoundPropertyModel<SendProposalPage> model = new CompoundPropertyModel<SendProposalPage>(
this);
Form<SendProposalPage> form = new Form<SendProposalPage>("editForm", model) {
private static final long serialVersionUID = 1L;
@Override
protected void onSubmit() {
// confirm a repository name was entered
if (StringUtils.isEmpty(myUrl)) {
error("Please enter your Gitblit url!");
return;
}
if (StringUtils.isEmpty(destinationUrl)) {
error("Please enter a destination url for your proposal!");
return;
}
// build new proposal
FederationProposal proposal = GitBlit.self().createFederationProposal(myUrl, token);
proposal.url = myUrl;
proposal.message = message;
try {
FederationProposalResult res = FederationUtils
.propose(destinationUrl, proposal);
switch (res) {
case ACCEPTED:
info(MessageFormat.format("Proposal successfully received by {0}.",
destinationUrl));
setResponsePage(RepositoriesPage.class);
break;
case NO_POKE:
error(MessageFormat.format(
"Sorry, {0} could not find a Gitblit instance at {1}.",
destinationUrl, myUrl));
break;
case NO_PROPOSALS:
error(MessageFormat.format(
"Sorry, {0} is not accepting proposals at this time.",
destinationUrl));
break;
case FEDERATION_DISABLED:
error(MessageFormat
.format("Sorry, {0} is not configured to federate with any Gitblit instances.",
destinationUrl));
break;
case MISSING_DATA:
error(MessageFormat.format("Sorry, {0} did not receive any proposal data!",
destinationUrl));
break;
case ERROR:
error(MessageFormat.format(
"Sorry, {0} reports that an unexpected error occurred!",
destinationUrl));
break;
}
} catch (Exception e) {
if (!StringUtils.isEmpty(e.getMessage())) {
error(e.getMessage());
} else {
error("Failed to send proposal!");
}
}
}
};
form.add(new TextField<String>("myUrl"));
form.add(new TextField<String>("destinationUrl"));
form.add(new TextField<String>("message"));
form.add(new Label("tokenType", proposal.tokenType.name()));
form.add(new Label("token", proposal.token));
form.add(new Button("save"));
Button cancel = new Button("cancel") {
private static final long serialVersionUID = 1L;
@Override
public void onSubmit() {
setResponsePage(RepositoriesPage.class);
}
};
cancel.setDefaultFormProcessing(false);
form.add(cancel);
add(form);
List<RepositoryModel> repositories = new ArrayList<RepositoryModel>(
proposal.repositories.values());
RepositoriesPanel repositoriesPanel = new RepositoriesPanel("repositories", false,
repositories, getAccessRestrictions());
add(repositoriesPanel);
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
xml:lang="en"
lang="en">
<body>
<wicket:panel>
<table class="repositories">
<tr>
<th class="left">
<img style="vertical-align: top; border: 1px solid #888; background-color: white;" src="federated_16x16.png"/>
<wicket:message key="gb.proposals">[proposals]</wicket:message>
</th>
<th><wicket:message key="gb.received">[received]</wicket:message></th>
<th><wicket:message key="gb.type">[type]</wicket:message></th>
<th><wicket:message key="gb.token">[token]</wicket:message></th>
<th class="right"></th>
</tr>
<tbody>
<tr wicket:id="row">
<td class="left"><span class="list" wicket:id="url">[field]</span></td>
<td><span class="date"" wicket:id="received">[received]</span></td>
<td><span wicket:id="tokenType">[token type]</span></td>
<td><span class="sha1"" wicket:id="token">[token]</span></td>
<td class="rightAlign"><span class="link"><a wicket:id="deleteProposal"><wicket:message key="gb.delete">[delete]</wicket:message></a></span></td>
</tr>
</tbody>
</table>
</wicket:panel>
</body>
</html>
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.wicket.panels;
import java.text.MessageFormat;
import java.util.List;
import org.apache.wicket.Component;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import com.gitblit.GitBlit;
import com.gitblit.models.FederationProposal;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.ReviewProposalPage;
public class FederationProposalsPanel extends BasePanel {
private static final long serialVersionUID = 1L;
private final boolean hasProposals;
public FederationProposalsPanel(String wicketId) {
super(wicketId);
final List<FederationProposal> list = GitBlit.self().getPendingFederationProposals();
hasProposals = list.size() > 0;
DataView<FederationProposal> dataView = new DataView<FederationProposal>("row",
new ListDataProvider<FederationProposal>(list)) {
private static final long serialVersionUID = 1L;
private int counter;
@Override
protected void onBeforeRender() {
super.onBeforeRender();
counter = 0;
}
public void populateItem(final Item<FederationProposal> item) {
final FederationProposal entry = item.getModelObject();
item.add(new LinkPanel("url", "list", entry.url, ReviewProposalPage.class,
WicketUtils.newTokenParameter(entry.token)));
item.add(WicketUtils.createDateLabel("received", entry.received, getTimeZone()));
item.add(new Label("tokenType", entry.tokenType.name()));
item.add(new LinkPanel("token", "list", entry.token, ReviewProposalPage.class,
WicketUtils.newTokenParameter(entry.token)));
Link<Void> deleteLink = new Link<Void>("deleteProposal") {
private static final long serialVersionUID = 1L;
@Override
public void onClick() {
if (GitBlit.self().deletePendingFederationProposal(entry)) {
list.remove(entry);
info(MessageFormat.format("Proposal ''{0}'' deleted.", entry.name));
} else {
error(MessageFormat.format("Failed to delete proposal ''{0}''!",
entry.name));
}
}
};
deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
"Delete proposal \"{0}\"?", entry.name)));
item.add(deleteLink);
WicketUtils.setAlternatingBackground(item, counter);
counter++;
}
};
add(dataView);
}
public Component hideIfEmpty() {
return super.setVisible(hasProposals);
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
xml:lang="en"
lang="en">
<body>
<wicket:panel>
<table class="repositories">
<tr>
<th class="left">
<img style="vertical-align: top; border: 1px solid #888; background-color: white;" src="federated_16x16.png"/>
<wicket:message key="gb.registrations">[registrations]</wicket:message>
</th>
<th><wicket:message key="gb.name">[name]</wicket:message></th>
<th><wicket:message key="gb.frequency">[frequency]</wicket:message></th>
<th></th>
<th><wicket:message key="gb.lastPull">[lastPull]</wicket:message></th>
<th><wicket:message key="gb.nextPull">[nextPull]</wicket:message></th>
<th class="right"></th>
</tr>
<tbody>
<tr wicket:id="row">
<td class="left"><img style="border:0px;vertical-align:middle;" wicket:id="statusIcon" /><span class="list" wicket:id="url">[url]</span></td>
<td><span class="list" wicket:id="name">[name]</span></td>
<td><span wicket:id="frequency">[frequency]</span></td>
<td><img style="border:0px;vertical-align:middle;" wicket:id="typeIcon" /></td>
<td><span class="date"" wicket:id="lastPull">[lastPull]</span></td>
<td><span class="date"" wicket:id="nextPull">[nextPull]</span></td>
<td class="rightAlign"><span class="link"></span></td>
</tr>
</tbody>
</table>
</wicket:panel>
</body>
</html>
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.wicket.panels;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.wicket.Component;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import com.gitblit.GitBlit;
import com.gitblit.models.FederationModel;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.FederationRegistrationPage;
public class FederationRegistrationsPanel extends BasePanel {
private static final long serialVersionUID = 1L;
private final boolean hasRegistrations;
public FederationRegistrationsPanel(String wicketId) {
super(wicketId);
final List<FederationModel> list = new ArrayList<FederationModel>(GitBlit.self()
.getFederationRegistrations());
list.addAll(GitBlit.self().getFederationResultRegistrations());
Collections.sort(list);
hasRegistrations = list.size() > 0;
DataView<FederationModel> dataView = new DataView<FederationModel>("row",
new ListDataProvider<FederationModel>(list)) {
private static final long serialVersionUID = 1L;
private int counter;
@Override
protected void onBeforeRender() {
super.onBeforeRender();
counter = 0;
}
public void populateItem(final Item<FederationModel> item) {
final FederationModel entry = item.getModelObject();
item.add(new LinkPanel("url", "list", entry.url, FederationRegistrationPage.class,
WicketUtils.newRegistrationParameter(entry.url, entry.name)));
item.add(WicketUtils.getPullStatusImage("statusIcon", entry.getLowestStatus()));
item.add(new LinkPanel("name", "list", entry.name,
FederationRegistrationPage.class, WicketUtils.newRegistrationParameter(
entry.url, entry.name)));
item.add(WicketUtils.getRegistrationImage("typeIcon", entry, this));
item.add(WicketUtils.createDateLabel("lastPull", entry.lastPull, getTimeZone()));
item.add(WicketUtils
.createTimestampLabel("nextPull", entry.nextPull, getTimeZone()));
item.add(new Label("frequency", entry.frequency));
WicketUtils.setAlternatingBackground(item, counter);
counter++;
}
};
add(dataView);
}
public Component hideIfEmpty() {
return super.setVisible(hasRegistrations);
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
xml:lang="en"
lang="en">
<body>
<wicket:panel>
<div class="admin_nav">
<a wicket:id="federatedUsers"><wicket:message key="gb.federatedUserDefinitions">[users]</wicket:message></a>
| <a wicket:id="federatedSettings"><wicket:message key="gb.federatedSettingDefinitions">[settings]</wicket:message></a>
</div>
<table class="repositories">
<tr>
<th class="left">
<img style="vertical-align: top; border: 1px solid #888; background-color: white;" src="federated_16x16.png"/>
<wicket:message key="gb.tokens">[tokens]</wicket:message>
</th>
<th></th>
<th class="right"></th>
</tr>
<tbody>
<tr wicket:id="row">
<td class="left"><span wicket:id="description"></span></td>
<td><span class="sha1"" wicket:id="value">[value]</span></td>
<td class="rightAlign"><span class="link"><a wicket:id="repositoryDefinitions"><wicket:message key="gb.federatedRepositoryDefinitions">[repository definitions]</wicket:message></a> | <a wicket:id="send"><wicket:message key="gb.sendProposal">[send proposal]</wicket:message></a></span></td>
</tr>
</tbody>
</table>
</wicket:panel>
</body>
</html>
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.wicket.panels;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import com.gitblit.Constants.FederationRequest;
import com.gitblit.Constants.FederationToken;
import com.gitblit.FederationServlet;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.SendProposalPage;
public class FederationTokensPanel extends BasePanel {
private static final long serialVersionUID = 1L;
public FederationTokensPanel(String wicketId, final boolean showFederation) {
super(wicketId);
final String baseUrl = WicketUtils.getGitblitURL(getRequest());
add(new ExternalLink("federatedUsers", FederationServlet.asFederationLink(baseUrl, GitBlit.self()
.getFederationToken(FederationToken.USERS_AND_REPOSITORIES),
FederationRequest.PULL_USERS)));
add(new ExternalLink("federatedSettings", FederationServlet.asFederationLink(baseUrl, GitBlit
.self().getFederationToken(FederationToken.ALL), FederationRequest.PULL_SETTINGS)));
final List<String[]> data = new ArrayList<String[]>();
for (FederationToken token : FederationToken.values()) {
data.add(new String[] { token.name(), GitBlit.self().getFederationToken(token), null });
}
List<String> sets = GitBlit.getStrings(Keys.federation.sets);
Collections.sort(sets);
for (String set : sets) {
data.add(new String[] { FederationToken.REPOSITORIES.name(),
GitBlit.self().getFederationToken(set), set });
}
DataView<String[]> dataView = new DataView<String[]>("row", new ListDataProvider<String[]>(
data)) {
private static final long serialVersionUID = 1L;
private int counter;
@Override
protected void onBeforeRender() {
super.onBeforeRender();
counter = 0;
}
public void populateItem(final Item<String[]> item) {
final String[] entry = item.getModelObject();
final FederationToken token = FederationToken.fromName(entry[0]);
if (entry[2] == null) {
// standard federation token
item.add(new Label("description", describeToken(token)));
} else {
// federation set token
item.add(new Label("description", entry[2]));
}
item.add(new Label("value", entry[1]));
item.add(new ExternalLink("repositoryDefinitions", FederationServlet.asFederationLink(
baseUrl, entry[1], FederationRequest.PULL_REPOSITORIES)));
item.add(new BookmarkablePageLink<Void>("send",
SendProposalPage.class, WicketUtils.newTokenParameter(entry[1])));
WicketUtils.setAlternatingBackground(item, counter);
counter++;
}
};
add(dataView.setVisible(showFederation));
}
private String describeToken(FederationToken token) {
switch (token) {
case ALL:
return getString("gb.tokenAllDescription");
case USERS_AND_REPOSITORIES:
return getString("gb.tokenUnrDescription");
case REPOSITORIES:
default:
return getString("gb.tokenJurDescription");
}
}
}
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.tests;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import junit.framework.TestCase;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.FederationProposalResult;
import com.gitblit.Constants.FederationRequest;
import com.gitblit.Constants.FederationToken;
import com.gitblit.FederationServlet;
import com.gitblit.GitBlitServer;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.RepositoryModel;
import com.gitblit.utils.FederationUtils;
import com.google.gson.Gson;
public class FederationTests extends TestCase {
int port = 8180;
int shutdownPort = 8181;
@Override
protected void setUp() throws Exception {
// Start a Gitblit instance
Executors.newSingleThreadExecutor().execute(new Runnable() {
public void run() {
GitBlitServer.main("--httpPort", "" + port, "--httpsPort", "0", "--shutdownPort",
"" + shutdownPort, "--repositoriesFolder",
"\"" + GitBlitSuite.REPOSITORIES.getAbsolutePath() + "\"", "--userService",
"distrib/users.properties");
}
});
// Wait a few seconds for it to be running
Thread.sleep(2500);
}
@Override
protected void tearDown() throws Exception {
// Stop Gitblit
GitBlitServer.main("--stop", "--shutdownPort", "" + shutdownPort);
// Wait a few seconds for it to be running
Thread.sleep(2500);
}
public void testDeserialization() throws Exception {
String json = "{\"https://localhost:8443/git/a.b.c.orphan.git\":{\"name\":\"a.b.c.orphan.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 22, 2011 3:15:07 PM\",\"hasCommits\":true,\"showRemoteBranches\":false,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/test/jgit.git\":{\"name\":\"test/jgit.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 13, 2011 9:42:33 AM\",\"hasCommits\":true,\"showRemoteBranches\":true,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/test/helloworld.git\":{\"name\":\"test/helloworld.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 15, 2008 7:26:48 PM\",\"hasCommits\":true,\"showRemoteBranches\":false,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/working/ticgit\":{\"name\":\"working/ticgit\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 22, 2011 10:35:27 AM\",\"hasCommits\":true,\"showRemoteBranches\":false,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/ticgit.git\":{\"name\":\"ticgit.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 22, 2011 10:35:27 AM\",\"hasCommits\":true,\"showRemoteBranches\":true,\"useTickets\":true,\"useDocs\":true,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/helloworld.git\":{\"name\":\"helloworld.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 15, 2008 7:26:48 PM\",\"hasCommits\":true,\"showRemoteBranches\":false,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/test/helloworld3.git\":{\"name\":\"test/helloworld3.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Jul 15, 2008 7:26:48 PM\",\"hasCommits\":true,\"showRemoteBranches\":false,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false},\"https://localhost:8443/git/test/bluez-gnome.git\":{\"name\":\"test/bluez-gnome.git\",\"description\":\"\",\"owner\":\"\",\"lastChange\":\"Dec 19, 2008 6:35:33 AM\",\"hasCommits\":true,\"showRemoteBranches\":false,\"useTickets\":false,\"useDocs\":false,\"accessRestriction\":\"NONE\",\"isFrozen\":false,\"showReadme\":false,\"isFederated\":false}}";
Gson gson = new Gson();
Map<String, RepositoryModel> models = gson
.fromJson(json, FederationUtils.REPOSITORIES_TYPE);
assertEquals(8, models.size());
}
public void testProposal() throws Exception {
// create dummy repository data
Map<String, RepositoryModel> repositories = new HashMap<String, RepositoryModel>();
for (int i = 0; i < 5; i++) {
RepositoryModel model = new RepositoryModel();
model.accessRestriction = AccessRestrictionType.VIEW;
model.description = "cloneable repository " + i;
model.lastChange = new Date();
model.owner = "adminuser";
model.name = "repo" + i + ".git";
model.size = "5 MB";
model.hasCommits = true;
repositories.put(model.name, model);
}
FederationProposal proposal = new FederationProposal("http://testurl", FederationToken.ALL,
"testtoken", repositories);
// propose federation
assertEquals("proposal refused",
FederationUtils.propose("http://localhost:" + port, proposal),
FederationProposalResult.NO_PROPOSALS);
}
public void testPullRepositories() throws Exception {
try {
String url = FederationServlet.asFederationLink("http://localhost:" + port,
"testtoken", FederationRequest.PULL_REPOSITORIES);
String json = FederationUtils.readJson(url);
} catch (IOException e) {
if (!e.getMessage().contains("403")) {
throw e;
}
}
}
}
/*
* Copyright 2011 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.tests;
import javax.mail.Message;
import junit.framework.TestCase;
import com.gitblit.FileSettings;
import com.gitblit.MailExecutor;
public class MailTest extends TestCase {
public void testSendMail() throws Exception {
FileSettings settings = new FileSettings("mailtest.properties");
MailExecutor mail = new MailExecutor(settings);
Message message = mail.createMessageForAdministrators();
message.setSubject("Test");
message.setText("this is a test");
mail.queue(message);
mail.run();
assertTrue("mail queue is not empty!", mail.hasEmptyQueue());
}
}

Sorry, the diff of this file is not supported yet

+4
-0

@@ -15,1 +15,5 @@ /temp

/*.war
/proposals
/*.jar
/federation.properties
/mailtest.properties

@@ -8,2 +8,5 @@ <?xml version="1.0" encoding="UTF-8"?>

<!-- GenJar task -->
<taskdef resource="genjar.properties" classpath="${basedir}/tools/GenJar.jar" />
<!-- Project Properties -->

@@ -17,5 +20,15 @@ <property name="project.jar" value="gitblit.jar" />

<property name="project.resources.dir" value="${basedir}/resources" />
<available property="hasBuildProps" file="${basedir}/build.properties"/>
<!-- Load publication servers, paths, and credentials -->
<loadproperties srcfile="${basedir}/build.properties" />
<!--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Load build.properties, if available
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-->
<target name="buildprops" if="hasBuildProps">
<!-- Load publication servers, paths, and credentials -->
<loadproperties>
<file file="${basedir}/build.properties" />
</loadproperties>
</target>

@@ -28,3 +41,3 @@

-->
<target name="buildinfo">
<target name="buildinfo" depends="buildprops">

@@ -77,2 +90,3 @@ <!-- extract Gitblit version number from source code -->

<property name="distribution.warfile" value="gitblit-${gb.version}.war" />
<property name="fedclient.zipfile" value="fedclient-${gb.version}.zip" />
</target>

@@ -101,3 +115,3 @@

<mkdir dir="${project.build.dir}" />
<javac srcdir="${basedir}/src" destdir="${project.build.dir}">
<javac debug="true" srcdir="${basedir}/src" destdir="${project.build.dir}">
<include name="com/gitblit/build/Build.java" />

@@ -114,4 +128,5 @@ <include name="com/gitblit/Constants.java" />

</fileset>
<pathelement path="${project.build.dir}" />
</path>
<javac destdir="${project.build.dir}" failonerror="false">
<javac debug="true" destdir="${project.build.dir}" failonerror="false">
<src path="${basedir}/src" />

@@ -143,2 +158,3 @@ <classpath refid="master-classpath" />

<include name="**/*" />
<exclude name="federation.properties" />
</fileset>

@@ -202,2 +218,3 @@ <fileset dir="${basedir}">

<include name="blank.png" />
<include name="federated_16x16.png" />
</fileset>

@@ -256,2 +273,5 @@

<arg value="--substitute" />
<arg value="%FEDCLIENT%=${fedclient.zipfile}" />
<arg value="--substitute" />
<arg value="%BUILDDATE%=${gb.versionDate}" />

@@ -365,2 +385,41 @@

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Build the stand-alone, command-line Gitblit Federation Client
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-->
<target name="buildFederationClient" depends="compile" description="Builds the stand-alone Gitblit federation client">
<echo>Building Gitblit Federation Client ${gb.version}</echo>
<genjar jarfile="fedclient.jar">
<class name="com.gitblit.FederationClientLauncher" />
<resource file="${project.build.dir}/log4j.properties" />
<classfilter>
<exclude name="org.apache." />
<exclude name="org.bouncycastle." />
<exclude name="org.eclipse." />
<exclude name="org.slf4j." />
<exclude name="com.beust." />
<exclude name="com.google." />
</classfilter>
<classpath refid="master-classpath" />
<manifest>
<attribute name="Main-Class" value="com.gitblit.FederationClientLauncher" />
<attribute name="Specification-Version" value="${gb.version}" />
<attribute name="Release-Date" value="${gb.versionDate}" />
</manifest>
</genjar>
<!-- Build the federation client zip file -->
<zip destfile="${fedclient.zipfile}">
<fileset dir="${basedir}">
<include name="fedclient.jar" />
</fileset>
<fileset dir="${basedir}/distrib">
<include name="federation.properties" />
</fileset>
</zip>
</target>
<!--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Build the Gitblit Website

@@ -391,2 +450,3 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

<include name="blank.png" />
<include name="federated_16x16.png" />
</fileset>

@@ -476,2 +536,5 @@

<arg value="--substitute" />
<arg value="%FEDCLIENT%=${fedclient.zipfile}" />
<arg value="--substitute" />
<arg value="%BUILDDATE%=${gb.versionDate}" />

@@ -506,3 +569,3 @@

-->
<target name="buildAll" depends="buildGO,buildWAR,buildSite">
<target name="buildAll" depends="buildGO,buildWAR,buildFederationClient,buildSite">
<!-- Cleanup -->

@@ -520,3 +583,3 @@ <delete dir="${project.build.dir}" />

-->
<target name="publishBinaries" depends="buildGO,buildWAR" description="Publish the Gitblit binaries to Google Code">
<target name="publishBinaries" depends="buildGO,buildWAR,buildFederationClient" description="Publish the Gitblit binaries to Google Code">

@@ -544,2 +607,12 @@ <echo>Uploading Gitblit ${gb.version} binaries</echo>

labels="Featured, Type-Package, OpSys-All" />
<!-- Upload FedClient -->
<gcupload
username="${googlecode.user}"
password="${googlecode.password}"
projectname="gitblit"
filename="${fedclient.zipfile}"
targetfilename="fedclient-${gb.version}.zip"
summary="Gitblit Federation Client v${gb.version} (command-line tool to clone data from federated Gitblit instances)"
labels="Featured, Type-Package, OpSys-All" />
</target>

@@ -546,0 +619,0 @@

@@ -107,2 +107,8 @@ #

# Show federation registrations (without token) and the current pull status
# to non-administrator users.
#
# SINCE 0.6.0
web.showFederationRegistrations = false
# This is the message display above the repositories table.

@@ -224,2 +230,3 @@ # This can point to a file with Markdown content.

#
# SPACE-DELIMITED
# SINCE 0.5.0

@@ -230,2 +237,3 @@ web.prettyPrintExtensions = c cpp cs css htm html java js php pl prefs properties py rb sh sql xml vb

#
# SPACE-DELIMITED
# CASE-SENSITIVE

@@ -237,2 +245,3 @@ # SINCE 0.5.0

#
# SPACE-DELIMITED
# SINCE 0.5.0

@@ -243,2 +252,3 @@ web.imageExtensions = bmp jpg gif png

#
# SPACE-DELIMITED
# SINCE 0.5.0

@@ -274,2 +284,157 @@ web.binaryExtensions = jar pdf tar.gz zip

#
# Mail Settings
# SINCE 0.6.0
#
# Mail settings are used to notify administrators of received federation proposals
#
# ip or hostname of smtp server
#
# SINCE 0.6.0
mail.server =
# port to use for smtp requests
#
# SINCE 0.6.0
mail.port = 25
# debug the mail executor
#
# SINCE 0.6.0
mail.debug = false
# if your smtp server requires authentication, supply the credentials here
#
# SINCE 0.6.0
mail.username =
mail.password =
# from address for generated emails
#
# SINCE 0.6.0
mail.fromAddress =
# List of email addresses for the Gitblit administrators
#
# SPACE-DELIMITED
# SINCE 0.6.0
mail.adminAddresses =
#
# Federation Settings
# SINCE 0.6.0
#
# A Gitblit federation is a way to backup one Gitblit instance to another.
#
# *git.enableGitServlet* must be true to use this feature.
# Your federation name is used for federation status acknowledgments. If it is
# unset, and you elect to send a status acknowledgment, your Gitblit instance
# will be identified by its hostname, if available, else your internal ip address.
# The source Gitblit instance will also append your external IP address to your
# identification to differentiate multiple pulling systems behind a single proxy.
#
# SINCE 0.6.0
federation.name =
# Specify the passphrase of this Gitblit instance.
#
# An unspecified (empty) passphrase disables processing federation requests.
#
# This value can be anything you want: an integer, a sentence, an haiku, etc.
# Keep the value simple, though, to avoid Java properties file encoding issues.
#
# Changing your passphrase will break any registrations you have established with other
# Gitblit instances.
#
# CASE-SENSITIVE
# SINCE 0.6.0
# RESTART REQUIRED *(only to enable or disable federation)*
federation.passphrase =
# Control whether or not this Gitblit instance can receive federation proposals
# from another Gitblit instance. Registering a federated Gitblit is a manual
# process. Proposals help to simplify that process by allowing a remote Gitblit
# instance to send your Gitblit instance the federation pull data.
#
# SINCE 0.6.0
federation.allowProposals = false
# The destination folder for cached federation proposals.
# Use forward slashes even on Windows!!
#
# SINCE 0.6.0
federation.proposalsFolder = proposals
# The default pull frequency if frequency is unspecified on a registration
#
# SINCE 0.6.0
federation.defaultFrequency = 60 mins
# Federation Sets are named groups of repositories. The Federation Sets are
# available for selection in the repository settings page. You can assign a
# repository to one or more sets and then distribute the token for the set.
# This allows you to grant federation pull access to a subset of your available
# repositories. Tokens for federation sets only grant repository pull access.
#
# SPACE-DELIMITED
# CASE-SENSITIVE
# SINCE 0.6.0
federation.sets =
# Federation pull registrations
# Registrations are read once, at startup.
#
# RESTART REQUIRED
#
# frequency:
# The shortest frequency allowed is every 5 minutes
# Decimal frequency values are cast to integers
# Frequency values may be specified in mins, hours, or days
# Values that can not be parsed or are unspecified default to *federation.defaultFrequency*
#
# folder:
# if unspecified, the folder is *git.repositoriesFolder*
# if specified, the folder is relative to *git.repositoriesFolder*
#
# bare:
# if true, each repository will be created as a *bare* repository and will not
# have a working directory.
#
# if false, each repository will be created as a normal repository suitable
# for local work.
#
# mirror:
# if true, each repository HEAD is reset to *origin/master* after each pull.
# The repository will be flagged *isFrozen* after the initial clone.
#
# if false, each repository HEAD will point to the FETCH_HEAD of the initial
# clone from the origin until pushed to or otherwise manipulated.
#
# mergeAccounts:
# if true, remote accounts and their permissions are merged into your
# users.properties file
#
# notifyOnError:
# if true and the mail configuration is properly set, administrators will be
# notified by email of pull failures
#
# include and exclude:
# Space-delimited list of repositories to include or exclude from pull
# may be * wildcard to include or exclude all
# may use fuzzy match (e.g. org.eclipse.*)
#
# (Nearly) Perfect Mirror example
#
#federation.example1.url = https://go.gitblit.com
#federation.example1.token = 6f3b8a24bf970f17289b234284c94f43eb42f0e4
#federation.example1.frequency = 120 mins
#federation.example1.folder =
#federation.example1.bare = true
#federation.example1.mirror = true
#federation.example1.mergeAccounts = true
#
# Server Settings

@@ -276,0 +441,0 @@ #

+1
-1

@@ -5,2 +5,2 @@ @REM arch = x86, amd64, or ia32

@REM Delete the gitblit service
"%CD%\%ARCH%\gitblitv.exe" //DS//gitblit
"%CD%\%ARCH%\gitblit.exe" //DS//gitblit
## Gitblit realm file format: username=password,\#permission,repository1,repository2...
#Fri Jul 22 14:27:08 EDT 2011
admin=admin,\#admin
admin=admin,\#admin,\#notfederated
+24
-0

@@ -115,2 +115,18 @@ Gitblit

---------------------------------------------------------------------------
google-gson
---------------------------------------------------------------------------
google-gson, released under the
Apache-style Software License.
http://code.google.com/p/google-gson
---------------------------------------------------------------------------
javamail
---------------------------------------------------------------------------
javamail, released under multiple licenses
CDDL-1.0, BSD, GPL-2.0, GNU-Classpath.
http://kenai.com/projects/javamail
---------------------------------------------------------------------------
JUnit

@@ -130,3 +146,11 @@ ---------------------------------------------------------------------------

http://code.google.com/p/ant-googlecode
---------------------------------------------------------------------------
GenJar
---------------------------------------------------------------------------
GenJar, released under the
Apache Software License, Version 1.1.
http://genjar.sourceforge.net
---------------------------------------------------------------------------

@@ -133,0 +157,0 @@ Fancybox image viewer

@@ -15,3 +15,3 @@ /*

body {
width: 980px;
width: 1000px;
margin: 5px;

@@ -26,2 +26,8 @@ background-color: #ffffff;

hr {
color: #ffffff;
background-color: #ffffff;
height: 1px; !important
}
pre, code, pre.prettyprint, pre.plainprint {

@@ -28,0 +34,0 @@ color: black;

@@ -67,2 +67,8 @@ /*

color: #b05000;
}
div.markdown table.text th, div.markdown table.text td {
vertical-align: top;
border-top: 1px solid #ccc;
padding:5px;
}

@@ -133,3 +133,4 @@ /*

String url = httpRequest.getRequestURI().substring(httpRequest.getServletPath().length());
String servletUrl = httpRequest.getContextPath() + httpRequest.getServletPath();
String url = httpRequest.getRequestURI().substring(servletUrl.length());
String params = httpRequest.getQueryString();

@@ -141,3 +142,3 @@ if (url.length() > 0 && url.charAt(0) == '/') {

String repository = extractRepositoryName(url);
String repository = extractRepositoryName(fullUrl);

@@ -144,0 +145,0 @@ // Determine if the request URL is restricted

@@ -84,2 +84,4 @@ /*

downloadFromApache(MavenObject.JDOM, BuildType.RUNTIME);
downloadFromApache(MavenObject.GSON, BuildType.RUNTIME);
downloadFromApache(MavenObject.MAIL, BuildType.RUNTIME);

@@ -108,2 +110,4 @@ downloadFromEclipse(MavenObject.JGIT, BuildType.RUNTIME);

downloadFromApache(MavenObject.JDOM, BuildType.COMPILETIME);
downloadFromApache(MavenObject.GSON, BuildType.COMPILETIME);
downloadFromApache(MavenObject.MAIL, BuildType.COMPILETIME);

@@ -116,2 +120,15 @@ downloadFromEclipse(MavenObject.JGIT, BuildType.COMPILETIME);

}
public static void federationClient() {
downloadFromApache(MavenObject.JCOMMANDER, BuildType.RUNTIME);
downloadFromApache(MavenObject.SERVLET, BuildType.RUNTIME);
downloadFromApache(MavenObject.MAIL, BuildType.RUNTIME);
downloadFromApache(MavenObject.SLF4JAPI, BuildType.RUNTIME);
downloadFromApache(MavenObject.SLF4LOG4J, BuildType.RUNTIME);
downloadFromApache(MavenObject.LOG4J, BuildType.RUNTIME);
downloadFromApache(MavenObject.GSON, BuildType.RUNTIME);
downloadFromApache(MavenObject.JSCH, BuildType.RUNTIME);
downloadFromEclipse(MavenObject.JGIT, BuildType.RUNTIME);
}

@@ -373,24 +390,24 @@ /**

public static final MavenObject WICKET = new MavenObject("Apache Wicket",
"org/apache/wicket", "wicket", "1.4.17", 1960000, 1906000, 6818000,
"39815e37a6f56465b2d2c3d3017c4f3bf17db50a",
"a792ebae4123253ffd039c3be49e773f8622f94e",
"f2f244ca72d10081529b017e89d6276eab62c621");
"org/apache/wicket", "wicket", "1.4.18", 1960000, 1906000, 6818000,
"921a50dbbebdf034f0042f2294760e7535cb7041",
"b432d60b32449fdfb216ac23af8a3ed3e0a3368c",
"435e70f9de94975ee30c3f1b1aa1401aea9b4e70");
public static final MavenObject WICKET_EXT = new MavenObject("Apache Wicket Extensions",
"org/apache/wicket", "wicket-extensions", "1.4.17", 1180000, 1118000, 1458000,
"01111d0dbffdc425581b006a43864c22797ce72a",
"f194f40ea6e361bb745dfa22e2f9171eb63a9355",
"bd42e5ba9444a426bb2d7cacce91c6033b663b57");
"org/apache/wicket", "wicket-extensions", "1.4.18", 1180000, 1118000, 1458000,
"f568bd2ad382db935ab06fdccfdead3f10ed1f15",
"c00a4979d7647d3367c6e4897a2fd7d0f78a73cc",
"5e76ab69f6307e3ecb2638779008b3adf5cbf9aa");
public static final MavenObject WICKET_AUTH_ROLES = new MavenObject(
"Apache Wicket Auth Roles", "org/apache/wicket", "wicket-auth-roles", "1.4.17",
44000, 45000, 166000, "86d20ff32f62d3026213ff11a78555da643bc676",
"37e815350a2d6b97734b250a8a03d8bf3712bba7",
"ac3896368bfb372d178041a4ac3ee2c44f62e21c");
"Apache Wicket Auth Roles", "org/apache/wicket", "wicket-auth-roles", "1.4.18",
44000, 45000, 166000, "44cf0647e1adca377cc4258cd7fac33aa1dd11ab",
"2a7e9c6a9687136c2527afa2e53148cfa82696c6",
"6e280995097e84b72b283132b8fe6796595caa38");
public static final MavenObject WICKET_GOOGLE_CHARTS = new MavenObject(
"Apache Wicket Google Charts Add-On", "org/wicketstuff", "googlecharts", "1.4.17",
34000, 18750, 161000, "c567b98b0c5efe4147e77ef2d0d3c2d45c49dea5",
"3d32d958b2f7aa58388af5701ea3aafc433e573f",
"c37518b67ea85af485dd61fe854137eeacc50318");
"Apache Wicket Google Charts Add-On", "org/wicketstuff", "googlecharts", "1.4.18",
34000, 18750, 161000, "1f763cc8a04e62840b63787a77a479b04ad99c75",
"1521ed6397192c464e89787502f937bc96ece8f8",
"8b0398d58bce63ba7f7a9232c4ca24160c9b1a11");

@@ -401,6 +418,6 @@ public static final MavenObject JUNIT = new MavenObject("JUnit", "junit", "junit", "4.8.2",

public static final MavenObject MARKDOWNPAPERS = new MavenObject("MarkdownPapers",
"org/tautua/markdownpapers", "markdownpapers-core", "1.1.0", 87000, 58000, 278000,
"b879b4720fa642d3c490ab559af132daaa16dbb4",
"d98c53939815be2777d5a56dcdc3bbc9ddb468fa",
"4c09d2d3073e85b973572292af00bd69681df76b");
"org/tautua/markdownpapers", "markdownpapers-core", "1.1.1", 87000, 58000, 278000,
"07046e6d8f33866398dfc3955698925df9ff7719",
"178b49c34dbab6301ce848b67e7957bcf9b94d6a",
"160d370f6cb119a1b46a00f37cc28d23fd27daed");

@@ -420,12 +437,12 @@ public static final MavenObject BOUNCYCASTLE = new MavenObject("BouncyCastle",

public static final MavenObject JGIT = new MavenObject("JGit", "org/eclipse/jgit",
"org.eclipse.jgit", "1.0.0.201106090707-r", 1318000, 1354000, 2993000,
"34e70691382d67ee5c84ef207fb8d3784594ba2c",
"78dbd385cf40cb266f4fb2de8651b288a72f4e2d",
"dab55685bb6eee8d07cc87faf0cedaa3f9d04a0d");
"org.eclipse.jgit", "1.1.0.201109151100-r", 1318000, 1354000, 3300000,
"bacc988346c839f79513d7bc7f5c88b22ea6e7a5",
"90abf988d98ce0d4b162f94f63fc99c435eba6b4",
"a46540a2857a0fdbf43debf3383295a897946c79");
public static final MavenObject JGIT_HTTP = new MavenObject("JGit", "org/eclipse/jgit",
"org.eclipse.jgit.http.server", "1.0.0.201106090707-r", 68000, 62000, 99000,
"35e22f7000af95d0c90caaf2012071ef3734ff59",
"4a2368beb1e9db4a6a0d609b7b869f218bf8e7a9",
"3100ce7c40d6968481a12377c59c708cda2d17b5");
"org.eclipse.jgit.http.server", "1.1.0.201109151100-r", 68000, 62000, 110000,
"3070161a89756aac2dfc2e26d89faf31fe894ab4",
"9cecb8e4351e616688cafbcca906f542d9b1f525",
"20aaab759acd8eb6cb6acbb1b2934a689fb3774d");

@@ -451,2 +468,12 @@ public static final MavenObject JSCH = new MavenObject("JSch", "com/jcraft", "jsch",

public static final MavenObject GSON = new MavenObject("gson", "com/google/code/gson",
"gson", "1.7.1", 174000, 142000, 247000,
"0697e3a1fa094a983cd12f7f6f61abf9c6ea52e2",
"51f6f78aec2d30d0c2bfb4a5f00d456a6f7a5e7e",
"f0872fe17d484815328538b89909d5e46d85db74");
public static final MavenObject MAIL = new MavenObject("javax.mail", "javax/mail", "mail",
"1.4.3", 462000, 642000, 0, "8154bf8d666e6db154c548dc31a8d512c273f5ee",
"5875e2729de83a4e46391f8f979ec8bd03810c10", null);
public final String name;

@@ -453,0 +480,0 @@ public final String group;

@@ -58,2 +58,4 @@ /*

private static final String SPACE_DELIMITED = "SPACE-DELIMITED";
private static final String CASE_SENSITIVE = "CASE-SENSITIVE";

@@ -269,3 +271,3 @@

if (comment.contains(SINCE) || comment.contains(RESTART_REQUIRED)
|| comment.contains(CASE_SENSITIVE)) {
|| comment.contains(CASE_SENSITIVE) || comment.contains(SPACE_DELIMITED)) {
sb.append(MessageFormat.format(

@@ -272,0 +274,0 @@ "<span style=\"color:#004000;\"># <i>{0}</i></span>",

@@ -32,14 +32,16 @@ /*

// and only use A-Z a-z 0-9 .-_ in the string.
public static final String VERSION = "0.5.2";
public static final String VERSION = "0.6.0";
// The build script extracts this exact line so be careful editing it
// and only use A-Z a-z 0-9 .-_ in the string.
public static final String VERSION_DATE = "2011-07-27";
public static final String VERSION_DATE = "2011-09-27";
// The build script extracts this exact line so be careful editing it
// and only use A-Z a-z 0-9 .-_ in the string.
public static final String JGIT_VERSION = "JGit 1.0.0 (201106090707-r)";
public static final String JGIT_VERSION = "JGit 1.1.0 (201109151100-r)";
public static final String ADMIN_ROLE = "#admin";
public static final String NOT_FEDERATED_ROLE = "#notfederated";
public static final String PROPERTIES_FILE = "gitblit.properties";

@@ -53,6 +55,16 @@

public static final String FEDERATION_PATH = "/federation/";
public static final String BORDER = "***********************************************************";
public static final String FEDERATION_USER = "$gitblit";
public static final String PROPOSAL_EXT = ".json";
public static String getGitBlitVersion() {
return NAME + " v" + VERSION;
}
/**
* Enumeration representing the 4 access restriction levels.
* Enumeration representing the four access restriction levels.
*/

@@ -84,5 +96,104 @@ public static enum AccessRestrictionType {

public static String getGitBlitVersion() {
return NAME + " v" + VERSION;
/**
* Enumeration representing the types of federation tokens.
*/
public static enum FederationToken {
ALL, USERS_AND_REPOSITORIES, REPOSITORIES;
public static FederationToken fromName(String name) {
for (FederationToken type : values()) {
if (type.name().equalsIgnoreCase(name)) {
return type;
}
}
return REPOSITORIES;
}
public String toString() {
return name();
}
}
/**
* Enumeration representing the types of federation requests.
*/
public static enum FederationRequest {
POKE, PROPOSAL, PULL_REPOSITORIES, PULL_USERS, PULL_SETTINGS, STATUS;
public static FederationRequest fromName(String name) {
for (FederationRequest type : values()) {
if (type.name().equalsIgnoreCase(name)) {
return type;
}
}
return PULL_REPOSITORIES;
}
public String toString() {
return name();
}
}
/**
* Enumeration representing the statii of federation requests.
*/
public static enum FederationPullStatus {
PENDING, FAILED, SKIPPED, PULLED, MIRRORED, NOCHANGE, EXCLUDED;
public static FederationPullStatus fromName(String name) {
for (FederationPullStatus type : values()) {
if (type.name().equalsIgnoreCase(name)) {
return type;
}
}
return PENDING;
}
@Override
public String toString() {
return name();
}
}
/**
* Enumeration representing the federation types.
*/
public static enum FederationStrategy {
EXCLUDE, FEDERATE_THIS, FEDERATE_ORIGIN;
public static FederationStrategy fromName(String name) {
for (FederationStrategy type : values()) {
if (type.name().equalsIgnoreCase(name)) {
return type;
}
}
return FEDERATE_THIS;
}
public boolean exceeds(FederationStrategy type) {
return this.ordinal() > type.ordinal();
}
public boolean atLeast(FederationStrategy type) {
return this.ordinal() >= type.ordinal();
}
@Override
public String toString() {
return name();
}
}
/**
* Enumeration representing the possible results of federation proposal
* requests.
*/
public static enum FederationProposalResult {
ERROR, FEDERATION_DISABLED, MISSING_DATA, NO_PROPOSALS, NO_POKE, ACCEPTED;
@Override
public String toString() {
return name();
}
}
}

@@ -28,4 +28,2 @@ /*

import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.models.RepositoryModel;
import com.gitblit.utils.JGitUtils;

@@ -38,8 +36,2 @@ import com.gitblit.utils.StringUtils;

*
* Unlike the GitServlet and the SyndicationServlet, this servlet is not
* protected by an AccessRestrictionFilter. It performs its own authorization
* check, but it does not perform any authentication. The assumption is that
* requests to this servlet are made via the web ui and not by direct url
* access. Unauthorized requests fail with a standard 403 (FORBIDDEN) code.
*
* @author James Moger

@@ -77,3 +69,3 @@ *

/**
* Performs the authorization and zip streaming of the specified elements.
* Creates a zip stream from the repository of the requested data.
*

@@ -92,4 +84,4 @@ * @param request

return;
}
String repository = request.getParameter("r");

@@ -105,14 +97,2 @@ String basePath = request.getParameter("p");

// check roles first
boolean authorized = request.isUserInRole(Constants.ADMIN_ROLE);
authorized |= request.isUserInRole(repository);
if (!authorized) {
RepositoryModel model = GitBlit.self().getRepositoryModel(repository);
if (model.accessRestriction.atLeast(AccessRestrictionType.VIEW)) {
logger.warn("Unauthorized access via zip servlet for " + model.name);
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
}
if (!StringUtils.isEmpty(basePath)) {

@@ -119,0 +99,0 @@ name += "-" + basePath.replace('/', '_');

@@ -152,2 +152,4 @@ /*

model.canAdmin = true;
} else if (role.equalsIgnoreCase(Constants.NOT_FEDERATED_ROLE)) {
model.excludeFromFederation = true;
}

@@ -193,2 +195,5 @@ break;

}
if (model.excludeFromFederation) {
roles.add(Constants.NOT_FEDERATED_ROLE);
}

@@ -504,11 +509,12 @@ StringBuilder sb = new StringBuilder();

if (realmFileCopy.exists() && realmFileCopy.length() > 0) {
if (propertiesFile.delete()) {
if (!realmFileCopy.renameTo(propertiesFile)) {
throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!",
realmFileCopy.getAbsolutePath(), propertiesFile.getAbsolutePath()));
if (propertiesFile.exists()) {
if (!propertiesFile.delete()) {
throw new IOException(MessageFormat.format("Failed to delete {0}!",
propertiesFile.getAbsolutePath()));
}
} else {
throw new IOException(MessageFormat.format("Failed to delete (0)!",
propertiesFile.getAbsolutePath()));
}
if (!realmFileCopy.renameTo(propertiesFile)) {
throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!",
realmFileCopy.getAbsolutePath(), propertiesFile.getAbsolutePath()));
}
} else {

@@ -515,0 +521,0 @@ throw new IOException(MessageFormat.format("Failed to save {0}!",

@@ -19,2 +19,3 @@ /*

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;

@@ -24,2 +25,3 @@ import java.lang.reflect.Field;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

@@ -30,4 +32,10 @@ import java.util.HashMap;

import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.servlet.ServletContextEvent;

@@ -52,6 +60,14 @@ import javax.servlet.ServletContextListener;

import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.FederationRequest;
import com.gitblit.Constants.FederationStrategy;
import com.gitblit.Constants.FederationToken;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.FederationUtils;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

@@ -80,2 +96,9 @@ /**

private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(5);
private final List<FederationModel> federationRegistrations = Collections
.synchronizedList(new ArrayList<FederationModel>());
private final Map<String, FederationModel> federationPullResults = new ConcurrentHashMap<String, FederationModel>();
private RepositoryResolver<Void> repositoryResolver;

@@ -91,2 +114,4 @@

private MailExecutor mailExecutor;
public GitBlit() {

@@ -112,2 +137,11 @@ if (gitblit == null) {

/**
* Determine if this is the GO variant of Gitblit.
*
* @return true if this is the GO variant of Gitblit.
*/
public static boolean isGO() {
return self().settings instanceof FileSettings;
}
/**
* Returns the boolean value for the specified key. If the key does not

@@ -236,2 +270,26 @@ * exist or the value for the key can not be interpreted as a boolean, the

public UserModel authenticate(String username, char[] password) {
if (StringUtils.isEmpty(username)) {
// can not authenticate empty username
return null;
}
String pw = new String(password);
if (StringUtils.isEmpty(pw)) {
// can not authenticate empty password
return null;
}
// check to see if this is the federation user
if (canFederate()) {
if (username.equalsIgnoreCase(Constants.FEDERATION_USER)) {
List<String> tokens = getFederationTokens();
if (tokens.contains(pw)) {
// the federation user is an administrator
UserModel federationUser = new UserModel(Constants.FEDERATION_USER);
federationUser.canAdmin = true;
return federationUser;
}
}
}
// delegate authentication to the user service
if (userService == null) {

@@ -474,2 +532,8 @@ return null;

model.showReadme = getConfig(config, "showReadme", false);
model.federationStrategy = FederationStrategy.fromName(getConfig(config,
"federationStrategy", null));
model.federationSets = new ArrayList<String>(Arrays.asList(config.getStringList(
"gitblit", null, "federationSets")));
model.isFederated = getConfig(config, "isFederated", false);
model.origin = config.getString("remote", "origin", "url");
}

@@ -626,17 +690,3 @@ r.close();

if (r != null) {
StoredConfig config = JGitUtils.readConfig(r);
config.setString("gitblit", null, "description", repository.description);
config.setString("gitblit", null, "owner", repository.owner);
config.setBoolean("gitblit", null, "useTickets", repository.useTickets);
config.setBoolean("gitblit", null, "useDocs", repository.useDocs);
config.setString("gitblit", null, "accessRestriction",
repository.accessRestriction.name());
config.setBoolean("gitblit", null, "showRemoteBranches", repository.showRemoteBranches);
config.setBoolean("gitblit", null, "isFrozen", repository.isFrozen);
config.setBoolean("gitblit", null, "showReadme", repository.showReadme);
try {
config.save();
} catch (IOException e) {
logger.error("Failed to save repository config!", e);
}
updateConfiguration(r, repository);
r.close();

@@ -647,2 +697,31 @@ }

/**
* Updates the Gitblit configuration for the specified repository.
*
* @param r
* the Git repository
* @param repository
* the Gitblit repository model
*/
public void updateConfiguration(Repository r, RepositoryModel repository) {
StoredConfig config = JGitUtils.readConfig(r);
config.setString("gitblit", null, "description", repository.description);
config.setString("gitblit", null, "owner", repository.owner);
config.setBoolean("gitblit", null, "useTickets", repository.useTickets);
config.setBoolean("gitblit", null, "useDocs", repository.useDocs);
config.setString("gitblit", null, "accessRestriction", repository.accessRestriction.name());
config.setBoolean("gitblit", null, "showRemoteBranches", repository.showRemoteBranches);
config.setBoolean("gitblit", null, "isFrozen", repository.isFrozen);
config.setBoolean("gitblit", null, "showReadme", repository.showReadme);
config.setStringList("gitblit", null, "federationSets", repository.federationSets);
config.setString("gitblit", null, "federationStrategy",
repository.federationStrategy.name());
config.setBoolean("gitblit", null, "isFederated", repository.isFederated);
try {
config.save();
} catch (IOException e) {
logger.error("Failed to save repository config!", e);
}
}
/**
* Deletes the repository from the file system and removes the repository

@@ -725,2 +804,375 @@ * permission from all repository users.

/**
* Returns Gitblit's scheduled executor service for scheduling tasks.
*
* @return scheduledExecutor
*/
public ScheduledExecutorService executor() {
return scheduledExecutor;
}
public static boolean canFederate() {
String passphrase = getString(Keys.federation.passphrase, "");
return !StringUtils.isEmpty(passphrase);
}
/**
* Configures this Gitblit instance to pull any registered federated gitblit
* instances.
*/
private void configureFederation() {
boolean validPassphrase = true;
String passphrase = settings.getString(Keys.federation.passphrase, "");
if (StringUtils.isEmpty(passphrase)) {
logger.warn("Federation passphrase is blank! This server can not be PULLED from.");
validPassphrase = false;
}
if (validPassphrase) {
// standard tokens
for (FederationToken tokenType : FederationToken.values()) {
logger.info(MessageFormat.format("Federation {0} token = {1}", tokenType.name(),
getFederationToken(tokenType)));
}
// federation set tokens
for (String set : settings.getStrings(Keys.federation.sets)) {
logger.info(MessageFormat.format("Federation Set {0} token = {1}", set,
getFederationToken(set)));
}
}
// Schedule the federation executor
List<FederationModel> registrations = getFederationRegistrations();
if (registrations.size() > 0) {
FederationPullExecutor executor = new FederationPullExecutor(registrations, true);
scheduledExecutor.schedule(executor, 1, TimeUnit.MINUTES);
}
}
/**
* Returns the list of federated gitblit instances that this instance will
* try to pull.
*
* @return list of registered gitblit instances
*/
public List<FederationModel> getFederationRegistrations() {
if (federationRegistrations.isEmpty()) {
federationRegistrations.addAll(FederationUtils.getFederationRegistrations(settings));
}
return federationRegistrations;
}
/**
* Retrieve the specified federation registration.
*
* @param name
* the name of the registration
* @return a federation registration
*/
public FederationModel getFederationRegistration(String url, String name) {
// check registrations
for (FederationModel r : getFederationRegistrations()) {
if (r.name.equals(name) && r.url.equals(url)) {
return r;
}
}
// check the results
for (FederationModel r : getFederationResultRegistrations()) {
if (r.name.equals(name) && r.url.equals(url)) {
return r;
}
}
return null;
}
/**
* Returns the list of possible federation tokens for this Gitblit instance.
*
* @return list of federation tokens
*/
public List<String> getFederationTokens() {
List<String> tokens = new ArrayList<String>();
// generate standard tokens
for (FederationToken type : FederationToken.values()) {
tokens.add(getFederationToken(type));
}
// generate tokens for federation sets
for (String set : settings.getStrings(Keys.federation.sets)) {
tokens.add(getFederationToken(set));
}
return tokens;
}
/**
* Returns the specified federation token for this Gitblit instance.
*
* @param type
* @return a federation token
*/
public String getFederationToken(FederationToken type) {
return getFederationToken(type.name());
}
/**
* Returns the specified federation token for this Gitblit instance.
*
* @param value
* @return a federation token
*/
public String getFederationToken(String value) {
String passphrase = settings.getString(Keys.federation.passphrase, "");
return StringUtils.getSHA1(passphrase + "-" + value);
}
/**
* Compares the provided token with this Gitblit instance's tokens and
* determines if the requested permission may be granted to the token.
*
* @param req
* @param token
* @return true if the request can be executed
*/
public boolean validateFederationRequest(FederationRequest req, String token) {
String all = getFederationToken(FederationToken.ALL);
String unr = getFederationToken(FederationToken.USERS_AND_REPOSITORIES);
String jur = getFederationToken(FederationToken.REPOSITORIES);
switch (req) {
case PULL_REPOSITORIES:
return token.equals(all) || token.equals(unr) || token.equals(jur);
case PULL_USERS:
return token.equals(all) || token.equals(unr);
case PULL_SETTINGS:
return token.equals(all);
}
return false;
}
/**
* Acknowledge and cache the status of a remote Gitblit instance.
*
* @param identification
* the identification of the pulling Gitblit instance
* @param registration
* the registration from the pulling Gitblit instance
* @return true if acknowledged
*/
public boolean acknowledgeFederationStatus(String identification, FederationModel registration) {
// reset the url to the identification of the pulling Gitblit instance
registration.url = identification;
String id = identification;
if (!StringUtils.isEmpty(registration.folder)) {
id += "-" + registration.folder;
}
federationPullResults.put(id, registration);
return true;
}
/**
* Returns the list of registration results.
*
* @return the list of registration results
*/
public List<FederationModel> getFederationResultRegistrations() {
return new ArrayList<FederationModel>(federationPullResults.values());
}
/**
* Submit a federation proposal. The proposal is cached locally and the
* Gitblit administrator(s) are notified via email.
*
* @param proposal
* the proposal
* @param gitblitUrl
* the url of your gitblit instance to send an email to
* administrators
* @return true if the proposal was submitted
*/
public boolean submitFederationProposal(FederationProposal proposal, String gitblitUrl) {
// convert proposal to json
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(proposal);
try {
// make the proposals folder
File proposalsFolder = new File(getString(Keys.federation.proposalsFolder, "proposals")
.trim());
proposalsFolder.mkdirs();
// cache json to a file
File file = new File(proposalsFolder, proposal.token + Constants.PROPOSAL_EXT);
com.gitblit.utils.FileUtils.writeContent(file, json);
} catch (Exception e) {
logger.error(MessageFormat.format("Failed to cache proposal from {0}", proposal.url), e);
}
// send an email, if possible
try {
Message message = mailExecutor.createMessageForAdministrators();
if (message != null) {
message.setSubject("Federation proposal from " + proposal.url);
message.setText("Please review the proposal @ " + gitblitUrl + "/proposal/"
+ proposal.token);
mailExecutor.queue(message);
}
} catch (Throwable t) {
logger.error("Failed to notify administrators of proposal", t);
}
return true;
}
/**
* Returns the list of pending federation proposals
*
* @return list of federation proposals
*/
public List<FederationProposal> getPendingFederationProposals() {
List<FederationProposal> list = new ArrayList<FederationProposal>();
File folder = new File(getString(Keys.federation.proposalsFolder, "proposals").trim());
if (folder.exists()) {
File[] files = folder.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.isFile()
&& file.getName().toLowerCase().endsWith(Constants.PROPOSAL_EXT);
}
});
Gson gson = new Gson();
for (File file : files) {
String json = com.gitblit.utils.FileUtils.readContent(file, null);
FederationProposal proposal = gson.fromJson(json, FederationProposal.class);
list.add(proposal);
}
}
return list;
}
/**
* Get repositories for the specified token.
*
* @param gitblitUrl
* the base url of this gitblit instance
* @param token
* the federation token
* @return a map of <cloneurl, RepositoryModel>
*/
public Map<String, RepositoryModel> getRepositories(String gitblitUrl, String token) {
Map<String, String> federationSets = new HashMap<String, String>();
for (String set : getStrings(Keys.federation.sets)) {
federationSets.put(getFederationToken(set), set);
}
// Determine the Gitblit clone url
StringBuilder sb = new StringBuilder();
sb.append(gitblitUrl);
sb.append(Constants.GIT_PATH);
sb.append("{0}");
String cloneUrl = sb.toString();
// Retrieve all available repositories
UserModel user = new UserModel(Constants.FEDERATION_USER);
user.canAdmin = true;
List<RepositoryModel> list = getRepositoryModels(user);
// create the [cloneurl, repositoryModel] map
Map<String, RepositoryModel> repositories = new HashMap<String, RepositoryModel>();
for (RepositoryModel model : list) {
// by default, setup the url for THIS repository
String url = MessageFormat.format(cloneUrl, model.name);
switch (model.federationStrategy) {
case EXCLUDE:
// skip this repository
continue;
case FEDERATE_ORIGIN:
// federate the origin, if it is defined
if (!StringUtils.isEmpty(model.origin)) {
url = model.origin;
}
break;
}
if (federationSets.containsKey(token)) {
// include repositories only for federation set
String set = federationSets.get(token);
if (model.federationSets.contains(set)) {
repositories.put(url, model);
}
} else {
// standard federation token for ALL
repositories.put(url, model);
}
}
return repositories;
}
/**
* Creates a proposal from the token.
*
* @param gitblitUrl
* the url of this Gitblit instance
* @param token
* @return a potential proposal
*/
public FederationProposal createFederationProposal(String gitblitUrl, String token) {
FederationToken tokenType = FederationToken.REPOSITORIES;
for (FederationToken type : FederationToken.values()) {
if (token.equals(getFederationToken(type))) {
tokenType = type;
break;
}
}
Map<String, RepositoryModel> repositories = getRepositories(gitblitUrl, token);
FederationProposal proposal = new FederationProposal(gitblitUrl, tokenType, token,
repositories);
return proposal;
}
/**
* Returns the proposal identified by the supplied token.
*
* @param token
* @return the specified proposal or null
*/
public FederationProposal getPendingFederationProposal(String token) {
List<FederationProposal> list = getPendingFederationProposals();
for (FederationProposal proposal : list) {
if (proposal.token.equals(token)) {
return proposal;
}
}
return null;
}
/**
* Deletes a pending federation proposal.
*
* @param a
* proposal
* @return true if the proposal was deleted
*/
public boolean deletePendingFederationProposal(FederationProposal proposal) {
File folder = new File(getString(Keys.federation.proposalsFolder, "proposals").trim());
File file = new File(folder, proposal.token + Constants.PROPOSAL_EXT);
return file.delete();
}
/**
* Notify the administrators by email.
*
* @param subject
* @param message
*/
public void notifyAdministrators(String subject, String message) {
try {
Message mail = mailExecutor.createMessageForAdministrators();
if (mail != null) {
mail.setSubject(subject);
mail.setText(message);
mailExecutor.queue(mail);
}
} catch (MessagingException e) {
logger.error("Messaging error", e);
}
}
/**
* Configure the Gitblit singleton with the specified settings source. This

@@ -732,3 +1184,3 @@ * source may be file settings (Gitblit GO) or may be web.xml settings

*/
public void configureContext(IStoredSettings settings) {
public void configureContext(IStoredSettings settings, boolean startFederation) {
logger.info("Reading configuration from " + settings.toString());

@@ -762,2 +1214,11 @@ this.settings = settings;

setUserService(loginService);
mailExecutor = new MailExecutor(settings);
if (mailExecutor.isReady()) {
scheduledExecutor.scheduleAtFixedRate(mailExecutor, 1, 2, TimeUnit.MINUTES);
} else {
logger.warn("Mail server is not properly configured. Mail services disabled.");
}
if (startFederation) {
configureFederation();
}
}

@@ -776,3 +1237,3 @@

WebXmlSettings webxmlSettings = new WebXmlSettings(contextEvent.getServletContext());
configureContext(webxmlSettings);
configureContext(webxmlSettings, true);
}

@@ -788,3 +1249,4 @@ }

logger.info("Gitblit context destroyed by servlet container.");
scheduledExecutor.shutdownNow();
}
}

@@ -240,3 +240,3 @@ /*

GitBlit gitblit = GitBlit.self();
gitblit.configureContext(settings);
gitblit.configureContext(settings, true);
rootContext.addEventListener(gitblit);

@@ -243,0 +243,0 @@

@@ -64,3 +64,3 @@ /*

*
* @param url
* @param serverUrl
* @return action of the request

@@ -110,7 +110,8 @@ */

}
if (repository.isFrozen || repository.accessRestriction.atLeast(AccessRestrictionType.PUSH)) {
boolean readOnly = repository.isFrozen;
if (readOnly || repository.accessRestriction.atLeast(AccessRestrictionType.PUSH)) {
boolean authorizedUser = user.canAccessRepository(repository.name);
if (action.equals(gitReceivePack)) {
// Push request
if (!repository.isFrozen && authorizedUser) {
if (!readOnly && authorizedUser) {
// clone-restricted or push-authorized

@@ -117,0 +118,0 @@ return true;

@@ -89,3 +89,3 @@ /*

if (!StringUtils.isEmpty(value)) {
return Boolean.parseBoolean(value);
return Boolean.parseBoolean(value.trim());
}

@@ -111,3 +111,3 @@ }

if (!StringUtils.isEmpty(value)) {
return Integer.parseInt(value);
return Integer.parseInt(value.trim());
}

@@ -136,3 +136,3 @@ } catch (NumberFormatException e) {

if (!StringUtils.isEmpty(value)) {
return value.charAt(0);
return value.trim().charAt(0);
}

@@ -157,3 +157,3 @@ }

if (value != null) {
return value;
return value.trim();
}

@@ -160,0 +160,0 @@ }

@@ -28,2 +28,3 @@ /*

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

@@ -78,2 +79,6 @@

}
// sort the jars by name and then reverse the order so the newer version
// of the library gets loaded in the event that this is an upgrade
Collections.sort(jars);
Collections.reverse(jars);

@@ -83,3 +88,5 @@ if (jars.size() == 0) {

File libFolder = new File(folder);
System.err.println("Failed to find any JARs in " + libFolder.getPath());
// this is a test of adding a comment
// more really interesting things
System.err.println("Failed to find any really cool JARs in " + libFolder.getPath());
}

@@ -90,2 +97,3 @@ System.exit(-1);

try {
jar.canRead();
addJarFile(jar);

@@ -120,2 +128,3 @@ } catch (Throwable t) {

}
return jars;

@@ -122,0 +131,0 @@ }

@@ -19,5 +19,8 @@ /*

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.FederationStrategy;

@@ -31,3 +34,3 @@ /**

*/
public class RepositoryModel implements Serializable {
public class RepositoryModel implements Serializable, Comparable<RepositoryModel> {

@@ -48,2 +51,8 @@ private static final long serialVersionUID = 1L;

public boolean showReadme;
public FederationStrategy federationStrategy;
public List<String> federationSets;
public boolean isFederated;
public String frequency;
public String origin;
public String size;

@@ -60,2 +69,3 @@ public RepositoryModel() {

this.accessRestriction = AccessRestrictionType.NONE;
federationSets = new ArrayList<String>();
}

@@ -67,2 +77,7 @@

}
@Override
public int compareTo(RepositoryModel o) {
return name.compareTo(o.name);
}
}

@@ -20,4 +20,4 @@ /*

import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import java.util.HashSet;
import java.util.Set;

@@ -40,3 +40,4 @@ /**

public boolean canAdmin;
public final List<String> repositories = new ArrayList<String>();
public boolean excludeFromFederation;
public final Set<String> repositories = new HashSet<String>();

@@ -43,0 +44,0 @@ public UserModel(String username) {

@@ -29,6 +29,6 @@ /*

import com.gitblit.models.RepositoryModel;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.SyndicationUtils;
import com.gitblit.wicket.WicketUtils;

@@ -120,4 +120,4 @@ /**

String hostURL = WicketUtils.getHostURL(request);
String url = request.getRequestURI().substring(request.getServletPath().length());
String servletUrl = request.getContextPath() + request.getServletPath();
String url = request.getRequestURI().substring(servletUrl.length());
if (url.charAt(0) == '/' && url.length() > 1) {

@@ -144,3 +144,3 @@ url = url.substring(1);

try {
SyndicationUtils.toRSS(hostURL, getTitle(model.name, objectId), model.description,
SyndicationUtils.toRSS(HttpUtils.getGitblitURL(request), getTitle(model.name, objectId), model.description,
model.name, commits, response.getOutputStream());

@@ -147,0 +147,0 @@ } catch (Exception e) {

@@ -40,2 +40,4 @@ /*

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ResetCommand;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.eclipse.jgit.diff.DiffEntry;

@@ -66,2 +68,3 @@ import org.eclipse.jgit.diff.DiffEntry.ChangeType;

import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.FetchResult;

@@ -139,2 +142,11 @@ import org.eclipse.jgit.transport.RefSpec;

/**
* Encapsulates the result of cloning or pulling from a repository.
*/
public static class CloneResult {
public String name;
public FetchResult fetchResult;
public boolean createdRepository;
}
/**
* Clone or Fetch a repository. If the local repository does not exist,

@@ -147,11 +159,39 @@ * clone is called. If the repository does exist, fetch is called. By

* @param fromUrl
* @return FetchResult
* @return CloneResult
* @throws Exception
*/
public static FetchResult cloneRepository(File repositoriesFolder, String name, String fromUrl)
public static CloneResult cloneRepository(File repositoriesFolder, String name, String fromUrl)
throws Exception {
FetchResult result = null;
if (!name.toLowerCase().endsWith(Constants.DOT_GIT_EXT)) {
name += Constants.DOT_GIT_EXT;
return cloneRepository(repositoriesFolder, name, fromUrl, true, null);
}
/**
* Clone or Fetch a repository. If the local repository does not exist,
* clone is called. If the repository does exist, fetch is called. By
* default the clone/fetch retrieves the remote heads, tags, and notes.
*
* @param repositoriesFolder
* @param name
* @param fromUrl
* @param bare
* @param credentialsProvider
* @return CloneResult
* @throws Exception
*/
public static CloneResult cloneRepository(File repositoriesFolder, String name, String fromUrl,
boolean bare, CredentialsProvider credentialsProvider) throws Exception {
CloneResult result = new CloneResult();
if (bare) {
// bare repository, ensure .git suffix
if (!name.toLowerCase().endsWith(Constants.DOT_GIT_EXT)) {
name += Constants.DOT_GIT_EXT;
}
} else {
// normal repository, strip .git suffix
if (name.toLowerCase().endsWith(Constants.DOT_GIT_EXT)) {
name = name.substring(0, name.indexOf(Constants.DOT_GIT_EXT));
}
}
result.name = name;
File folder = new File(repositoriesFolder, name);

@@ -161,10 +201,13 @@ if (folder.exists()) {

FileRepository repository = new FileRepository(gitDir);
result = fetchRepository(repository);
result.fetchResult = fetchRepository(credentialsProvider, repository);
repository.close();
} else {
CloneCommand clone = new CloneCommand();
clone.setBare(true);
clone.setBare(bare);
clone.setCloneAllBranches(true);
clone.setURI(fromUrl);
clone.setDirectory(folder);
if (credentialsProvider != null) {
clone.setCredentialsProvider(credentialsProvider);
}
clone.call();

@@ -175,3 +218,4 @@ // Now we have to fetch because CloneCommand doesn't fetch

FileRepository repository = new FileRepository(gitDir);
result = fetchRepository(repository);
result.createdRepository = true;
result.fetchResult = fetchRepository(credentialsProvider, repository);
repository.close();

@@ -186,2 +230,3 @@ }

*
* @param credentialsProvider
* @param repository

@@ -192,4 +237,4 @@ * @param refSpecs

*/
public static FetchResult fetchRepository(Repository repository, RefSpec... refSpecs)
throws Exception {
public static FetchResult fetchRepository(CredentialsProvider credentialsProvider,
Repository repository, RefSpec... refSpecs) throws Exception {
Git git = new Git(repository);

@@ -205,4 +250,28 @@ FetchCommand fetch = git.fetch();

}
if (credentialsProvider != null) {
fetch.setCredentialsProvider(credentialsProvider);
}
fetch.setRefSpecs(specs);
FetchResult result = fetch.call();
FetchResult fetchRes = fetch.call();
return fetchRes;
}
/**
* Reset HEAD to the latest remote tracking commit.
*
* @param repository
* @param remoteRef
* the remote tracking reference (e.g. origin/master)
* @return Ref
* @throws Exception
*/
public static Ref resetHEAD(Repository repository, String remoteRef) throws Exception {
if (!remoteRef.startsWith(Constants.R_REMOTES)) {
remoteRef = Constants.R_REMOTES + remoteRef;
}
Git git = new Git(repository);
ResetCommand reset = git.reset();
reset.setMode(ResetType.SOFT);
reset.setRef(remoteRef);
Ref result = reset.call();
return result;

@@ -209,0 +278,0 @@ }

@@ -345,2 +345,55 @@ /*

}
/**
* Validates that a name is composed of letters, digits, or limited other
* characters.
*
* @param name
* @return the first invalid character found or null if string is acceptable
*/
public static Character findInvalidCharacter(String name) {
char[] validChars = { '/', '.', '_', '-' };
for (char c : name.toCharArray()) {
if (!Character.isLetterOrDigit(c)) {
boolean ok = false;
for (char vc : validChars) {
ok |= c == vc;
}
if (!ok) {
return c;
}
}
}
return null;
}
/**
* Simple fuzzy string comparison. This is a case-insensitive check. A
* single wildcard * value is supported.
*
* @param value
* @param pattern
* @return true if the value matches the pattern
*/
public static boolean fuzzyMatch(String value, String pattern) {
if (value.equalsIgnoreCase(pattern)) {
return true;
}
if (pattern.contains("*")) {
boolean prefixMatches = false;
boolean suffixMatches = false;
int wildcard = pattern.indexOf('*');
String prefix = pattern.substring(0, wildcard).toLowerCase();
prefixMatches = value.toLowerCase().startsWith(prefix);
if (pattern.length() > (wildcard + 1)) {
String suffix = pattern.substring(wildcard + 1).toLowerCase();
suffixMatches = value.toLowerCase().endsWith(suffix);
return prefixMatches && suffixMatches;
}
return prefixMatches || suffixMatches;
}
return false;
}
}

@@ -21,2 +21,4 @@ /*

import com.gitblit.models.FederationModel;
/**

@@ -242,2 +244,39 @@ * Utility class of time functions.

}
/**
* Convert a frequency string into minutes.
*
* @param frequency
* @return minutes
*/
public static int convertFrequencyToMinutes(String frequency) {
// parse the frequency
frequency = frequency.toLowerCase();
int mins = 60;
if (!StringUtils.isEmpty(frequency)) {
try {
String str;
if (frequency.indexOf(' ') > -1) {
str = frequency.substring(0, frequency.indexOf(' ')).trim();
} else {
str = frequency.trim();
}
mins = (int) Float.parseFloat(str);
} catch (NumberFormatException e) {
}
if (mins < 5) {
mins = 5;
}
}
if (frequency.indexOf("day") > -1) {
// convert to minutes
mins *= 24 * 60;
} else if (frequency.indexOf("hour") > -1) {
// convert to minutes
mins *= 60;
} else if (frequency.indexOf("min") > -1) {
// default mins
}
return mins;
}
}

@@ -35,2 +35,4 @@ /*

import com.gitblit.wicket.pages.DocsPage;
import com.gitblit.wicket.pages.ReviewProposalPage;
import com.gitblit.wicket.pages.FederationRegistrationPage;
import com.gitblit.wicket.pages.HistoryPage;

@@ -99,2 +101,6 @@ import com.gitblit.wicket.pages.LogPage;

// federation urls
mount("/proposal", ReviewProposalPage.class, "t");
mount("/registration", FederationRegistrationPage.class, "u", "n");
// setup login/logout urls, if we are using authentication

@@ -101,0 +107,0 @@ if (useAuthentication) {

@@ -104,2 +104,39 @@ gb.repository = repository

gb.cancel = cancel
gb.changePassword = change password
gb.changePassword = change password
gb.isFederated = is federated
gb.federateThis = federate this repository
gb.federateOrigin = federate the origin
gb.excludeFromFederation = exclude from federation
gb.excludeFromFederationDescription = block federated Gitblit instances from pulling this object
gb.tokens = federation tokens
gb.tokenAllDescription = all repositories, users, & settings
gb.tokenUnrDescription = all repositories & users
gb.tokenJurDescription = all repositories
gb.federatedRepositoryDefinitions = repository definitions
gb.federatedUserDefinitions = user definitions
gb.federatedSettingDefinitions = setting definitions
gb.proposals = federation proposals
gb.received = received
gb.type = type
gb.token = token
gb.repositories = repositories
gb.proposal = proposal
gb.frequency = frequency
gb.folder = folder
gb.lastPull = last pull
gb.nextPull = next pull
gb.inclusions = inclusions
gb.exclusions = exclusions
gb.registration = registration
gb.registrations = federation registrations
gb.sendProposal propose
gb.status = status
gb.origin = origin
gb.federationStrategy = federation strategy
gb.federationRegistration = federation registration
gb.federationResults = federation pull results
gb.federationSets = federation sets
gb.message = message
gb.myUrlDescription = the publicly accessible url for your Gitblit instance
gb.destinationUrl = send to
gb.destinationUrlDescription = the url of the Gitblit instance to send your proposal

@@ -42,2 +42,3 @@ /*

import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.FederationStrategy;
import com.gitblit.GitBlit;

@@ -144,2 +145,20 @@ import com.gitblit.Keys;

}
protected Map<FederationStrategy, String> getFederationTypes() {
Map<FederationStrategy, String> map = new LinkedHashMap<FederationStrategy, String>();
for (FederationStrategy type : FederationStrategy.values()) {
switch (type) {
case EXCLUDE:
map.put(type, getString("gb.excludeFromFederation"));
break;
case FEDERATE_THIS:
map.put(type, getString("gb.federateThis"));
break;
case FEDERATE_ORIGIN:
map.put(type, getString("gb.federateOrigin"));
break;
}
}
return map;
}

@@ -146,0 +165,0 @@ protected TimeZone getTimeZone() {

@@ -95,5 +95,5 @@ /*

add(new BookmarkablePageLink<Void>("treeLink", TreePage.class, newCommitParameter()));
add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest()
.getRelativePathPrefixToContextRoot(), repositoryName, objectId, null))
.setVisible(GitBlit.getBoolean(Keys.web.allowZipDownloads, true)));
final String baseUrl = WicketUtils.getGitblitURL(getRequest());
add(new ExternalLink("zipLink", DownloadZipServlet.asLink(baseUrl, repositoryName,
objectId, null)).setVisible(GitBlit.getBoolean(Keys.web.allowZipDownloads, true)));

@@ -100,0 +100,0 @@ // Parent Commits

@@ -20,11 +20,16 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<tr><th><wicket:message key="gb.description"></wicket:message></th><td class="edit"><input type="text" wicket:id="description" size="40" tabindex="2" /></td></tr>
<tr><th><wicket:message key="gb.owner"></wicket:message></th><td class="edit"><select wicket:id="owner" tabindex="3" /> &nbsp;<i><wicket:message key="gb.ownerDescription"></wicket:message></i></td></tr>
<tr><th><wicket:message key="gb.enableTickets"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="useTickets" tabindex="4" /> &nbsp;<i><wicket:message key="gb.useTicketsDescription"></wicket:message></i></td></tr>
<tr><th><wicket:message key="gb.enableDocs"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="useDocs" tabindex="5" /> &nbsp;<i><wicket:message key="gb.useDocsDescription"></wicket:message></i></td></tr>
<tr><th><wicket:message key="gb.showRemoteBranches"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="showRemoteBranches" tabindex="6" /> &nbsp;<i><wicket:message key="gb.showRemoteBranchesDescription"></wicket:message></i></td></tr>
<tr><th><wicket:message key="gb.showReadme"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="showReadme" tabindex="7" /> &nbsp;<i><wicket:message key="gb.showReadmeDescription"></wicket:message></i></td></tr>
<tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select wicket:id="accessRestriction" tabindex="8" /></td></tr>
<tr><th><wicket:message key="gb.origin"></wicket:message></th><td class="edit"><input type="text" wicket:id="origin" size="80" tabindex="3" /></td></tr>
<tr><th><wicket:message key="gb.owner"></wicket:message></th><td class="edit"><select wicket:id="owner" tabindex="4" /> &nbsp;<i><wicket:message key="gb.ownerDescription"></wicket:message></i></td></tr>
<tr><th><wicket:message key="gb.enableTickets"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="useTickets" tabindex="5" /> &nbsp;<i><wicket:message key="gb.useTicketsDescription"></wicket:message></i></td></tr>
<tr><th><wicket:message key="gb.enableDocs"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="useDocs" tabindex="6" /> &nbsp;<i><wicket:message key="gb.useDocsDescription"></wicket:message></i></td></tr>
<tr><th><wicket:message key="gb.showRemoteBranches"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="showRemoteBranches" tabindex="7" /> &nbsp;<i><wicket:message key="gb.showRemoteBranchesDescription"></wicket:message></i></td></tr>
<tr><th><wicket:message key="gb.showReadme"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="showReadme" tabindex="8" /> &nbsp;<i><wicket:message key="gb.showReadmeDescription"></wicket:message></i></td></tr>
<tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="isFrozen" tabindex="9" /> &nbsp;<i><wicket:message key="gb.isFrozenDescription"></wicket:message></i></td></tr>
<tr><th style="vertical-align: top;"><wicket:message key="gb.permittedUsers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>
<tr><th></th><td class="editButton"><input type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="10" /> <input type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="11" /></td></tr>
<tr><td style="padding-top:10px;" colspan="2"><hr></hr></td></tr>
<tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select wicket:id="accessRestriction" tabindex="10" /></td></tr>
<tr><th style="vertical-align: top;"><wicket:message key="gb.permittedUsers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>
<tr><td style="padding-top:10px;" colspan="2"><hr></hr></td></tr>
<tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select wicket:id="federationStrategy" tabindex="11" /></td></tr>
<tr><th style="vertical-align: top;"><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr>
<tr><th></th><td class="editButton"><input type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="12" /> <input type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="13" /></td></tr>
</tbody>

@@ -31,0 +36,0 @@ </table>

@@ -40,2 +40,3 @@ /*

import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.FederationStrategy;
import com.gitblit.GitBlit;

@@ -76,2 +77,3 @@ import com.gitblit.GitBlitException;

List<String> federationSets = new ArrayList<String>();
List<String> repositoryUsers = new ArrayList<String>();

@@ -86,8 +88,17 @@ if (isCreate) {

}
federationSets.addAll(repositoryModel.federationSets);
}
final String oldName = repositoryModel.name;
// users palette
final Palette<String> usersPalette = new Palette<String>("users", new ListModel<String>(
repositoryUsers), new CollectionModel<String>(GitBlit.self().getAllUsernames()),
new ChoiceRenderer<String>("", ""), 10, false);
// federation sets palette
List<String> sets = GitBlit.getStrings(Keys.federation.sets);
final Palette<String> federationSetsPalette = new Palette<String>("federationSets",
new ListModel<String>(federationSets), new CollectionModel<String>(sets),
new ChoiceRenderer<String>("", ""), 10, false);
CompoundPropertyModel<RepositoryModel> model = new CompoundPropertyModel<RepositoryModel>(

@@ -128,15 +139,7 @@ repositoryModel);

// confirm valid characters in repository name
char[] validChars = { '/', '.', '_', '-' };
for (char c : repositoryModel.name.toCharArray()) {
if (!Character.isLetterOrDigit(c)) {
boolean ok = false;
for (char vc : validChars) {
ok |= c == vc;
}
if (!ok) {
error(MessageFormat.format(
"Illegal character ''{0}'' in repository name!", c));
return;
}
}
Character c = StringUtils.findInvalidCharacter(repositoryModel.name);
if (c != null) {
error(MessageFormat.format("Illegal character ''{0}'' in repository name!",
c));
return;
}

@@ -150,2 +153,11 @@

// save federation set preferences
if (repositoryModel.federationStrategy.exceeds(FederationStrategy.EXCLUDE)) {
repositoryModel.federationSets.clear();
Iterator<String> sets = federationSetsPalette.getSelectedChoices();
while (sets.hasNext()) {
repositoryModel.federationSets.add(sets.next());
}
}
// save the repository

@@ -185,2 +197,14 @@ GitBlit.self().updateRepositoryModel(oldName, repositoryModel, isCreate);

form.add(new CheckBox("isFrozen"));
// TODO enable origin definition
form.add(new TextField<String>("origin").setEnabled(false/* isCreate */));
// federation strategies - remove ORIGIN choice if this repository has
// no origin.
List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>(
Arrays.asList(FederationStrategy.values()));
if (StringUtils.isEmpty(repositoryModel.origin)) {
federationStrategies.remove(FederationStrategy.FEDERATE_ORIGIN);
}
form.add(new DropDownChoice<FederationStrategy>("federationStrategy", federationStrategies,
new FederationTypeRenderer()));
form.add(new CheckBox("useTickets"));

@@ -191,2 +215,3 @@ form.add(new CheckBox("useDocs"));

form.add(usersPalette);
form.add(federationSetsPalette);

@@ -274,2 +299,23 @@ form.add(new Button("save"));

}
private class FederationTypeRenderer implements IChoiceRenderer<FederationStrategy> {
private static final long serialVersionUID = 1L;
private final Map<FederationStrategy, String> map;
public FederationTypeRenderer() {
map = getFederationTypes();
}
@Override
public String getDisplayValue(FederationStrategy type) {
return map.get(type);
}
@Override
public String getIdValue(FederationStrategy type, int index) {
return Integer.toString(index);
}
}
}

@@ -22,4 +22,5 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<tr><th><wicket:message key="gb.canAdmin"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="canAdmin" tabindex="6" /> &nbsp;<i><wicket:message key="gb.canAdminDescription"></wicket:message></i></td></tr>
<tr><th><wicket:message key="gb.excludeFromFederation"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="excludeFromFederation" tabindex="7" /> &nbsp;<i><wicket:message key="gb.excludeFromFederationDescription"></wicket:message></i></td></tr>
<tr><th style="vertical-align: top;"><wicket:message key="gb.restrictedRepositories"></wicket:message></th><td style="padding:2px;"><span wicket:id="repositories"></span></td></tr>
<tr><th></th><td class="editButton"><input type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="7" /> <input type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="8" /></td></tr>
<tr><th></th><td class="editButton"><input type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="8" /> <input type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="9" /></td></tr>
</tbody>

@@ -26,0 +27,0 @@ </table>

@@ -86,4 +86,4 @@ /*

final Palette<String> repositories = new Palette<String>("repositories",
new ListModel<String>(userModel.repositories), new CollectionModel<String>(repos),
new ChoiceRenderer<String>("", ""), 10, false);
new ListModel<String>(new ArrayList<String>(userModel.repositories)),
new CollectionModel<String>(repos), new ChoiceRenderer<String>("", ""), 10, false);
Form<UserModel> form = new Form<UserModel>("editForm", model) {

@@ -176,2 +176,3 @@

form.add(new CheckBox("canAdmin"));
form.add(new CheckBox("excludeFromFederation"));
form.add(repositories);

@@ -178,0 +179,0 @@

@@ -72,2 +72,7 @@ /*

error("Invalid username or password!");
} else if (user.username.equals(Constants.FEDERATION_USER)) {
// disallow the federation user from logging in via the
// web ui
error("Invalid username or password!");
user = null;
} else {

@@ -74,0 +79,0 @@ loginUser(user);

@@ -9,28 +9,37 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<wicket:extend>
<div style="padding-top:10px;">
<!-- branch name -->
<div><span class="metricsTitle" wicket:id="branchTitle"></span></div>
<!-- placeholder for more info -->
<div style="float:right;width:200px;text-align: left;">
</div>
<!-- branch stats -->
<h2><wicket:message key="gb.stats"></wicket:message></h2>
<span wicket:id="branchStats"></span>
<!-- commit activity trend -->
<h2><wicket:message key="gb.commitActivityTrend"></wicket:message></h2>
<div><img wicket:id="commitsChart" /></div>
<!-- commit activity by day of week -->
<h2><wicket:message key="gb.commitActivityDOW"></wicket:message></h2>
<div><img wicket:id="dayOfWeekChart" /></div>
<!-- commit activity by primary authors -->
<h2><wicket:message key="gb.commitActivityAuthors"></wicket:message></h2>
<div><img wicket:id="authorsChart" /></div>
</div>
<div style="padding-top:10px;">
<!-- branch name -->
<div><span class="metricsTitle" wicket:id="branchTitle"></span></div>
<table>
<tr>
<!-- branch stats -->
<td colspan=2>
<h2><wicket:message key="gb.stats"></wicket:message></h2>
<span wicket:id="branchStats"></span>
</td>
</tr>
<tr>
<!-- commit activity trend -->
<td>
<h2><wicket:message key="gb.commitActivityTrend"></wicket:message></h2>
<div><img wicket:id="commitsChart" /></div>
</td>
<!-- commit activity by day of week -->
<td>
<h2><wicket:message key="gb.commitActivityDOW"></wicket:message></h2>
<div><img wicket:id="dayOfWeekChart" /></div>
</td>
</tr>
<tr>
<!-- commit activity by primary authors -->
<td colspan=2>
<h2><wicket:message key="gb.commitActivityAuthors"></wicket:message></h2>
<div style="text-align: center;"><img wicket:id="authorsChart" /></div>
</td>
</tr>
</table>
</div>
</wicket:extend>
</body>
</html>

@@ -124,3 +124,3 @@ /*

}
ChartProvider provider = new ChartProvider(new Dimension(500, 200), ChartType.PIE, data);
ChartProvider provider = new ChartProvider(new Dimension(800, 200), ChartType.PIE, data);
provider.setPieLabels(labels.toArray(new String[labels.size()]));

@@ -127,0 +127,0 @@ add(new Chart(wicketId, provider));

@@ -22,2 +22,8 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<div style="padding-top: 10px;"wicket:id="usersPanel">[users panel]</div>
<div style="padding-top: 10px;"wicket:id="federationTokensPanel">[federation tokens panel]</div>
<div style="padding-top: 10px;"wicket:id="federationProposalsPanel">[federation proposals panel]</div>
<div style="padding-top: 10px;"wicket:id="federationRegistrationsPanel">[federation registrations panel]</div>

@@ -24,0 +30,0 @@ </wicket:extend>

@@ -22,2 +22,3 @@ /*

import java.io.InputStreamReader;
import java.text.MessageFormat;

@@ -34,2 +35,5 @@ import org.apache.wicket.Component;

import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.FederationProposalsPanel;
import com.gitblit.wicket.panels.FederationRegistrationsPanel;
import com.gitblit.wicket.panels.FederationTokensPanel;
import com.gitblit.wicket.panels.RepositoriesPanel;

@@ -65,2 +69,10 @@ import com.gitblit.wicket.panels.UsersPanel;

error(cachedMessage);
} else if (showAdmin) {
int pendingProposals = GitBlit.self().getPendingFederationProposals().size();
if (pendingProposals == 1) {
info("There is 1 federation proposal awaiting review.");
} else if (pendingProposals > 1) {
info(MessageFormat.format("There are {0} federation proposals awaiting review.",
pendingProposals));
}
}

@@ -103,5 +115,26 @@

add(repositoriesMessage);
add(new RepositoriesPanel("repositoriesPanel", showAdmin, getAccessRestrictions()));
add(new RepositoriesPanel("repositoriesPanel", showAdmin, null, getAccessRestrictions()));
add(new UsersPanel("usersPanel", showAdmin).setVisible(showAdmin));
boolean showFederation = showAdmin && GitBlit.canFederate();
add(new FederationTokensPanel("federationTokensPanel", showFederation)
.setVisible(showFederation));
FederationProposalsPanel proposalsPanel = new FederationProposalsPanel(
"federationProposalsPanel");
if (showFederation) {
proposalsPanel.hideIfEmpty();
} else {
proposalsPanel.setVisible(false);
}
boolean showRegistrations = GitBlit.getBoolean(Keys.web.showFederationRegistrations, false);
FederationRegistrationsPanel registrationsPanel = new FederationRegistrationsPanel(
"federationRegistrationsPanel");
if (showAdmin || showRegistrations) {
registrationsPanel.hideIfEmpty();
} else {
registrationsPanel.setVisible(false);
}
add(proposalsPanel);
add(registrationsPanel);
}
}

@@ -117,3 +117,3 @@ /*

StringBuilder sb = new StringBuilder();
sb.append(WicketUtils.getHostURL(getRequestCycle().getRequest()));
sb.append(WicketUtils.getGitblitURL(getRequestCycle().getRequest()));
sb.append(Constants.GIT_PATH);

@@ -120,0 +120,0 @@ sb.append(repositoryName);

@@ -81,2 +81,4 @@ /*

final String baseUrl = WicketUtils.getGitblitURL(getRequest());
// changed paths list

@@ -116,6 +118,5 @@ ListDataProvider<PathModel> pathsDp = new ListDataProvider<PathModel>(paths);

entry.path)));
links.add(new ExternalLink("zip", DownloadZipServlet.asLink(getRequest()
.getRelativePathPrefixToContextRoot(), repositoryName, objectId,
entry.path)).setVisible(GitBlit.getBoolean(
Keys.web.allowZipDownloads, true)));
links.add(new ExternalLink("zip", DownloadZipServlet.asLink(baseUrl,
repositoryName, objectId, entry.path)).setVisible(GitBlit
.getBoolean(Keys.web.allowZipDownloads, true)));
item.add(links);

@@ -122,0 +123,0 @@ } else {

@@ -70,2 +70,21 @@ /*

}
public static class JavascriptTextPrompt extends AttributeModifier {
private static final long serialVersionUID = 1L;
public JavascriptTextPrompt(String event, String msg) {
super(event, true, new Model<String>(msg));
}
protected String newValue(final String currentValue, final String message) {
String result = "var userText = prompt('" + message + "','"
+ (currentValue == null ? "" : currentValue) + "'); " + "return userText; ";
// String result = prefix;
// if (currentValue != null) {
// result = prefix + currentValue;
// }
return result;
}
}
}

@@ -77,3 +77,3 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<td class="author"><span wicket:id="repositoryOwner">[repository owner]</span></td>
<td style="text-align: right;padding-right:10px;"><img class="inlineIcon" wicket:id="ticketsIcon" /><img class="inlineIcon" wicket:id="docsIcon" /><img class="inlineIcon" wicket:id="frozenIcon" /><img class="inlineIcon" wicket:id="accessRestrictionIcon" /></td>
<td style="text-align: right;padding-right:10px;"><img class="inlineIcon" wicket:id="ticketsIcon" /><img class="inlineIcon" wicket:id="docsIcon" /><img class="inlineIcon" wicket:id="frozenIcon" /><img class="inlineIcon" wicket:id="federatedIcon" /><img class="inlineIcon" wicket:id="accessRestrictionIcon" /></td>
<td><span wicket:id="repositoryLastChange">[last change]</span></td>

@@ -80,0 +80,0 @@ <td style="text-align: right;padding-right:15px;"><span style="font-size:0.8em;" wicket:id="repositorySize">[repository size]</span></td>

@@ -63,7 +63,25 @@ /*

public RepositoriesPanel(String wicketId, final boolean showAdmin,
List<RepositoryModel> models,
final Map<AccessRestrictionType, String> accessRestrictionTranslations) {
super(wicketId);
final boolean linksActive;
final boolean showSize = GitBlit.getBoolean(Keys.web.showRepositorySizes, true);
final UserModel user = GitBlitWebSession.get().getUser();
List<RepositoryModel> models = GitBlit.self().getRepositoryModels(user);
if (models == null) {
linksActive = true;
models = GitBlit.self().getRepositoryModels(user);
final ByteFormat byteFormat = new ByteFormat();
if (showSize) {
for (RepositoryModel model : models) {
model.size = byteFormat.format(GitBlit.self().calculateSize(model));
}
}
} else {
// disable links if the repositories are already provided
// the repositories are most likely from a proposal
linksActive = false;
}
final IDataProvider<RepositoryModel> dp;

@@ -104,2 +122,3 @@

groupedModels.add(new GroupRepositoryModel(root, subModels.size()));
Collections.sort(subModels);
groupedModels.addAll(subModels);

@@ -111,5 +130,5 @@ }

}
final String baseUrl = WicketUtils.getGitblitURL(getRequest());
final boolean showSize = GitBlit.getBoolean(Keys.web.showRepositorySizes, true);
final ByteFormat byteFormat = new ByteFormat();
DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("row", dp) {

@@ -136,4 +155,3 @@ private static final long serialVersionUID = 1L;

item.add(row);
if (entry.hasCommits) {
// Existing repository
if (entry.hasCommits && linksActive) {
PageParameters pp = WicketUtils.newRepositoryParameter(entry.name);

@@ -144,5 +162,12 @@ row.add(new LinkPanel("repositoryName", "list", entry.name, SummaryPage.class,

SummaryPage.class, pp));
} else {
// new/empty repository OR proposed repository
row.add(new Label("repositoryName", entry.name));
row.add(new Label("repositoryDescription", entry.description));
}
if (entry.hasCommits) {
// Existing repository
if (showSize) {
row.add(new Label("repositorySize", byteFormat.format(GitBlit.self()
.calculateSize(entry))));
row.add(new Label("repositorySize", entry.size));
} else {

@@ -153,4 +178,2 @@ row.add(new Label("repositorySize").setVisible(false));

// New repository
row.add(new Label("repositoryName", entry.name));
row.add(new Label("repositoryDescription", entry.description));
row.add(new Label("repositorySize", "<span class='empty'>(empty)</span>")

@@ -180,2 +203,9 @@ .setEscapeModelStrings(false));

}
if (entry.isFederated) {
row.add(WicketUtils.newImage("federatedIcon", "federated_16x16.png",
getString("gb.isFederated")));
} else {
row.add(WicketUtils.newClearPixel("federatedIcon").setVisible(false));
}
switch (entry.accessRestriction) {

@@ -253,4 +283,4 @@ case NONE:

}
row.add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest()
.getRelativePathPrefixToContextRoot(), entry.name, null, 0)));
row.add(new ExternalLink("syndication", SyndicationServlet.asLink(baseUrl,
entry.name, null, 0)).setVisible(linksActive));
WicketUtils.setAlternatingBackground(item, counter);

@@ -257,0 +287,0 @@ counter++;

@@ -42,5 +42,8 @@ /*

import com.gitblit.Constants.FederationPullStatus;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.models.FederationModel;
import com.gitblit.models.Metric;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.JGitUtils.SearchType;

@@ -113,2 +116,26 @@ import com.gitblit.utils.StringUtils;

public static ContextImage getPullStatusImage(String wicketId, FederationPullStatus status) {
String filename = null;
switch (status) {
case MIRRORED:
case PULLED:
filename = "bullet_green.png";
break;
case SKIPPED:
filename = "bullet_yellow.png";
break;
case FAILED:
filename = "bullet_red.png";
break;
case EXCLUDED:
filename = "bullet_white.png";
break;
case PENDING:
case NOCHANGE:
default:
filename = "bullet_black.png";
}
return WicketUtils.newImage(wicketId, filename, status.name());
}
public static ContextImage getFileImage(String wicketId, String filename) {

@@ -160,2 +187,13 @@ filename = filename.toLowerCase();

public static ContextImage getRegistrationImage(String wicketId, FederationModel registration,
Component c) {
if (registration.isResultData()) {
return WicketUtils.newImage(wicketId, "information_16x16.png",
c.getString("gb.federationResults"));
} else {
return WicketUtils.newImage(wicketId, "arrow_left.png",
c.getString("gb.federationRegistration"));
}
}
public static ContextImage newClearPixel(String wicketId) {

@@ -185,19 +223,7 @@ return newImage(wicketId, "pixel.png");

public static String getHostURL(Request request) {
public static String getGitblitURL(Request request) {
HttpServletRequest req = ((WebRequest) request).getHttpServletRequest();
return getHostURL(req);
return HttpUtils.getGitblitURL(req);
}
public static String getHostURL(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
sb.append(request.getScheme());
sb.append("://");
sb.append(request.getServerName());
if ((request.getScheme().equals("http") && request.getServerPort() != 80)
|| (request.getScheme().equals("https") && request.getServerPort() != 443)) {
sb.append(":" + request.getServerPort());
}
return sb.toString();
}
public static HeaderContributor syndicationDiscoveryLink(final String feedTitle,

@@ -220,2 +246,10 @@ final String url) {

}
public static PageParameters newTokenParameter(String token) {
return new PageParameters("t=" + token);
}
public static PageParameters newRegistrationParameter(String url, String name) {
return new PageParameters("u=" + url + ",n=" + name);
}

@@ -230,2 +264,6 @@ public static PageParameters newUsernameParameter(String username) {

public static PageParameters newObjectParameter(String objectId) {
return new PageParameters("h=" + objectId);
}
public static PageParameters newObjectParameter(String repositoryName, String objectId) {

@@ -333,2 +371,14 @@ if (StringUtils.isEmpty(objectId)) {

public static String getToken(PageParameters params) {
return params.getString("t", "");
}
public static String getUrlParameter(PageParameters params) {
return params.getString("u", "");
}
public static String getNameParameter(PageParameters params) {
return params.getString("n", "");
}
public static Label createDateLabel(String wicketId, Date date, TimeZone timeZone) {

@@ -340,4 +390,13 @@ String format = GitBlit.getString(Keys.web.datestampShortFormat, "MM/dd/yy");

}
String dateString = df.format(date);
String title = TimeUtils.timeAgo(date);
String dateString;
if (date.getTime() == 0) {
dateString = "--";
} else {
dateString = df.format(date);
}
String title = null;
if (date.getTime() <= System.currentTimeMillis()) {
// past
title = TimeUtils.timeAgo(date);
}
if ((System.currentTimeMillis() - date.getTime()) < 10 * 24 * 60 * 60 * 1000L) {

@@ -350,3 +409,5 @@ String tmp = dateString;

WicketUtils.setCssClass(label, TimeUtils.timeAgoCss(date));
WicketUtils.setHtmlTooltip(label, title);
if (!StringUtils.isEmpty(title)) {
WicketUtils.setHtmlTooltip(label, title);
}
return label;

@@ -368,5 +429,11 @@ }

}
String title = TimeUtils.timeAgo(date);
String title = null;
if (date.getTime() <= System.currentTimeMillis()) {
// past
title = TimeUtils.timeAgo(date);
}
Label label = new Label(wicketId, dateString);
WicketUtils.setHtmlTooltip(label, title);
if (!StringUtils.isEmpty(title)) {
WicketUtils.setHtmlTooltip(label, title);
}
return label;

@@ -373,0 +440,0 @@ }

@@ -58,2 +58,16 @@ <?xml version="1.0" encoding="UTF-8"?>

<!-- Federation Servlet
<url-pattern> MUST match:
* com.gitblit.Constants.FEDERATION_PATH
* Wicket Filter ignorePaths parameter -->
<servlet>
<servlet-name>FederationServlet</servlet-name>
<servlet-class>com.gitblit.FederationServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FederationServlet</servlet-name>
<url-pattern>/federation/*</url-pattern>
</servlet-mapping>

@@ -88,3 +102,17 @@ <!-- Git Access Restriction Filter

</filter-mapping>
<!-- Download Zip Restriction Filter
<url-pattern> MUST match:
* DownloadZipServlet
* com.gitblit.Constants.ZIP_PATH
* Wicket Filter ignorePaths parameter -->
<filter>
<filter-name>ZipFilter</filter-name>
<filter-class>com.gitblit.DownloadZipFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ZipFilter</filter-name>
<url-pattern>/zip/*</url-pattern>
</filter-mapping>

@@ -110,5 +138,7 @@ <!-- Wicket Filter -->

* com.gitblit.Constants.GIT_PATH
* Zipfilter <url-pattern>
* ZipServlet <url-pattern>
* com.gitblit.Constants.ZIP_PATH -->
<param-value>git/,feed/,zip/</param-value>
* com.gitblit.Constants.ZIP_PATH
* FederationServlet <url-pattern> -->
<param-value>git/,feed/,zip/,federation/</param-value>
</init-param>

@@ -115,0 +145,0 @@ </filter>

@@ -76,3 +76,3 @@ /*

FileSettings settings = new FileSettings("distrib/gitblit.properties");
GitBlit.self().configureContext(settings);
GitBlit.self().configureContext(settings, true);
FileUserService loginService = new FileUserService(new File("distrib/users.properties"));

@@ -79,0 +79,0 @@ GitBlit.self().setUserService(loginService);

@@ -27,4 +27,12 @@ /*

public void testMarkdown() throws Exception {
assertTrue(MarkdownUtils.transformMarkdown("# H1").equals("<h1> H1</h1>"));
assertTrue(MarkdownUtils.transformMarkdown("## H2").equals("<h2> H2</h2>"));
assertEquals("<h1> H1</h1>", MarkdownUtils.transformMarkdown("# H1"));
assertEquals("<h2> H2</h2>", MarkdownUtils.transformMarkdown("## H2"));
assertEquals("<p><strong>THIS</strong> is a test</p>",
MarkdownUtils.transformMarkdown("**THIS** is a test"));
assertEquals("<p>** THIS ** is a test</p>",
MarkdownUtils.transformMarkdown("** THIS ** is a test"));
assertEquals("<p>**THIS ** is a test</p>",
MarkdownUtils.transformMarkdown("**THIS ** is a test"));
assertEquals("<p>** THIS** is a test</p>",
MarkdownUtils.transformMarkdown("** THIS** is a test"));
try {

@@ -31,0 +39,0 @@ MarkdownUtils.transformMarkdown((String) null);

@@ -106,2 +106,20 @@ /*

}
public void testStringsFromValue2() throws Exception {
List<String> strings = StringUtils.getStringsFromValue("common/* libraries/*");
assertTrue(strings.size() == 2);
assertTrue(strings.get(0).equals("common/*"));
assertTrue(strings.get(1).equals("libraries/*"));
}
public void testFuzzyMatching() throws Exception {
assertTrue(StringUtils.fuzzyMatch("12345", "12345"));
assertTrue(StringUtils.fuzzyMatch("AbCdEf", "abcdef"));
assertTrue(StringUtils.fuzzyMatch("AbCdEf", "abc*"));
assertTrue(StringUtils.fuzzyMatch("AbCdEf", "*def"));
assertTrue(StringUtils.fuzzyMatch("AbCdEfHIJ", "abc*hij"));
assertFalse(StringUtils.fuzzyMatch("123", "12345"));
assertFalse(StringUtils.fuzzyMatch("AbCdEfHIJ", "abc*hhh"));
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet