www.github.com/gitblit/gitblit.git
Advanced tools
| # | ||
| # 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" /> <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" /> <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 |
+80
-7
@@ -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 @@ # |
@@ -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}!", |
+479
-17
@@ -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" /> <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" /> <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" /> <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" /> <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" /> <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" /> <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" /> <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" /> <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" /> <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" /> <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" /> <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" /> <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" /> <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 @@ } |
+33
-3
@@ -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