www.github.com/gitblit/gitblit.git
Advanced tools
| # Security Policy | ||
| ## Reporting a Vulnerability | ||
| The Gitblit team takes security bugs seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. | ||
| To report a security vulnerability, you can use the Github mechanism to [privately report a vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability). On Gitblit's repository page, choose the `Security` tab (under the repository name). Click the `Report a vulnerability` button on the right. | ||
| Alternatively, you can also report any security issue via e-mail. Send an email to the following email address and include the word "SECURITY" in the subject line. | ||
| ``` | ||
| gitblitorg@gmail.com | ||
| ``` |
| # Nightly build of a snapshot version | ||
| # and a docker image which is pushed | ||
| # to a docker registry | ||
| name: Nightly image build and push | ||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| forced: | ||
| description: 'Force run, independent of new commits' | ||
| required: false | ||
| default: 'false' | ||
| schedule: | ||
| - cron: '33 1 * * *' | ||
| jobs: | ||
| # Check if new commits were added since the last time this workflow ran. | ||
| # The Github cache is used for this, using the SHA as the key. | ||
| check_commits: | ||
| name: Check for new commits | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| build: ${{ steps.cache-sha.outputs.cache-hit == false }} | ||
| steps: | ||
| - name: Cache marker for latest commit | ||
| uses: actions/cache@v4 | ||
| id: cache-sha | ||
| with: | ||
| key: sha-${{ github.sha }} | ||
| path: timestamp.txt | ||
| - name: Register latest commit | ||
| if: ${{ steps.cache-sha.outputs.cache-hit == false }} | ||
| run: | | ||
| echo "Current commit $GITHUB_SHA has no cache hit." | ||
| date > timestamp.txt | ||
| echo "Build job should be triggered now" | ||
| cat timestamp.txt | ||
| - name: Stop on no new commit | ||
| if: ${{ steps.cache-sha.outputs.cache-hit }} | ||
| run: | | ||
| echo "Current commit $GITHUB_SHA was already seen." | ||
| echo "Build job should be skipped." | ||
| [ -f timestamp.txt ] && cat timestamp.txt | ||
| # Build Gitblit GO so that it can be packed into a docker image. | ||
| # The built tarball is saved as an artifact, it can be downloaded | ||
| # by interested parties. | ||
| # We could even do better and check if paths of source files changed, | ||
| # but that is not that easy, so we build on any commit. | ||
| build: | ||
| name: build GO | ||
| runs-on: ubuntu-latest | ||
| needs: check_commits | ||
| if: ${{ needs.check_commits.outputs.build == 'true' || github.event.inputs.forced == 'true' }} | ||
| steps: | ||
| - name: Checkout Gitblit | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| submodules: true | ||
| - name: Setup Java 8 | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| java-version: 8 | ||
| distribution: 'temurin' | ||
| - name: Report Java version | ||
| run: | | ||
| java -version | ||
| javac -version | ||
| - name: Build GO with Ant | ||
| run: ant buildGO | ||
| - name: Save built Gitblit package | ||
| if: ${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' }} | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: gitblit-nightly | ||
| path: build/target/gitblit-*-SNAPSHOT.tar.gz | ||
| # This is a gating job, which checks if the secrets necessary for pushing an image | ||
| # to the docker hub are present in the repository. This way this workflow can be | ||
| # present in repos which cannot upload to the docker hub. | ||
| secret-gate: | ||
| name: Gate job checking for docker hub secret | ||
| runs-on: ubuntu-latest | ||
| needs: build | ||
| outputs: | ||
| build_docker: ${{steps.check-dh-login.outputs.secrets_present}} | ||
| steps: | ||
| - name: Check if we have the necessary data for docker | ||
| id: check-dh-login | ||
| run: | | ||
| if [[ -n "${{secrets.DOCKERHUB_GB_TOKEN}}" && -n "${{secrets.DOCKERHUB_GB_USER}}" ]] ; then | ||
| echo "secrets_present=true" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "No Docker Hub login data found. Skipping Docker." | ||
| fi | ||
| # Only if the gating job signals success will this job run and build and push the docker image | ||
| # built for the current snapshot version of Gitblit. | ||
| docker: | ||
| name: Build and push nightly docker image | ||
| runs-on: ubuntu-latest | ||
| if: needs.secret-gate.outputs.build_docker == 'true' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop') | ||
| needs: secret-gate | ||
| env: | ||
| GH_ORG: gitblit-org | ||
| GITBLIT_VERSION: SNAPSHOT | ||
| steps: | ||
| - name: Checkout gitblit-docker | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| repository: ${{ env.GH_ORG }}/gitblit-docker | ||
| ref: master | ||
| fetch-depth: 2 | ||
| - name: Download Gitblit nightly build | ||
| uses: actions/download-artifact@v4 | ||
| id: get-gb | ||
| with: | ||
| name: gitblit-nightly | ||
| - name: Extract snapshot version | ||
| id: gb-version | ||
| run: | | ||
| for file in $(ls -1 ${{steps.get-gb.outputs.download-path}}) ; do | ||
| if [[ "$file" = gitblit-*.gz ]] ; then gbver=$file ; fi | ||
| done | ||
| gbver=${gbver%.tar.gz} | ||
| gbver=${gbver##*gitblit-} | ||
| echo "Version detected: $gbver" | ||
| echo "GITBLIT_VERSION=$gbver" >> "${GITHUB_ENV}" | ||
| echo "gb-version=$gbver" >> $GITHUB_OUTPUT | ||
| - name: Generate Dockerfile for snapshot image | ||
| run: | | ||
| generate/generate_dockerfile.sh -v ${{ steps.gb-version.outputs.gb-version }} > generate/Dockerfile | ||
| echo "BUILD_DATE=$(date +%Y-%m-%dT%H:%M:%S)" >> "${GITHUB_ENV}" | ||
| - name: Login to Docker Hub | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| username: ${{ secrets.DOCKERHUB_GB_USER }} | ||
| password: ${{ secrets.DOCKERHUB_GB_TOKEN }} | ||
| - name: Build snapshot docker image | ||
| uses: docker/build-push-action@v6 | ||
| with: | ||
| file: generate/Dockerfile | ||
| context: . | ||
| load: true | ||
| tags: gitblit/gitblit:nightly | ||
| labels: | | ||
| org.label-schema.vcs-ref=${{github.sha}} | ||
| org.label-schema.build-date=${{env.BUILD_DATE}} | ||
| org.opencontainers.image.revision=${{ env.GITBLIT_GIT_SHA }} | ||
| org.opencontainers.image.created=${{ env.BUILD_DATE }} | ||
| - name: Install Goss for testing the docker image | ||
| uses: e1himself/goss-installation-action@v1.2.1 | ||
| with: | ||
| version: 'v0.4.9' | ||
| - name: Test docker container - normal mode | ||
| env: | ||
| GOSS_WAIT_OPTS: "-r 15s -s 5s > /dev/null" | ||
| run: | | ||
| dgoss run -e GITBLIT_GOSS_TEST=true -p 8080:8080 -p 8443:8443 gitblit/gitblit:nightly | ||
| - name: Test docker container - bind mount | ||
| env: | ||
| GOSS_WAIT_OPTS: "-r 15s -s 5s > /dev/null" | ||
| run: | | ||
| mkdir gitblit-data | ||
| mkdir gitblit-data/etc | ||
| echo "This should not be overwritten" > gitblit-data/etc/gitblit.properties | ||
| echo "include = gitblit-docker.properties" >> gitblit-data/etc/gitblit.properties | ||
| sed -e '/mode: / d' -e '/group: / d' goss.yaml > gitblit-data/goss.yaml | ||
| cp goss_wait.yaml gitblit-data/ | ||
| GOSS_FILES_PATH=gitblit-data dgoss run -e GITBLIT_GOSS_TEST=true -p 8080:8080 -p 8443:8443 -v "$PWD/gitblit-data":/var/opt/gitblit gitblit/gitblit:nightly | ||
| [ -d gitblit-data/srv/git ] || exit 1 | ||
| [ -f gitblit-data/etc/defaults.properties ] || exit 1 | ||
| grep --quiet "This should not be overwritten" gitblit-data/etc/gitblit.properties || exit 1 | ||
| sudo rm -rf gitblit-data | ||
| - name: Test docker container - tmpfs | ||
| env: | ||
| GOSS_WAIT_OPTS: "-r 15s -s 5s > /dev/null" | ||
| run: | | ||
| dgoss run -e GITBLIT_GOSS_TEST=true -p 8080:8080 -p 8443:8443 --tmpfs /var/opt/gitblit/temp gitblit/gitblit:nightly | ||
| # Delete the artifact unless this is the official Gitblit repo | ||
| - uses: geekyeggo/delete-artifact@v5 | ||
| if: ${{ github.repository != 'gitblit-org/gitblit' }} | ||
| with: | ||
| name: gitblit-nightly | ||
| failOnError: false | ||
| - name: Push docker image to registry | ||
| uses: docker/build-push-action@v6 | ||
| with: | ||
| file: generate/Dockerfile | ||
| context: . | ||
| push: true | ||
| tags: gitblit/gitblit:nightly | ||
| labels: | | ||
| org.label-schema.vcs-ref=${{github.sha}} | ||
| org.label-schema.build-date=${{env.BUILD_DATE}} |
| /* | ||
| * Copyright (c) 2013 by syntevo GmbH. All Rights Reserved. | ||
| * | ||
| * Redistribution and use in source and binary forms, with or without | ||
| * modification, are permitted provided that the following conditions are met: | ||
| * | ||
| * o Redistributions of source code must retain the above copyright notice, | ||
| * this list of conditions and the following disclaimer. | ||
| * | ||
| * o Redistributions in binary form must reproduce the above copyright notice, | ||
| * this list of conditions and the following disclaimer in the documentation | ||
| * and/or other materials provided with the distribution. | ||
| * | ||
| * o Neither the name of syntevo GmbH nor the names of | ||
| * its contributors may be used to endorse or promote products derived | ||
| * from this software without specific prior written permission. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | ||
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | ||
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||
| * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| */ | ||
| package com.syntevo.bugtraq; | ||
| import java.util.*; | ||
| import org.jetbrains.annotations.*; | ||
| public final class BugtraqConfigEntry { | ||
| // Fields ================================================================= | ||
| private final String url; | ||
| private final List<String> projects; | ||
| private final List<BugtraqEntry> entries; | ||
| // Setup ================================================================== | ||
| public BugtraqConfigEntry(@NotNull String url, @NotNull String logIdRegex, @Nullable String logLinkRegex, @Nullable String logFilterRegex, @Nullable String logLinkText, @Nullable List<String> projects) throws BugtraqException { | ||
| this.url = url; | ||
| this.projects = projects; | ||
| this.entries = new ArrayList<>(); | ||
| if (projects == null) { | ||
| entries.add(new BugtraqEntry(url, logIdRegex, logLinkRegex, logFilterRegex, logLinkText)); | ||
| } | ||
| else { | ||
| for (String project : projects) { | ||
| final String projectUrl = this.url.replace("%PROJECT%", project); | ||
| final String projectLogIdRegex = logIdRegex.replace("%PROJECT%", project); | ||
| final String projectLogLinkRegex = logLinkRegex != null ? logLinkRegex.replace("%PROJECT%", project) : null; | ||
| final String projectLogFilterRegex = logFilterRegex != null ? logFilterRegex.replace("%PROJECT%", project) : null; | ||
| final String projectLogLinkText = logLinkText != null ? logLinkText.replace("%PROJECT%", project) : null; | ||
| entries.add(new BugtraqEntry(projectUrl, projectLogIdRegex, projectLogLinkRegex, projectLogFilterRegex, projectLogLinkText)); | ||
| } | ||
| } | ||
| } | ||
| // Accessing ============================================================== | ||
| @NotNull | ||
| public String getUrl() { | ||
| return url; | ||
| } | ||
| @Nullable | ||
| public List<String> getProjects() { | ||
| return projects != null ? Collections.unmodifiableList(projects) : null; | ||
| } | ||
| public List<BugtraqEntry> getEntries() { | ||
| return entries; | ||
| } | ||
| } |
| package com.gitblit.instance; | ||
| import com.gitblit.IStoredSettings; | ||
| import com.gitblit.manager.IRuntimeManager; | ||
| import com.gitblit.utils.JsonUtils; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import java.io.IOException; | ||
| import java.util.concurrent.Executors; | ||
| import java.util.concurrent.ScheduledExecutorService; | ||
| import java.util.concurrent.TimeUnit; | ||
| import static com.gitblit.utils.JsonUtils.sendJsonString; | ||
| public class GitblitInstance | ||
| { | ||
| private final static String STATS_URL = "https://instats.gitblit.dev/hiitsme/"; | ||
| private final static Logger LOG = LoggerFactory.getLogger(GitblitInstance.class); | ||
| private IRuntimeManager runtimeManager; | ||
| private String instanceId; | ||
| private GitblitInstanceReport report; | ||
| private ScheduledExecutorService executor; | ||
| /** | ||
| * Initialize the Gitblit instance reporting system. | ||
| * | ||
| * This will gather the static and dynamic statistics about the running | ||
| * instance, so that they can be reported. | ||
| * | ||
| * @param runtimeManager | ||
| * The runtime manager is used to determine the type of instance | ||
| * as well as for some other settings and data. | ||
| */ | ||
| public void init(IRuntimeManager runtimeManager) { | ||
| this.runtimeManager = runtimeManager; | ||
| // Initialize ID | ||
| GitblitInstanceId instanceId = new GitblitInstanceId(runtimeManager.getBaseFolder()); | ||
| this.instanceId = instanceId.getId().toString(); | ||
| LOG.info(this.instanceId); | ||
| GitblitInstanceStat instanceStat; | ||
| if (runtimeManager.getSettings().hasSettings("container.dockerfileVersion")) { | ||
| instanceStat = new GitblitInstanceStat(GitblitInstanceStat.GitblitInstanceType.DOCKER); | ||
| } | ||
| else if (runtimeManager.getStatus().isGO){ | ||
| instanceStat = new GitblitInstanceStat(GitblitInstanceStat.GitblitInstanceType.GO); | ||
| } | ||
| else { | ||
| instanceStat = new GitblitInstanceStat(GitblitInstanceStat.GitblitInstanceType.WAR); | ||
| } | ||
| instanceStat.init(runtimeManager.getStatus()); | ||
| this.report = new GitblitInstanceReport(this.instanceId, instanceStat); | ||
| } | ||
| public void start() | ||
| { | ||
| if (shouldRunReports()) { | ||
| startReports(); | ||
| } | ||
| } | ||
| public void stop() | ||
| { | ||
| if (this.executor != null && !this.executor.isShutdown() && !this.executor.isTerminated()) { | ||
| this.executor.shutdownNow(); | ||
| System.out.println("Gitblit instance reporting task stopped."); | ||
| } | ||
| } | ||
| /** | ||
| * Determine if the reporting task should run. | ||
| * | ||
| * We do not want to report anything, i.e. the reporting task to run, | ||
| * if we are running unit tests or integration tests. | ||
| * Instance reports should only be sent for production instances or released versions. | ||
| * Therefore we also check if the Gitblit version is a SNAPSHOT version, | ||
| * or if the docker image is not a release version, when running from a docker image. | ||
| * A docker image running under GOSS should also not report anything. | ||
| */ | ||
| boolean shouldRunReports() | ||
| { | ||
| // We can only run reports when we have been initialized | ||
| if (this.report == null || this.runtimeManager == null) { | ||
| return false; | ||
| } | ||
| // Check if we are running in a test environment | ||
| IStoredSettings settings = this.runtimeManager.getSettings(); | ||
| if (! settings.getString("gitblit.testReportingUrl", "").isEmpty()) { | ||
| // Force reporting to run overriding any test settings | ||
| LOG.debug("Enabled reporting to test server URL: {}", settings.getString("gitblit.testReportingUrl", "")); | ||
| return true; | ||
| } | ||
| if (settings.getBoolean("gitblit.testRun", false)) { | ||
| return false; | ||
| } | ||
| // Check if we are running a SNAPSHOT version | ||
| if (this.runtimeManager.getStatus().version.endsWith("SNAPSHOT")) { | ||
| return false; | ||
| } | ||
| if (this.report.instanceStat.instanceType == GitblitInstanceStat.GitblitInstanceType.DOCKER) { | ||
| // Check if we are running a docker image that is not a release version | ||
| if (! settings.getString("container.imageType", "").equals("release")) { | ||
| return false; | ||
| } | ||
| // Check if we are running a docker image under GOSS | ||
| if (System.getenv("GITBLIT_GOSS_TEST") != null) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
| /** | ||
| * Start the reporting task. | ||
| * | ||
| * This will start a thread that runs once a day and sends the instance | ||
| * report to the popularity report server. | ||
| */ | ||
| private void startReports() | ||
| { | ||
| this.executor = Executors.newSingleThreadScheduledExecutor(); | ||
| String statsUrl = STATS_URL; | ||
| int delay = 24; | ||
| int period = 24 * 60; // 24 hours in minutes | ||
| TimeUnit unit = TimeUnit.MINUTES; | ||
| long retryInterval = 60 * 60 * 1000; // 1 hour in milliseconds | ||
| final long retryTimeout = 20 * 60 * 60 * 1000; // 20 hours in milliseconds | ||
| // If we are running in a test environment, we will send the reports more frequently | ||
| String testUrl = this.runtimeManager.getSettings().getString("gitblit.testReportingUrl", ""); | ||
| if (! testUrl.isEmpty()) { | ||
| statsUrl = testUrl; | ||
| delay = 10; | ||
| period = 24; | ||
| unit = TimeUnit.SECONDS; | ||
| retryInterval = 10 * 1000; // 10 seconds in milliseconds | ||
| } | ||
| final String baseUrl = statsUrl; | ||
| final long retryIntervalFinal = retryInterval; | ||
| this.executor.scheduleAtFixedRate(new Runnable() | ||
| { | ||
| @Override | ||
| public void run() | ||
| { | ||
| sendMyStats(baseUrl + instanceId, retryIntervalFinal, retryTimeout); | ||
| } | ||
| }, delay, period, unit); | ||
| } | ||
| /** | ||
| * Send the instance report to the popularity report server. | ||
| * | ||
| * This will send a JSON object to the server with the instance report. | ||
| * | ||
| * @param reportUrl | ||
| * The URL to send the report to. | ||
| * @param retryInterval | ||
| * The interval in milliseconds to wait before retrying to send the report if it failed. | ||
| * @param retryTimeout | ||
| * The timeout in milliseconds to give up sending the report if it fails repeatedly. | ||
| */ | ||
| private void sendMyStats(String reportUrl, long retryInterval, long retryTimeout) | ||
| { | ||
| // Create a HTTP POST request payload | ||
| String report = JsonUtils.toJsonString(this.report.fromNow()); | ||
| int status = 0; | ||
| long timeToGiveup = System.currentTimeMillis() + retryTimeout; | ||
| while (status != 200 && System.currentTimeMillis() < timeToGiveup) { | ||
| try { | ||
| status = sendJsonString(reportUrl, report, "gitblitta", "countmein".toCharArray()); | ||
| if (status != 200) { | ||
| LOG.debug("Error sending stats to " + reportUrl + ": " + status); | ||
| } | ||
| } | ||
| catch (IOException e) { | ||
| LOG.debug("Exception sending stats to " + reportUrl + ": " + e.getMessage()); | ||
| } | ||
| if (status != 200) { | ||
| try { | ||
| Thread.sleep(retryInterval); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| return; // exit if interrupted | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| package com.gitblit.instance; | ||
| import com.gitblit.utils.FileUtils; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import java.io.File; | ||
| import java.io.IOException; | ||
| import java.net.InetAddress; | ||
| import java.net.NetworkInterface; | ||
| import java.net.Socket; | ||
| import java.util.ArrayList; | ||
| import java.util.Enumeration; | ||
| import java.util.List; | ||
| import java.util.UUID; | ||
| /** | ||
| * The instance id is a unique identifier for an installed Gitblit instance. | ||
| * | ||
| * This is used to track the number of Gitblit instances in the field. | ||
| * Its purpose is to gauge the popularity of Gitblit and to help | ||
| * prioritize feature requests. | ||
| * | ||
| * The instance id should be unique between different instances, even | ||
| * on the same machine. But it should stay the same between restarts of | ||
| * the same instance. It should also stay the same between upgrades of | ||
| * the same instance. Therefore, it must be stored in a file that is | ||
| * not overwritten during upgrades, once it has been created. | ||
| */ | ||
| public class GitblitInstanceId | ||
| { | ||
| static final String STORAGE_FILE = "gbins"; | ||
| private final Logger log = LoggerFactory.getLogger(getClass()); | ||
| private final File idFileBase; | ||
| private UUID id; | ||
| /** | ||
| * Constructor. | ||
| */ | ||
| public GitblitInstanceId() | ||
| { | ||
| this.idFileBase = null; | ||
| } | ||
| /** | ||
| * Constructor. | ||
| */ | ||
| public GitblitInstanceId(File idFileBase) | ||
| { | ||
| this.idFileBase = idFileBase; | ||
| } | ||
| /** | ||
| * Get the instance id. | ||
| * | ||
| * @return the instance id. | ||
| */ | ||
| public UUID getId() { | ||
| if (this.id == null) { | ||
| load(); | ||
| } | ||
| return this.id; | ||
| } | ||
| /** | ||
| * Load the instance id from the file. | ||
| */ | ||
| private void load() | ||
| { | ||
| if (this.idFileBase == null) { | ||
| // Not working with stored id. | ||
| log.debug("No id file base directory specified. Generated id is not persisted."); | ||
| generate(); | ||
| return; | ||
| } | ||
| File idFile = new File(this.idFileBase, STORAGE_FILE); | ||
| if (idFile.exists()) { | ||
| // Read the file | ||
| String uuidString = readFromFile(idFile); | ||
| // Parse the UUID | ||
| try { | ||
| this.id = UUID.fromString(uuidString); | ||
| return; | ||
| } | ||
| catch (IllegalArgumentException e) { | ||
| log.debug("Unable to parse instance id. Will generate a new one: {}", e.getMessage(), e); | ||
| } | ||
| } | ||
| // Generate a new instance id and persist it to disk. | ||
| generate(); | ||
| storeToFile(idFile); | ||
| } | ||
| private String readFromFile(File idfile) | ||
| { | ||
| // log.debug("Loading instance id from file: {}", idfile.getAbsolutePath()); | ||
| String string = FileUtils.readContent(idfile, null).trim(); | ||
| String uuidString = string.replaceAll("\\s+",""); | ||
| return uuidString.trim(); | ||
| } | ||
| private void storeToFile(File idfile) | ||
| { | ||
| // Make sure that the directory exists | ||
| if (!idfile.getParentFile().exists()) { | ||
| if (!idfile.getParentFile().mkdirs()) { | ||
| log.debug("Unable to create directory for instance id file: {}", idfile.getParentFile().getAbsolutePath()); | ||
| return; | ||
| } | ||
| } | ||
| // Write the UUID to the file | ||
| String uuidString = this.id.toString(); | ||
| FileUtils.writeContent(idfile, uuidString); | ||
| } | ||
| /** | ||
| * Generate a new instance id and persist it to disk. | ||
| * | ||
| * UUID is variant, i.e. OSF DCE, version 8, a custom format. | ||
| * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | ||
| * date -rand-8rnd-8rnd- rand OUI | ||
| * | ||
| * The variant nibble has the variant (1) in the upper two bits as 0b10xx, | ||
| * and the lower two bits are used as a version, currently 0bxx00. | ||
| * Should the format of this UUID change, the version can be incremented | ||
| * to 0bxx01 or 0bxx10. Further increments would set the bits in the variant | ||
| * nibble to 0bxx11 and employ more bits from the next nibble for further | ||
| * differentiation. | ||
| */ | ||
| private void generate() | ||
| { | ||
| // Start with a random UUID | ||
| UUID id = UUID.randomUUID(); | ||
| long upper = id.getMostSignificantBits(); | ||
| long lower = id.getLeastSignificantBits(); | ||
| // Set the variant bits to 0b1000, variant 1, our version 0. | ||
| lower &= 0x0FFFFFFFFFFFFFFFL; // Clear the variant bits | ||
| lower |= 0x8000000000000000L; // Set the variant bits to 0b1000 | ||
| // Set the version bits to 0b1000, version 8. | ||
| upper &= 0xFFFFFFFFFFFF0FFFL; // Clear the version bits | ||
| upper |= 0x0000000000008000L; // Set the version bits to 0b1000 | ||
| // Set the first four bytes to represent the date. | ||
| long date = System.currentTimeMillis(); | ||
| date &= 0xFFFFFFFFFFFF0000L; // Clear the last two bytes, those are only a few minutes. | ||
| date <<= 2 * 8; // We do not need the upper two bytes, that is too far into the future. | ||
| upper &= 0x00000000FFFFFFFFL; // Clear the date bits. | ||
| upper |= date; // Set the date in the upper 32 bits. | ||
| // Set the OUI in the lower three bytes. | ||
| Long oui = getNodeOUI(); | ||
| if (oui != null) { | ||
| lower &= 0xFFFFFFFFFF000000L; // Clear the OUI bits. | ||
| lower |= (0x1000000L | oui); // Set the OUI in the lower three bytes. Mark as valid OUI in bit above them. | ||
| } | ||
| else { | ||
| // Mark this as an invalid OUI, i.e. random bits, by setting the bit above the OUI bits to zero. | ||
| lower &= 0xFFFFFFFFFEFFFFFFL; // Clear the valid OUI indicator bit. | ||
| } | ||
| this.id = new UUID(upper, lower); | ||
| } | ||
| /** | ||
| * Get the OUI of one NIC of this host. | ||
| * | ||
| * @return null if no OUI could be detected, otherwise the OUI in the lower three bytes of a Long. | ||
| */ | ||
| private Long getNodeOUI() | ||
| { | ||
| byte[] node = null; | ||
| String logPrefix = "Unable to detect host. Use random value."; | ||
| try { | ||
| InetAddress ipa = InetAddress.getLocalHost(); | ||
| NetworkInterface iface = NetworkInterface.getByInetAddress(ipa); | ||
| if (iface != null) { | ||
| node = iface.getHardwareAddress(); | ||
| logPrefix = "From getLocalHost:"; | ||
| } | ||
| if (node == null) { | ||
| List<byte[]> macs = new ArrayList<>(); | ||
| List<byte[]> offmacs = new ArrayList<>(); | ||
| Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); | ||
| while (interfaces.hasMoreElements()) { | ||
| iface = interfaces.nextElement(); | ||
| byte[] mac = iface.getHardwareAddress(); | ||
| if (mac != null) { | ||
| if (iface.isLoopback()) { | ||
| continue; | ||
| } | ||
| if (iface.isVirtual() || iface.isPointToPoint()) { | ||
| continue; | ||
| } | ||
| if (iface.isUp()) { | ||
| macs.add(mac); | ||
| } | ||
| else { | ||
| offmacs.add(mac); | ||
| } | ||
| } | ||
| } | ||
| if (macs.size() == 1) { | ||
| node = macs.get(0); | ||
| logPrefix = "From up iface:"; | ||
| } | ||
| else if (offmacs.size() == 1) { | ||
| node = offmacs.get(0); | ||
| logPrefix = "From down iface:"; | ||
| } | ||
| } | ||
| if (node == null) { | ||
| Socket socket = new Socket("www.gitblit.dev", 80); | ||
| ipa = socket.getLocalAddress(); | ||
| socket.close(); | ||
| iface = NetworkInterface.getByInetAddress(ipa); | ||
| if (iface != null) { | ||
| node = iface.getHardwareAddress(); | ||
| logPrefix = "From socket:"; | ||
| } | ||
| } | ||
| if (node == null) { | ||
| log.debug(logPrefix); | ||
| return null; | ||
| } | ||
| if (log.isDebugEnabled()) { | ||
| log.debug("{} {}", logPrefix, String.format("%02X:%02X:%02X", node[0], node[1], node[2])); | ||
| } | ||
| long l = (((long)node[0]) << 16) & 0xff0000; | ||
| l |= (((long)node[1]) << 8) & 0xff00; | ||
| l |= ((long)node[2]) & 0xff; | ||
| return l; | ||
| } | ||
| catch (IOException e) { | ||
| log.debug("Exception while getting OUI: {}", e.getMessage(), e); | ||
| return null; | ||
| } | ||
| } | ||
| } |
| package com.gitblit.instance; | ||
| import com.google.gson.annotations.SerializedName; | ||
| import java.text.SimpleDateFormat; | ||
| import java.util.TimeZone; | ||
| /** | ||
| * GitblitInstanceReport collects the static and dynamic statistics about a running | ||
| * Gitblit instance, pairs it with a report version and instance id. | ||
| * This can then be send to the popularity report server. | ||
| * | ||
| */ | ||
| class GitblitInstanceReport | ||
| { | ||
| private final int reportVersion = 1; | ||
| @SerializedName("instance") | ||
| private final String instanceId; | ||
| private final String startTs; | ||
| private String lpingTs; | ||
| final GitblitInstanceStat instanceStat; | ||
| GitblitInstanceReport(String instanceId, GitblitInstanceStat instanceStat) | ||
| { | ||
| this.instanceId = instanceId; | ||
| this.instanceStat = instanceStat; | ||
| // Convert the timestamp taken from instanceStat to a string in the format "yyyy-MM-dd'T'HHmmssZ" so | ||
| // it can be used better in a file name. It is replicated here so that it can be directly used by the receiver. | ||
| SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); | ||
| dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); | ||
| this.startTs = dateFormat.format(instanceStat.startTs); | ||
| } | ||
| GitblitInstanceReport fromNow() | ||
| { | ||
| SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); | ||
| dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); | ||
| this.lpingTs = dateFormat.format(System.currentTimeMillis()); | ||
| return this; | ||
| } | ||
| } |
| package com.gitblit.instance; | ||
| import com.gitblit.models.ServerStatus; | ||
| import java.util.Date; | ||
| /** | ||
| * GitblitInstanceStat collects the static information about a Gitblit instance, | ||
| * such as its version, type, operating system and other static data. | ||
| * | ||
| */ | ||
| class GitblitInstanceStat | ||
| { | ||
| enum GitblitInstanceType { | ||
| GO, | ||
| WAR, | ||
| EXPRESS, | ||
| DOCKER | ||
| } | ||
| final GitblitInstanceType instanceType; | ||
| String version; | ||
| Date startTs; | ||
| String os; | ||
| String osName; | ||
| String osVersion; | ||
| String osArch; | ||
| String javaVersion; | ||
| String javaVendor; | ||
| String javaRuntimeVersion; | ||
| String javaRuntimeName; | ||
| String javaVmVersion; | ||
| String javaVmName; | ||
| long maxMem; | ||
| GitblitInstanceStat() | ||
| { | ||
| this.instanceType = GitblitInstanceType.WAR; | ||
| initOS(); | ||
| initJava(); | ||
| } | ||
| GitblitInstanceStat(GitblitInstanceType instanceType) | ||
| { | ||
| this.instanceType = instanceType; | ||
| initOS(); | ||
| initJava(); | ||
| } | ||
| GitblitInstanceStat init(ServerStatus serverStatus) | ||
| { | ||
| this.version = serverStatus.version; | ||
| this.startTs = serverStatus.bootDate; | ||
| this.maxMem = serverStatus.heapMaximum; | ||
| return this; | ||
| } | ||
| void initOS() | ||
| { | ||
| String os = System.getProperty("os.name"); | ||
| if (os == null) { | ||
| this.os = "Unknown"; | ||
| } else { | ||
| String oslc = os.toLowerCase(); | ||
| if (oslc.contains("windows")) { | ||
| this.os = "Windows"; | ||
| } else if (oslc.contains("linux")) { | ||
| this.os = "Linux"; | ||
| } else if (oslc.contains("mac") || oslc.contains("darwin")) { | ||
| this.os = "macOS"; | ||
| } else if (oslc.contains("bsd")) { | ||
| this.os = "BSD"; | ||
| } else if (oslc.contains("solaris") || oslc.contains("sun") || | ||
| oslc.contains("aix") || oslc.contains("hpux") || oslc.contains("unix")) { | ||
| this.os = "Unix"; | ||
| } else { | ||
| this.os = os; | ||
| } | ||
| } | ||
| this.osName = System.getProperty("os.name"); | ||
| this.osVersion = System.getProperty("os.version"); | ||
| this.osArch = System.getProperty("os.arch"); | ||
| } | ||
| void initJava() | ||
| { | ||
| this.javaVersion = System.getProperty("java.version"); | ||
| this.javaVendor = System.getProperty("java.vendor"); | ||
| this.javaRuntimeVersion = System.getProperty("java.runtime.version", ""); | ||
| this.javaRuntimeName = System.getProperty("java.runtime.name", ""); | ||
| this.javaVmVersion = System.getProperty("java.vm.version", ""); | ||
| this.javaVmName = System.getProperty("java.vm.name", ""); | ||
| } | ||
| @Override | ||
| public String toString() | ||
| { | ||
| StringBuilder sb = new StringBuilder(); | ||
| sb.append("GitblitInstanceStat {") | ||
| .append("\n instanceType: ").append(instanceType) | ||
| .append(",\n version: ").append(version) | ||
| .append(",\n startTs: ").append(startTs) | ||
| .append(",\n os: ").append(os) | ||
| .append(",\n osName: ").append(osName) | ||
| .append(",\n osVersion: ").append(osVersion) | ||
| .append(",\n osArch: ").append(osArch) | ||
| .append(",\n javaVersion: ").append(javaVersion) | ||
| .append(",\n javaVendor: ").append(javaVendor) | ||
| .append(",\n javaRuntimeVersion: ").append(javaRuntimeVersion) | ||
| .append(",\n javaRuntimeName: ").append(javaRuntimeName) | ||
| .append(",\n javaVmVersion: ").append(javaVmVersion) | ||
| .append(",\n javaVmName: ").append(javaVmName) | ||
| .append(",\n maxMem: ").append(maxMem) | ||
| .append("\n}"); | ||
| 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="fr" | ||
| lang="fr"> | ||
| <body> | ||
| <wicket:extend> | ||
| <div class="container"> | ||
| <div class="markdown"> | ||
| <div class="row"> | ||
| <div class="span10 offset1"> | ||
| <h3><center>Dépôt vide</center></h3> | ||
| <div class="alert alert-info"> | ||
| <span wicket:id="repository" style="font-weight: bold;">[repository]</span> est un dépôt vide, Gitblit ne peut pas le visualiser. | ||
| <p></p> | ||
| S'il vous plaît, envoyer quelques commits dans <span wicket:id="pushurl"></span> | ||
| <hr/> | ||
| Après avoir envoyé (push) des commits, vous pouvez <b>rafraîchir</b> cette page pour visualiser votre dépôt. | ||
| </div> | ||
| <h3><center>Créer un nouveau référentiel en ligne de commande</center></h3> | ||
| <pre wicket:id="createSyntax"></pre> | ||
| <h3><center>Déposer un dépôt existant en ligne de commande</center></h3> | ||
| <pre wicket:id="existingSyntax"></pre> | ||
| <div class="span8 offset1"> | ||
| <h2><center>Apprendre Git</center></h2> | ||
| <p>Si vous n'êtes pas sûr de vous sur l'utilisation de ces commandes, envisagez de consulter le <a href="http://book.git-scm.com">Git Community Book (Livre de la communauté Git)</a> pour mieux comprendre comment utiliser Git.</p> | ||
| <h4>Clients Git "Open Source"</h4> | ||
| <table> | ||
| <tbody> | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>Version officielle de Git, en ligne de commande</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Module Git pour l'IDE Eclipse (basé sur JGit, comme Gitblit)</td></tr> | ||
| <tr><td><a href="http://gitextensions.github.io">Git Extensions</a></td><td>Client pour Git écrit en C#, intégré à l'Explorateur de fichier de Windows et à Visual Studio</td></tr> | ||
| <tr><td><a href="http://rowanj.github.io/gitx/">GitX-dev</a></td><td>Client Git pour Mac OS X</td></tr> | ||
| </tbody> | ||
| </table> | ||
| <h4>Clients Git propriétaire</h4> | ||
| <table> | ||
| <tbody> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>Un client Git et Mercurial écrit Java pour Windows, Mac, et Linux</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>Un client Git gratuit pour Windows et Mac</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Un client Git et Mercurial gratuit pour Windows et Mac</td></tr> | ||
| <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>Client Git pour Mac OS</td></tr> | ||
| </tbody> | ||
| </table> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </wicket:extend> | ||
| </body> | ||
| </html> |
| /*! | ||
| * clipboard.js v2.0.11 | ||
| * https://clipboardjs.com/ | ||
| * | ||
| * Licensed MIT © Zeno Rocha | ||
| */ | ||
| !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return b}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),r=n.n(e);function c(t){try{return document.execCommand(t)}catch(t){return}}var a=function(t){t=r()(t);return c("cut"),t};function o(t,e){var n,o,t=(n=t,o="rtl"===document.documentElement.getAttribute("dir"),(t=document.createElement("textarea")).style.fontSize="12pt",t.style.border="0",t.style.padding="0",t.style.margin="0",t.style.position="absolute",t.style[o?"right":"left"]="-9999px",o=window.pageYOffset||document.documentElement.scrollTop,t.style.top="".concat(o,"px"),t.setAttribute("readonly",""),t.value=n,t);return e.container.appendChild(t),e=r()(t),c("copy"),t.remove(),e}var f=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{container:document.body},n="";return"string"==typeof t?n=o(t,e):t instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(null==t?void 0:t.type)?n=o(t.value,e):(n=r()(t),c("copy")),n};function l(t){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var s=function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{},e=t.action,n=void 0===e?"copy":e,o=t.container,e=t.target,t=t.text;if("copy"!==n&&"cut"!==n)throw new Error('Invalid "action" value, use either "copy" or "cut"');if(void 0!==e){if(!e||"object"!==l(e)||1!==e.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===n&&e.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===n&&(e.hasAttribute("readonly")||e.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes')}return t?f(t,{container:o}):e?"cut"===n?a(e):f(e,{container:o}):void 0};function p(t){return(p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function d(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}function y(t,e){return(y=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function h(n){var o=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(t){return!1}}();return function(){var t,e=v(n);return t=o?(t=v(this).constructor,Reflect.construct(e,arguments,t)):e.apply(this,arguments),e=this,!(t=t)||"object"!==p(t)&&"function"!=typeof t?function(t){if(void 0!==t)return t;throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}(e):t}}function v(t){return(v=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function m(t,e){t="data-clipboard-".concat(t);if(e.hasAttribute(t))return e.getAttribute(t)}var b=function(){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&y(t,e)}(r,i());var t,e,n,o=h(r);function r(t,e){var n;return function(t){if(!(t instanceof r))throw new TypeError("Cannot call a class as a function")}(this),(n=o.call(this)).resolveOptions(e),n.listenClick(t),n}return t=r,n=[{key:"copy",value:function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{container:document.body};return f(t,e)}},{key:"cut",value:function(t){return a(t)}},{key:"isSupported",value:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:["copy","cut"],t="string"==typeof t?[t]:t,e=!!document.queryCommandSupported;return t.forEach(function(t){e=e&&!!document.queryCommandSupported(t)}),e}}],(e=[{key:"resolveOptions",value:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===p(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=u()(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget,n=this.action(e)||"copy",t=s({action:n,container:this.container,target:this.target(e),text:this.text(e)});this.emit(t?"success":"error",{action:n,text:t,trigger:e,clearSelection:function(){e&&e.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(t){return m("action",t)}},{key:"defaultTarget",value:function(t){t=m("target",t);if(t)return document.querySelector(t)}},{key:"defaultText",value:function(t){return m("text",t)}},{key:"destroy",value:function(){this.listener.destroy()}}])&&d(t.prototype,e),n&&d(t,n),r}()},828:function(t){var e;"undefined"==typeof Element||Element.prototype.matches||((e=Element.prototype).matches=e.matchesSelector||e.mozMatchesSelector||e.msMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector),t.exports=function(t,e){for(;t&&9!==t.nodeType;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}},438:function(t,e,n){var u=n(828);function i(t,e,n,o,r){var i=function(e,n,t,o){return function(t){t.delegateTarget=u(t.target,n),t.delegateTarget&&o.call(e,t)}}.apply(this,arguments);return t.addEventListener(n,i,r),{destroy:function(){t.removeEventListener(n,i,r)}}}t.exports=function(t,e,n,o,r){return"function"==typeof t.addEventListener?i.apply(null,arguments):"function"==typeof n?i.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return i(t,e,n,o,r)}))}},879:function(t,n){n.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},n.nodeList=function(t){var e=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===e||"[object HTMLCollection]"===e)&&"length"in t&&(0===t.length||n.node(t[0]))},n.string=function(t){return"string"==typeof t||t instanceof String},n.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},370:function(t,e,n){var f=n(879),l=n(438);t.exports=function(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!f.string(e))throw new TypeError("Second argument must be a String");if(!f.fn(n))throw new TypeError("Third argument must be a Function");if(f.node(t))return c=e,a=n,(u=t).addEventListener(c,a),{destroy:function(){u.removeEventListener(c,a)}};if(f.nodeList(t))return o=t,r=e,i=n,Array.prototype.forEach.call(o,function(t){t.addEventListener(r,i)}),{destroy:function(){Array.prototype.forEach.call(o,function(t){t.removeEventListener(r,i)})}};if(f.string(t))return t=t,e=e,n=n,l(document.body,t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList");var o,r,i,u,c,a}},817:function(t){t.exports=function(t){var e,n="SELECT"===t.nodeName?(t.focus(),t.value):"INPUT"===t.nodeName||"TEXTAREA"===t.nodeName?((e=t.hasAttribute("readonly"))||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),e||t.removeAttribute("readonly"),t.value):(t.hasAttribute("contenteditable")&&t.focus(),n=window.getSelection(),(e=document.createRange()).selectNodeContents(t),n.removeAllRanges(),n.addRange(e),n.toString());return n}},279:function(t){function e(){}e.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){var o=this;function r(){o.off(t,r),e.apply(n,arguments)}return r._=e,this.on(t,r,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;o<r;o++)n[o].fn.apply(n[o].ctx,e);return this},off:function(t,e){var n=this.e||(this.e={}),o=n[t],r=[];if(o&&e)for(var i=0,u=o.length;i<u;i++)o[i].fn!==e&&o[i].fn._!==e&&r.push(o[i]);return r.length?n[t]=r:delete n[t],this}},t.exports=e,t.exports.TinyEmitter=e}},r={},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,{a:e}),e},o.d=function(t,e){for(var n in e)o.o(e,n)&&!o.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o(686).default;function o(t){if(r[t])return r[t].exports;var e=r[t]={exports:{}};return n[t](e,e.exports,o),e.exports}var n,r}); |
| // Instantiate the clipboarding | ||
| var clipboard = new ClipboardJS('.ctcbtn'); | ||
| clipboard.on('success', function (e) { | ||
| showTooltip(e.trigger, "Copied!"); | ||
| }); | ||
| clipboard.on('error', function (e) { | ||
| showTooltip(e.trigger, fallbackMessage(e.action)); | ||
| }); | ||
| // Attach events to buttons to clear tooltip again | ||
| var btns = document.querySelectorAll('.ctcbtn'); | ||
| for (var i = 0; i < btns.length; i++) { | ||
| btns[i].addEventListener('mouseleave', clearTooltip); | ||
| btns[i].addEventListener('blur', clearTooltip); | ||
| } | ||
| function findTooltipped(elem) { | ||
| do { | ||
| if (elem.classList.contains('tooltipped')) return elem; | ||
| elem = elem.parentElement; | ||
| } while (elem != null); | ||
| return null; | ||
| } | ||
| // Show or hide tooltip by setting the tooltipped-active class | ||
| // on a parent that contains tooltipped. Since the copy button | ||
| // could be and image, or be hidden after clicking, the tooltipped | ||
| // element might be higher in the hierarchy. | ||
| var ttset; | ||
| function showTooltip(elem, msg) { | ||
| let ttelem = findTooltipped(elem); | ||
| if (ttelem != null) { | ||
| ttelem.classList.add('tooltipped-active'); | ||
| ttelem.setAttribute('data-tt-text', msg); | ||
| ttset=Date.now(); | ||
| } | ||
| else { | ||
| console.warn("Could not find any tooltipped element for clipboard button.", elem); | ||
| } | ||
| } | ||
| function clearTooltip(e) { | ||
| let ttelem = findTooltipped(e.currentTarget); | ||
| if (ttelem != null) { | ||
| let now = Date.now(); | ||
| if (now - ttset < 500) { | ||
| // Give the tooltip some time to display | ||
| setTimeout(function(){ttelem.classList.remove('tooltipped-active')}, 1000) | ||
| } | ||
| else { | ||
| ttelem.classList.remove('tooltipped-active'); | ||
| } | ||
| } | ||
| else { | ||
| console.warn("Could not find any tooltipped element for clipboard button.", e.currentTarget); | ||
| } | ||
| } | ||
| // If the API is not supported, at least fall back to a message saying | ||
| // that now that the text is selected, Ctrl-C can be used. | ||
| // This is still a problem in the repo URL dropdown. When it is hidden, Ctrl-C doesn't work. | ||
| function fallbackMessage(action) { | ||
| var actionMsg = ""; | ||
| if (/Mac/i.test(navigator.userAgent)) { | ||
| actionMsg = "Press ⌘-C to copy"; | ||
| } | ||
| else { | ||
| actionMsg = "Press Ctrl-C to copy"; | ||
| } | ||
| return actionMsg; | ||
| } |
| package com.gitblit.instance; | ||
| import org.junit.Rule; | ||
| import org.junit.Test; | ||
| import org.junit.rules.TemporaryFolder; | ||
| import java.io.File; | ||
| import java.nio.file.Files; | ||
| import java.util.UUID; | ||
| import static org.junit.Assert.assertEquals; | ||
| import static org.junit.Assert.assertNotNull; | ||
| import static org.junit.Assert.assertTrue; | ||
| import static org.junit.Assert.fail; | ||
| public class GitblitInstanceIdTest | ||
| { | ||
| @Rule | ||
| public TemporaryFolder baseFolder = new TemporaryFolder(); | ||
| /** | ||
| * Tests that the version nibble is set to 0x8. | ||
| */ | ||
| @Test | ||
| public void testUuidVersion() throws Exception | ||
| { | ||
| GitblitInstanceId id = new GitblitInstanceId(); | ||
| UUID uuid = id.getId(); | ||
| assertNotNull(uuid); | ||
| long upper = uuid.getMostSignificantBits(); | ||
| assertEquals(0x0000000000008000L, (upper & 0x000000000000F000L)); | ||
| } | ||
| /** | ||
| * Tests that the variant nibble is set to 0x8. | ||
| */ | ||
| @Test | ||
| public void testUuidVariant() throws Exception | ||
| { | ||
| GitblitInstanceId id = new GitblitInstanceId(); | ||
| UUID uuid = id.getId(); | ||
| assertNotNull(uuid); | ||
| long lower = uuid.getLeastSignificantBits(); | ||
| assertEquals(0x8000000000000000L, (lower & 0xF000000000000000L)); | ||
| } | ||
| /** | ||
| * Test that the first four bytes hold a timestamp in a newly generated id. | ||
| */ | ||
| @Test | ||
| public void testDatePart() throws Exception | ||
| { | ||
| GitblitInstanceId id = new GitblitInstanceId(); | ||
| UUID uuid = id.getId(); | ||
| assertNotNull(uuid); | ||
| long upper = uuid.getMostSignificantBits(); | ||
| long ts = System.currentTimeMillis(); | ||
| assertEquals("Date part of UUID does not equal current date/time.", ts >> 2*8, upper >> 4*8); | ||
| } | ||
| /** | ||
| * Test that a new id is generated and stored in a file, when none existed. | ||
| */ | ||
| @Test | ||
| public void testStoreId() throws Exception | ||
| { | ||
| GitblitInstanceId id = new GitblitInstanceId(baseFolder.getRoot()); | ||
| UUID uuid = id.getId(); | ||
| assertNotNull(uuid); | ||
| long lower = uuid.getLeastSignificantBits(); | ||
| assertEquals(0x8000000000000000L, (lower & 0xF000000000000000L)); | ||
| long upper = uuid.getMostSignificantBits(); | ||
| assertEquals(0x0000000000008000L, (upper & 0x000000000000F000L)); | ||
| File idFile = new File(baseFolder.getRoot(), GitblitInstanceId.STORAGE_FILE); | ||
| assertTrue("Id file was not created", idFile.exists()); | ||
| String string = Files.readAllLines(idFile.toPath()).get(0); | ||
| try { | ||
| UUID uuidFromFile = UUID.fromString(string); | ||
| assertEquals("Returned id and id read from file are not equal.", uuid, uuidFromFile); | ||
| } catch (IllegalArgumentException e) { | ||
| fail("UUID read from file is not valid: " + string); | ||
| } | ||
| } | ||
| /** | ||
| * Test that a new id is generated and stored in a file, when none existed. | ||
| */ | ||
| @Test | ||
| public void testStoreIdNonexistingFolder() throws Exception | ||
| { | ||
| File baseSubFolder = new File(baseFolder.getRoot(), "nonexisting"); | ||
| GitblitInstanceId id = new GitblitInstanceId(baseSubFolder); | ||
| UUID uuid = id.getId(); | ||
| assertNotNull(uuid); | ||
| long lower = uuid.getLeastSignificantBits(); | ||
| assertEquals(0x8000000000000000L, (lower & 0xF000000000000000L)); | ||
| long upper = uuid.getMostSignificantBits(); | ||
| assertEquals(0x0000000000008000L, (upper & 0x000000000000F000L)); | ||
| File idFile = new File(baseSubFolder, GitblitInstanceId.STORAGE_FILE); | ||
| assertTrue("Id file was not created", idFile.exists()); | ||
| String string = Files.readAllLines(idFile.toPath()).get(0); | ||
| try { | ||
| UUID uuidFromFile = UUID.fromString(string); | ||
| assertEquals("Returned id and id read from file are not equal.", uuid, uuidFromFile); | ||
| } catch (IllegalArgumentException e) { | ||
| fail("UUID read from file is not valid: " + string); | ||
| } | ||
| } | ||
| /** | ||
| * Test that an existing id is read from an existing id file. | ||
| */ | ||
| @Test | ||
| public void testReadId() throws Exception | ||
| { | ||
| File idFile = new File(baseFolder.getRoot(), GitblitInstanceId.STORAGE_FILE); | ||
| String uuidString = "0196e409-c664-82f3-88f1-e963d16c7b8a"; | ||
| Files.write(idFile.toPath(), uuidString.getBytes()); | ||
| GitblitInstanceId id = new GitblitInstanceId(baseFolder.getRoot()); | ||
| UUID uuid = id.getId(); | ||
| assertNotNull(uuid); | ||
| UUID refUuid = UUID.fromString(uuidString); | ||
| assertEquals("Returned id is not equal to reference id", refUuid, uuid); | ||
| } | ||
| /** | ||
| * Test reading id from a file with whitespace | ||
| */ | ||
| @Test | ||
| public void testReadIdWhitespace() throws Exception | ||
| { | ||
| File idFile = new File(baseFolder.getRoot(), GitblitInstanceId.STORAGE_FILE); | ||
| String uuidString = "0196e409-c664-82f3-88f1-e963d16c7b8a"; | ||
| String fileString = "\n " + uuidString + " \n \n"; | ||
| Files.write(idFile.toPath(), fileString.getBytes()); | ||
| GitblitInstanceId id = new GitblitInstanceId(baseFolder.getRoot()); | ||
| UUID uuid = id.getId(); | ||
| assertNotNull(uuid); | ||
| UUID refUuid = UUID.fromString(uuidString); | ||
| assertEquals("Returned id is not equal to reference id", refUuid, uuid); | ||
| } | ||
| } |
| package com.gitblit.instance; | ||
| import com.gitblit.Constants; | ||
| import com.gitblit.models.ServerStatus; | ||
| import org.junit.Before; | ||
| import org.junit.Test; | ||
| import java.util.Date; | ||
| import static org.junit.Assert.assertEquals; | ||
| import static org.junit.Assert.assertFalse; | ||
| import static org.junit.Assert.assertNotNull; | ||
| import static org.junit.Assert.assertTrue; | ||
| public class GitblitInstanceStatTest | ||
| { | ||
| protected GitblitInstanceStat instanceStat; | ||
| protected ServerStatus serverStatus; | ||
| @Before | ||
| public void setUp() throws Exception | ||
| { | ||
| instanceStat = new GitblitInstanceStat(); | ||
| serverStatus = new ServerStatus(); | ||
| instanceStat.init(serverStatus); | ||
| } | ||
| @Test | ||
| public void testGetVersion() | ||
| { | ||
| String version = instanceStat.version; | ||
| assertNotNull(version); | ||
| assertFalse(version.isEmpty()); | ||
| assertEquals(Constants.getVersion(), version); | ||
| } | ||
| @Test | ||
| public void testGetStartTs() | ||
| { | ||
| Date date = instanceStat.startTs; | ||
| assertNotNull(date); | ||
| assertEquals(serverStatus.bootDate, date); | ||
| } | ||
| @Test | ||
| public void testGetType() | ||
| { | ||
| String type = instanceStat.instanceType.name(); | ||
| assertNotNull(type); | ||
| assertFalse(type.isEmpty()); | ||
| assertEquals("WAR", type); | ||
| } | ||
| @Test | ||
| public void testGetOS() | ||
| { | ||
| String os = instanceStat.os; | ||
| String oslc = System.getProperty("os.name").toLowerCase(); | ||
| if (oslc.contains("windows")) { | ||
| assertEquals("Windows", os); | ||
| } | ||
| else if (oslc.contains("linux")) { | ||
| assertEquals("Linux", os); | ||
| } | ||
| else if (oslc.contains("mac")) { | ||
| assertEquals("macOS", os); | ||
| } | ||
| } | ||
| @Test | ||
| public void testGetOSName() | ||
| { | ||
| String name = instanceStat.osName; | ||
| assertNotNull(name); | ||
| assertFalse(name.isEmpty()); | ||
| assertEquals(System.getProperty("os.name"), name); | ||
| } | ||
| @Test | ||
| public void testGetOSVersion() | ||
| { | ||
| String version = instanceStat.osVersion; | ||
| assertNotNull(version); | ||
| assertFalse(version.isEmpty()); | ||
| assertEquals(System.getProperty("os.version"), version); | ||
| } | ||
| @Test | ||
| public void testGetOSArch() | ||
| { | ||
| String arch = instanceStat.osArch; | ||
| assertNotNull(arch); | ||
| assertFalse(arch.isEmpty()); | ||
| assertEquals(System.getProperty("os.arch"), arch); | ||
| } | ||
| @Test | ||
| public void testGetJavaVersion() | ||
| { | ||
| String version = instanceStat.javaVersion; | ||
| assertNotNull(version); | ||
| assertFalse(version.isEmpty()); | ||
| assertEquals(System.getProperty("java.version"), version); | ||
| } | ||
| @Test | ||
| public void testGetJavaVendor() | ||
| { | ||
| String vendor = instanceStat.javaVendor; | ||
| assertNotNull(vendor); | ||
| assertFalse(vendor.isEmpty()); | ||
| assertEquals(System.getProperty("java.vendor"), vendor); | ||
| } | ||
| @Test | ||
| public void testGetJavaRuntimeVersion() | ||
| { | ||
| String rt = instanceStat.javaRuntimeVersion; | ||
| assertNotNull(rt); | ||
| assertFalse(rt.isEmpty()); | ||
| assertEquals(System.getProperty("java.runtime.version"), rt); | ||
| } | ||
| @Test | ||
| public void testGetJavaRuntimeName() | ||
| { | ||
| String rt = instanceStat.javaRuntimeName; | ||
| assertNotNull(rt); | ||
| assertFalse(rt.isEmpty()); | ||
| assertEquals(System.getProperty("java.runtime.name"), rt); | ||
| } | ||
| @Test | ||
| public void testGetJavaVmVersion() | ||
| { | ||
| String vm = instanceStat.javaVmVersion; | ||
| assertNotNull(vm); | ||
| assertFalse(vm.isEmpty()); | ||
| assertEquals(System.getProperty("java.vm.version"), vm); | ||
| } | ||
| @Test | ||
| public void testGetJavaVmName() | ||
| { | ||
| String vm = instanceStat.javaVmName; | ||
| assertNotNull(vm); | ||
| assertFalse(vm.isEmpty()); | ||
| assertEquals(System.getProperty("java.vm.name"), vm); | ||
| } | ||
| @Test | ||
| public void testGetMaxMem() | ||
| { | ||
| long maxMem = instanceStat.maxMem; | ||
| assertTrue(maxMem > 0); | ||
| assertEquals(Runtime.getRuntime().maxMemory(), maxMem); | ||
| } | ||
| @Test | ||
| public void testToString() | ||
| { | ||
| String str = instanceStat.toString(); | ||
| assertNotNull(str); | ||
| assertFalse(str.isEmpty()); | ||
| assertTrue(str.contains("GitblitInstanceStat")); | ||
| assertTrue(str.contains("version")); | ||
| assertTrue(str.contains("instanceType")); | ||
| assertTrue(str.contains("os")); | ||
| assertTrue(str.contains("osName")); | ||
| assertTrue(str.contains("osVersion")); | ||
| assertTrue(str.contains("osArch")); | ||
| assertTrue(str.contains("javaVersion")); | ||
| assertTrue(str.contains("javaVendor")); | ||
| assertTrue(str.contains("javaRuntimeVersion")); | ||
| assertTrue(str.contains("javaRuntimeName")); | ||
| assertTrue(str.contains("javaVmVersion")); | ||
| assertTrue(str.contains("javaVmName")); | ||
| } | ||
| } |
| package com.gitblit.instance; | ||
| import com.gitblit.manager.IRuntimeManager; | ||
| import com.gitblit.models.ServerStatus; | ||
| import com.gitblit.tests.mock.MockRuntimeManager; | ||
| import org.junit.Test; | ||
| import static org.junit.Assert.assertFalse; | ||
| import static org.junit.Assert.assertTrue; | ||
| public class GitblitInstanceTest | ||
| { | ||
| @Test | ||
| public void testShouldNotReportUnintialized() | ||
| { | ||
| GitblitInstance instance = new GitblitInstance(); | ||
| assertFalse(instance.shouldRunReports()); | ||
| } | ||
| @Test | ||
| public void testShouldNotReportInTests() | ||
| { | ||
| GitblitInstance instance = new GitblitInstance(); | ||
| instance.init(new MockRuntimeManager()); | ||
| assertFalse(instance.shouldRunReports()); | ||
| } | ||
| @Test | ||
| public void testShouldNotReportInSnapshotVersion() | ||
| { | ||
| GitblitInstance instance = new GitblitInstance(); | ||
| IRuntimeManager runtimeManager = new MockRuntimeManager(); | ||
| runtimeManager.getSettings().overrideSetting("gitblit.testRun", "false"); | ||
| instance.init(runtimeManager); | ||
| assertFalse(instance.shouldRunReports()); | ||
| } | ||
| @Test | ||
| public void testShouldReportIfForced() | ||
| { | ||
| GitblitInstance instance = new GitblitInstance(); | ||
| IRuntimeManager runtimeManager = new MockRuntimeManager(); | ||
| runtimeManager.getSettings().overrideSetting("gitblit.testRunReporting", "true"); | ||
| instance.init(runtimeManager); | ||
| assertTrue(instance.shouldRunReports()); | ||
| } | ||
| @Test | ||
| public void testShouldReportInReleaseVersion() | ||
| { | ||
| ServerStatus serverStatus = new ServerStatus("1.10.123"); | ||
| MockRuntimeManager runtimeManager = new MockRuntimeManager(); | ||
| runtimeManager.setStatus(serverStatus); | ||
| runtimeManager.getSettings().overrideSetting("gitblit.testRun", "false"); | ||
| GitblitInstance instance = new GitblitInstance(); | ||
| instance.init(runtimeManager); | ||
| assertTrue(instance.shouldRunReports()); | ||
| } | ||
| } |
| package com.gitblit.transport.ssh; | ||
| import org.junit.Rule; | ||
| import org.junit.Test; | ||
| import org.junit.rules.TemporaryFolder; | ||
| import java.io.File; | ||
| import java.io.IOException; | ||
| import java.security.KeyPair; | ||
| import java.util.Iterator; | ||
| import static org.junit.Assert.*; | ||
| public class FileKeyPairProviderTest | ||
| { | ||
| @Rule | ||
| public TemporaryFolder testFolder = new TemporaryFolder(); | ||
| private void generateKeyPair(File file, String algorithm, int keySize) { | ||
| if (file.exists()) { | ||
| file.delete(); | ||
| } | ||
| SshDaemon.generateKeyPair(file, algorithm, keySize); | ||
| } | ||
| @Test | ||
| public void loadKeysEddsa() throws IOException | ||
| { | ||
| File file = testFolder.newFile("eddsa.pem"); | ||
| generateKeyPair(file, "EdDSA", 0); | ||
| FileKeyPairProvider hostKeyPairProvider = new FileKeyPairProvider(); | ||
| hostKeyPairProvider.setFiles(new String [] { file.getPath() }); | ||
| Iterable<KeyPair> keyPairs = hostKeyPairProvider.loadKeys(); | ||
| Iterator<KeyPair> iterator = keyPairs.iterator(); | ||
| assertTrue(iterator.hasNext()); | ||
| KeyPair keyPair = iterator.next(); | ||
| assertNotNull(keyPair); | ||
| assertEquals("Unexpected key pair type", "EdDSA", keyPair.getPrivate().getAlgorithm()); | ||
| } | ||
| @Test | ||
| public void loadKeysEd25519() throws IOException | ||
| { | ||
| File file = testFolder.newFile("ed25519.pem"); | ||
| generateKeyPair(file, "ED25519", 0); | ||
| FileKeyPairProvider hostKeyPairProvider = new FileKeyPairProvider(); | ||
| hostKeyPairProvider.setFiles(new String [] { file.getPath() }); | ||
| Iterable<KeyPair> keyPairs = hostKeyPairProvider.loadKeys(); | ||
| Iterator<KeyPair> iterator = keyPairs.iterator(); | ||
| assertTrue(iterator.hasNext()); | ||
| KeyPair keyPair = iterator.next(); | ||
| assertNotNull(keyPair); | ||
| assertEquals("Unexpected key pair type", "Ed25519", keyPair.getPrivate().getAlgorithm()); | ||
| } | ||
| @Test | ||
| public void loadKeysECDSA() throws IOException | ||
| { | ||
| File file = testFolder.newFile("ecdsa.pem"); | ||
| generateKeyPair(file, "ECDSA", 0); | ||
| FileKeyPairProvider hostKeyPairProvider = new FileKeyPairProvider(); | ||
| hostKeyPairProvider.setFiles(new String [] { file.getPath() }); | ||
| Iterable<KeyPair> keyPairs = hostKeyPairProvider.loadKeys(); | ||
| Iterator<KeyPair> iterator = keyPairs.iterator(); | ||
| assertTrue(iterator.hasNext()); | ||
| KeyPair keyPair = iterator.next(); | ||
| assertNotNull(keyPair); | ||
| assertEquals("Unexpected key pair type", "ECDSA", keyPair.getPrivate().getAlgorithm()); | ||
| } | ||
| @Test | ||
| public void loadKeysRSA() throws IOException | ||
| { | ||
| File file = testFolder.newFile("rsa.pem"); | ||
| generateKeyPair(file, "RSA", 4096); | ||
| FileKeyPairProvider hostKeyPairProvider = new FileKeyPairProvider(); | ||
| hostKeyPairProvider.setFiles(new String [] { file.getPath() }); | ||
| Iterable<KeyPair> keyPairs = hostKeyPairProvider.loadKeys(); | ||
| Iterator<KeyPair> iterator = keyPairs.iterator(); | ||
| assertTrue(iterator.hasNext()); | ||
| KeyPair keyPair = iterator.next(); | ||
| assertNotNull(keyPair); | ||
| assertEquals("Unexpected key pair type", "RSA", keyPair.getPrivate().getAlgorithm()); | ||
| } | ||
| @Test | ||
| public void loadKeysDefault() throws IOException | ||
| { | ||
| File rsa = testFolder.newFile("rsa.pem"); | ||
| generateKeyPair(rsa, "RSA", 2048); | ||
| File ecdsa = testFolder.newFile("ecdsa.pem"); | ||
| generateKeyPair(ecdsa, "ECDSA", 0); | ||
| File eddsa = testFolder.newFile("eddsa.pem"); | ||
| generateKeyPair(eddsa, "EdDSA", 0); | ||
| File ed25519 = testFolder.newFile("ed25519.pem"); | ||
| generateKeyPair(ed25519, "ED25519", 0); | ||
| FileKeyPairProvider hostKeyPairProvider = new FileKeyPairProvider(); | ||
| hostKeyPairProvider.setFiles(new String [] { ecdsa.getPath(), eddsa.getPath(), rsa.getPath(), ed25519.getPath() }); | ||
| Iterable<KeyPair> keyPairs = hostKeyPairProvider.loadKeys(); | ||
| Iterator<KeyPair> iterator = keyPairs.iterator(); | ||
| assertTrue(iterator.hasNext()); | ||
| KeyPair keyPair = iterator.next(); | ||
| assertNotNull(keyPair); | ||
| assertEquals("Unexpected key pair type", "ECDSA", keyPair.getPrivate().getAlgorithm()); | ||
| assertTrue(iterator.hasNext()); | ||
| keyPair = iterator.next(); | ||
| assertNotNull(keyPair); | ||
| assertEquals("Unexpected key pair type", "EdDSA", keyPair.getPrivate().getAlgorithm()); | ||
| assertTrue(iterator.hasNext()); | ||
| keyPair = iterator.next(); | ||
| assertNotNull(keyPair); | ||
| assertEquals("Unexpected key pair type", "RSA", keyPair.getPrivate().getAlgorithm()); | ||
| assertTrue(iterator.hasNext()); | ||
| keyPair = iterator.next(); | ||
| assertNotNull(keyPair); | ||
| assertEquals("Unexpected key pair type", "Ed25519", keyPair.getPrivate().getAlgorithm()); | ||
| assertFalse(iterator.hasNext()); | ||
| } | ||
| } |
@@ -1,2 +0,2 @@ | ||
| name: Continous build - build and test on every push | ||
| name: Continous integration | ||
@@ -8,10 +8,13 @@ on: | ||
| - gh-pages | ||
| pull_request: | ||
| branches-ignore: | ||
| - 'release*' | ||
| - gh-pages | ||
| jobs: | ||
| build: | ||
| name: Build and test | ||
| runs-on: ${{ matrix.os }} | ||
| build_linux: | ||
| name: Linux build and test | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| matrix: | ||
| os: [ubuntu-latest, windows-latest] | ||
| java-version: [8, 11] | ||
@@ -21,3 +24,3 @@ | ||
| - name: Checkout | ||
| uses: actions/checkout@v1 | ||
| uses: actions/checkout@v4 | ||
| with: | ||
@@ -27,6 +30,13 @@ submodules: true | ||
| - name: Setup Java ${{ matrix.java-version }} | ||
| uses: actions/setup-java@v1 | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| java-version: ${{ matrix.java-version }} | ||
| distribution: 'temurin' | ||
| - name: Setup Moxie | ||
| run: | | ||
| wget http://gitblit-org.github.io/moxie/maven/com/gitblit/moxie/moxie+ant/0.10.0/moxie+ant-0.10.0.tar.gz | ||
| tar -xzf moxie+ant-0.10.0.tar.gz | ||
| moxie-0.10.0/bin/moxie -version | ||
| - name: Report Java version | ||
@@ -37,19 +47,24 @@ run: | | ||
| - name: Build with Ant | ||
| run: ant test | ||
| - name: Build with Moxie | ||
| run: moxie-0.10.0/bin/moxie test | ||
| build_j7: | ||
| name: Build and test on Java 7 | ||
| runs-on: ubuntu-latest | ||
| build_windows: | ||
| name: Windows build and test | ||
| runs-on: windows-latest | ||
| strategy: | ||
| matrix: | ||
| java-version: [8, 11] | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v1 | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| submodules: true | ||
| - name: Setup Java 7 | ||
| uses: actions/setup-java@v1 | ||
| - name: Setup Java ${{ matrix.java-version }} | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| java-version: 7 | ||
| java-version: ${{ matrix.java-version }} | ||
| distribution: 'temurin' | ||
@@ -61,9 +76,3 @@ - name: Report Java version | ||
| - name: Setup Moxie | ||
| run: | | ||
| wget http://gitblit.github.io/moxie/maven/com/gitblit/moxie/moxie+ant/0.9.4/moxie+ant-0.9.4.tar.gz | ||
| tar -xzf moxie+ant-0.9.4.tar.gz | ||
| moxie-0.9.4/bin/moxie -version | ||
| - name: Build with Moxie | ||
| run: moxie-0.9.4/bin/moxie test | ||
| - name: Build with Ant | ||
| run: ant test |
+0
-1
@@ -6,4 +6,3 @@ dist: trusty | ||
| jdk: | ||
| - openjdk7 | ||
| - openjdk8 | ||
| - oraclejdk11 |
+20
-13
@@ -8,7 +8,7 @@ <?xml version="1.0" encoding="UTF-8"?> | ||
| documentation @ http://gitblit.github.io/moxie | ||
| documentation @ http://gitblit-org.github.io/moxie | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| --> | ||
| <property name="moxie.version" value="0.9.4" /> | ||
| <property name="moxie.url" value="http://gitblit.github.io/moxie/maven" /> | ||
| <property name="moxie.version" value="0.10.0" /> | ||
| <property name="moxie.url" value="http://gitblit-org.github.io/moxie/maven" /> | ||
| <property name="moxie.jar" value="moxie-toolkit-${moxie.version}.jar" /> | ||
@@ -39,3 +39,3 @@ <property name="moxie.dir" value="${user.home}/.moxie" /> | ||
| <!-- GitHub user/organization name --> | ||
| <property name="gh.org" value="gitblit" /> | ||
| <property name="gh.org" value="gitblit-org" /> | ||
| <!-- GitHub project name --> | ||
@@ -77,3 +77,4 @@ <property name="gh.repo" value="gitblit" /> | ||
| <!-- Download links --> | ||
| <property name="gc.url" value="https://github.com/${gh.org}/gitblit/releases/download/" /> | ||
| <property name="gh.url" value="https://github.com/${gh.org}" /> | ||
| <property name="gc.url" value="${gh.url}/gitblit/releases/download/" /> | ||
| <property name="docker.url" value="https://hub.docker.com/r/gitblit/gitblit" /> | ||
@@ -649,8 +650,9 @@ | ||
| <replace token="%DOCKERURL%" value="${docker.url}" /> | ||
| <replace token="%GBLOG4JURL%" value="${gh.url}/${gh.repo}/blob/master/src/main/java/log4j.properties" /> | ||
| <properties token="%PROPERTIES%" file="${project.distrib.dir}/data/defaults.properties" /> | ||
| <regex searchPattern="\b(commit)(\s*[#]?|-){0,1}([0-9a-fA-F]{5,})\b" replacePattern="<a href='https://github.com/gitblit/gitblit/commit/$3'>commit $3</a>" /> | ||
| <regex searchPattern="\b(issue)(\s*[#]?|-){0,1}(\d+)\b" replacePattern="<a href='https://github.com/gitblit/gitblit/issues/$3'>issue $3</a>" /> | ||
| <regex searchPattern="\b(pr|pull request)(\s*[#]?|-){0,1}(\d+)\b" replacePattern="<a href='https://github.com/gitblit/gitblit/pull/$3'>pull request #$3</a>" /> | ||
| <regex searchPattern="\b(commit)(\s*[#]?|-){0,1}([0-9a-fA-F]{5,})\b" replacePattern="<a href='https://github.com/gitblit-org/gitblit/commit/$3'>commit $3</a>" /> | ||
| <regex searchPattern="\b(issue)(\s*[#]?|-){0,1}(\d+)\b" replacePattern="<a href='https://github.com/gitblit-org/gitblit/issues/$3'>issue $3</a>" /> | ||
| <regex searchPattern="\b(pr|pull request)(\s*[#]?|-){0,1}(\d+)\b" replacePattern="<a href='https://github.com/gitblit-org/gitblit/pull/$3'>pull request #$3</a>" /> | ||
| <regex searchPattern="\b(ticket)(\s*[#]?|-){0,1}(\d+)\b" replacePattern="<a href='https://dev.gitblit.com/tickets/gitblit.git/$3'>ticket $3</a>" /> | ||
@@ -727,5 +729,9 @@ | ||
| --> | ||
| <target name="updateGhPages"> | ||
| <target name="updateGhPages" depends="prepare"> | ||
| <!-- Build gh-pages branch --> | ||
| <mx:ghpages repositorydir="${basedir}" obliterate="true" /> | ||
| <mx:ghpages repositorydir="${basedir}" obliterate="true"> | ||
| <keep> | ||
| <file name="CNAME"/> | ||
| </keep> | ||
| </mx:ghpages> | ||
| </target> | ||
@@ -1119,8 +1125,9 @@ | ||
| <replace token="%DOCKERURL%" value="${docker.url}" /> | ||
| <replace token="%GBLOG4JURL%" value="${gh.url}/${gh.repo}/blob/master/src/main/java/log4j.properties" /> | ||
| <properties token="%PROPERTIES%" file="${project.distrib.dir}/data/defaults.properties" /> | ||
| <regex searchPattern="\b(commit)(\s*[#]?|-){0,1}([0-9a-fA-F]{5,})\b" replacePattern="<a href='https://github.com/gitblit/gitblit/commit/$3'>commit $3</a>" /> | ||
| <regex searchPattern="\b(issue)(\s*[#]?|-){0,1}(\d+)\b" replacePattern="<a href='https://github.com/gitblit/gitblit/issues/$3'>issue $3</a>" /> | ||
| <regex searchPattern="\b(pr|pull request)(\s*[#]?|-){0,1}(\d+)\b" replacePattern="<a href='https://github.com/gitblit/gitblit/pull/$3'>pull request #$3</a>" /> | ||
| <regex searchPattern="\b(commit)(\s*[#]?|-){0,1}([0-9a-fA-F]{5,})\b" replacePattern="<a href='https://github.com/gitblit-org/gitblit/commit/$3'>commit $3</a>" /> | ||
| <regex searchPattern="\b(issue)(\s*[#]?|-){0,1}(\d+)\b" replacePattern="<a href='https://github.com/gitblit-org/gitblit/issues/$3'>issue $3</a>" /> | ||
| <regex searchPattern="\b(pr|pull request)(\s*[#]?|-){0,1}(\d+)\b" replacePattern="<a href='https://github.com/gitblit-org/gitblit/pull/$3'>pull request #$3</a>" /> | ||
| <regex searchPattern="\b(ticket)(\s*[#]?|-){0,1}(\d+)\b" replacePattern="<a href='https://dev.gitblit.com/tickets/gitblit.git/$3'>ticket $3</a>" /> | ||
@@ -1127,0 +1134,0 @@ |
+11
-4
@@ -24,3 +24,3 @@ Gitblit | ||
| http://code.google.com/p/google-code-prettify | ||
| https://github.com/googlearchive/code-prettify | ||
@@ -129,3 +129,3 @@ --------------------------------------------------------------------------- | ||
| http://code.google.com/p/google-gson | ||
| https://github.com/google/gson | ||
@@ -370,3 +370,10 @@ --------------------------------------------------------------------------- | ||
| https://code.google.com/p/google-guice | ||
| https://github.com/google/guice | ||
| --------------------------------------------------------------------------- | ||
| clipboard.js | ||
| --------------------------------------------------------------------------- | ||
| clipboard.js, release under the | ||
| MIT License | ||
| https://clipboardjs.com |
@@ -34,2 +34,5 @@ /* | ||
| import java.io.IOException; | ||
| import java.nio.charset.Charset; | ||
| import java.nio.charset.IllegalCharsetNameException; | ||
| import java.nio.charset.UnsupportedCharsetException; | ||
| import java.util.ArrayList; | ||
@@ -40,2 +43,3 @@ import java.util.Collections; | ||
| import java.util.Set; | ||
| import java.util.StringTokenizer; | ||
@@ -59,2 +63,4 @@ import org.eclipse.jgit.errors.ConfigInvalidException; | ||
| import static java.nio.charset.StandardCharsets.UTF_8; | ||
| public final class BugtraqConfig { | ||
@@ -75,2 +81,3 @@ | ||
| private static final String LOG_LINKTEXT = "loglinktext"; | ||
| private static final String PROJECTS = "projects"; | ||
@@ -98,2 +105,3 @@ // Static ================================================================= | ||
| } | ||
| if (getString(null, URL, config, baseConfig) != null) { | ||
@@ -109,3 +117,3 @@ allNames.add(null); | ||
| final List<BugtraqEntry> entries = new ArrayList<BugtraqEntry>(); | ||
| final List<BugtraqConfigEntry> entries = new ArrayList<>(); | ||
| for (String name : allNames) { | ||
@@ -158,4 +166,22 @@ final String url = getString(name, URL, config, baseConfig); | ||
| final String projectsList = getString(name, PROJECTS, config, baseConfig); | ||
| final List<String> projects; | ||
| if (projectsList != null) { | ||
| projects = new ArrayList<>(); | ||
| final StringTokenizer tokenizer = new StringTokenizer(projectsList, ",", false); | ||
| while (tokenizer.hasMoreTokens()) { | ||
| projects.add(tokenizer.nextToken().trim()); | ||
| } | ||
| if (projects.isEmpty()) { | ||
| throw new ConfigInvalidException("'" + name + ".projects' must specify at least one project or be not present at all."); | ||
| } | ||
| } | ||
| else { | ||
| projects = null; | ||
| } | ||
| final String linkText = getString(name, LOG_LINKTEXT, config, baseConfig); | ||
| entries.add(new BugtraqEntry(url, idRegex, linkRegex, filterRegex, linkText)); | ||
| entries.add(new BugtraqConfigEntry(url, idRegex, linkRegex, filterRegex, linkText, projects)); | ||
| } | ||
@@ -173,7 +199,7 @@ | ||
| @NotNull | ||
| private final List<BugtraqEntry> entries; | ||
| private final List<BugtraqConfigEntry> entries; | ||
| // Setup ================================================================== | ||
| BugtraqConfig(@NotNull List<BugtraqEntry> entries) { | ||
| BugtraqConfig(@NotNull List<BugtraqConfigEntry> entries) { | ||
| this.entries = entries; | ||
@@ -185,3 +211,3 @@ } | ||
| @NotNull | ||
| public List<BugtraqEntry> getEntries() { | ||
| public List<BugtraqConfigEntry> getEntries() { | ||
| return Collections.unmodifiableList(entries); | ||
@@ -211,2 +237,3 @@ } | ||
| } | ||
| RevCommit commit = rw.parseCommit(headId); | ||
@@ -220,3 +247,3 @@ RevTree tree = commit.getTree(); | ||
| ObjectLoader ldr = repository.open(entid, Constants.OBJ_BLOB); | ||
| content = new String(ldr.getCachedBytes(), commit.getEncoding()); | ||
| content = new String(ldr.getCachedBytes(), guessEncoding(commit)); | ||
| break; | ||
@@ -257,2 +284,11 @@ } | ||
| @NotNull | ||
| private static Charset guessEncoding(RevCommit commit) { | ||
| try { | ||
| return commit.getEncoding(); | ||
| } catch (IllegalCharsetNameException | UnsupportedCharsetException e) { | ||
| return UTF_8; | ||
| } | ||
| } | ||
| @Nullable | ||
@@ -269,4 +305,4 @@ private static String getString(@Nullable String subsection, @NotNull String key, @NotNull Config config, @Nullable Config baseConfig) { | ||
| return value; | ||
| } | ||
| return value; | ||
| } | ||
@@ -273,0 +309,0 @@ @Nullable |
@@ -34,3 +34,3 @@ /* | ||
| final class BugtraqEntry { | ||
| public final class BugtraqEntry { | ||
@@ -37,0 +37,0 @@ // Fields ================================================================= |
@@ -60,6 +60,8 @@ /* | ||
| for (BugtraqEntry entry : config.getEntries()) { | ||
| final List<BugtraqParserIssueId> ids = entry.getParser().parse(message); | ||
| for (BugtraqParserIssueId id : ids) { | ||
| allIds.add(new IssueId(entry, id)); | ||
| for (BugtraqConfigEntry configEntry : config.getEntries()) { | ||
| for (BugtraqEntry entry : configEntry.getEntries()) { | ||
| final List<BugtraqParserIssueId> ids = entry.getParser().parse(message); | ||
| for (BugtraqParserIssueId id : ids) { | ||
| allIds.add(new IssueId(entry, id)); | ||
| } | ||
| } | ||
@@ -66,0 +68,0 @@ } |
@@ -72,2 +72,6 @@ /* | ||
| // define your jenkins access token here or set groovy.jenkinsToken in | ||
| // gitblit.properties or web.xml (https://github.com/jenkinsci/git-plugin/#push-notification-from-repository) | ||
| def jenkinsToken = gitblit.getString('groovy.jenkinsToken', 'yourtoken') | ||
| // define the repository base url | ||
@@ -77,5 +81,5 @@ def jenkinsGitbaseurl = gitblit.getString('groovy.jenkinsGitbaseurl', "${url}/r") | ||
| // define the trigger url | ||
| def triggerUrl = jenkinsUrl + "/git/notifyCommit?url=" + jenkinsGitbaseurl + "/${repository.name}" | ||
| def triggerUrl = jenkinsUrl + "/git/notifyCommit?url=" + jenkinsGitbaseurl + "/${repository.name}" + "&token=" + jenkinsToken | ||
| // trigger the build | ||
| new URL(triggerUrl).getContent() |
@@ -30,2 +30,2 @@ # GitBlit YouTrack Receive Hook | ||
| Much of this script was cobbled together from the example receive hooks in the official [GitBlit](https://github.com/gitblit/gitblit) distribution. | ||
| Much of this script was cobbled together from the example receive hooks in the official [GitBlit](https://github.com/gitblit-org/gitblit) distribution. |
@@ -284,3 +284,3 @@ /* | ||
| if (userService == null) { | ||
| JOptionPane.showMessageDialog(this, MessageFormat.format("Sorry, {0} doesn't look like a Gitblit GO installation.", folder)); | ||
| JOptionPane.showMessageDialog(this, MessageFormat.format("Sorry, {0} doesn''t look like a Gitblit GO installation.", folder)); | ||
| } else { | ||
@@ -303,3 +303,7 @@ // build empty certificate model for all users | ||
| ucm.user = userService.getUserModel(ucm.user.username); | ||
| map.put(ucm.user.username, ucm); | ||
| // Users may have been deleted, but are still present in authority.conf. | ||
| // TODO: Currently this only keeps the app from crashing. It should provide means to show obsolete user entries and delete them. | ||
| if (ucm.user != null) { | ||
| map.put(ucm.user.username, ucm); | ||
| } | ||
| } | ||
@@ -306,0 +310,0 @@ } catch (IOException e) { |
@@ -648,2 +648,24 @@ /* | ||
| public enum TlsClientCertPolicy { | ||
| REQUIRED, TRUE, OPTIONAL, FALSE, DISABLED, NONE; | ||
| public static TlsClientCertPolicy fromString(String value) { | ||
| for (TlsClientCertPolicy t : values()) { | ||
| if (t.name().equalsIgnoreCase(value)) { | ||
| switch(t) { | ||
| case TRUE: | ||
| return REQUIRED; | ||
| case FALSE: | ||
| return OPTIONAL; | ||
| case NONE: | ||
| return DISABLED; | ||
| default: | ||
| return t; | ||
| } | ||
| } | ||
| } | ||
| return TlsClientCertPolicy.OPTIONAL; | ||
| } | ||
| } | ||
| /** | ||
@@ -650,0 +672,0 @@ * The type of merge Gitblit will use when merging a ticket to the integration branch. |
@@ -230,4 +230,7 @@ /* | ||
| public String toString() { | ||
| if (propertiesFile == null) { | ||
| return "[empty]"; | ||
| } | ||
| return propertiesFile.getAbsolutePath(); | ||
| } | ||
| } |
@@ -109,3 +109,3 @@ /* | ||
| super(patchset.isFF() ? ObjectId.fromString(patchset.parent) : ObjectId.zeroId(), | ||
| ObjectId.fromString(patchset.tip), null); | ||
| ObjectId.fromString(patchset.tip), getPatchsetBranch(patchset.ticketId, patchset.number)); | ||
| this.change = new Change(username); | ||
@@ -112,0 +112,0 @@ this.change.patchset = patchset; |
@@ -777,2 +777,3 @@ /* | ||
| long ticketId = ticketService.assignNewId(repository); | ||
| patchset.ticketId = ticketId; | ||
@@ -1106,2 +1107,3 @@ // create the patchset command | ||
| newPatchset.commits = totalCommits; | ||
| newPatchset.ticketId = ticket == null ? 0 : ticket.number; | ||
@@ -1201,8 +1203,6 @@ Patchset currPatchset = ticket == null ? null : ticket.getCurrentPatchset(); | ||
| private RefUpdate updateRef(String ref, ObjectId newId, PatchsetType type) { | ||
| ObjectId ticketRefId = ObjectId.zeroId(); | ||
| ObjectId ticketRefId = null; | ||
| try { | ||
| ticketRefId = getRepository().resolve(ref); | ||
| } catch (Exception e) { | ||
| // ignore | ||
| } | ||
| } catch (Exception ignored) {} | ||
@@ -1223,3 +1223,3 @@ try { | ||
| ru.setExpectedOldObjectId(ticketRefId); | ||
| ru.setExpectedOldObjectId((ticketRefId == null) ? ObjectId.zeroId() : ticketRefId); | ||
| ru.setNewObjectId(newId); | ||
@@ -1261,3 +1261,4 @@ RefUpdate.Result result = ru.update(getRevWalk()); | ||
| } | ||
| ReceiveCommand cmd = new ReceiveCommand(ru.getOldObjectId(), ru.getNewObjectId(), ru.getName(), type); | ||
| ObjectId oldId = (ru.getOldObjectId() == null) ? ObjectId.zeroId() : ru.getOldObjectId(); | ||
| ReceiveCommand cmd = new ReceiveCommand(oldId, ru.getNewObjectId(), ru.getName(), type); | ||
| RefLogUtils.updateRefLog(user, getRepository(), Arrays.asList(cmd)); | ||
@@ -1264,0 +1265,0 @@ } |
@@ -47,3 +47,4 @@ /* | ||
| import org.eclipse.jetty.server.ServerConnector; | ||
| import org.eclipse.jetty.server.session.HashSessionManager; | ||
| import org.eclipse.jetty.server.session.SessionHandler; | ||
| import org.eclipse.jetty.servlet.ListenerHolder; | ||
| import org.eclipse.jetty.util.security.Constraint; | ||
@@ -61,2 +62,3 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool; | ||
| import com.gitblit.Constants.TlsClientCertPolicy; | ||
| import com.gitblit.authority.GitblitAuthority; | ||
@@ -294,10 +296,14 @@ import com.gitblit.authority.NewCertificateConfig; | ||
| serverKeyStore, serverTrustStore, params.storePassword, caRevocationList); | ||
| if (params.requireClientCertificates) { | ||
| TlsClientCertPolicy clientCertPolicy = TlsClientCertPolicy.fromString(params.requireClientCertificates); | ||
| if (clientCertPolicy == TlsClientCertPolicy.REQUIRED) { | ||
| factory.setNeedClientAuth(true); | ||
| } else if (clientCertPolicy == TlsClientCertPolicy.OPTIONAL) { | ||
| factory.setNeedClientAuth(false); | ||
| factory.setWantClientAuth(true); | ||
| } else { | ||
| factory.setWantClientAuth(true); | ||
| factory.setNeedClientAuth(false); | ||
| factory.setWantClientAuth(false); | ||
| } | ||
| ServerConnector connector = new ServerConnector(server, factory); | ||
| connector.setSoLingerTime(-1); | ||
| connector.setIdleTimeout(settings.getLong(Keys.server.httpIdleTimeout, 30000L)); | ||
@@ -339,3 +345,2 @@ connector.setPort(params.securePort); | ||
| ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConfig)); | ||
| connector.setSoLingerTime(-1); | ||
| connector.setIdleTimeout(settings.getLong(Keys.server.httpIdleTimeout, 30000L)); | ||
@@ -382,9 +387,9 @@ connector.setPort(params.port); | ||
| // Set cookies HttpOnly so they are not accessible to JavaScript engines | ||
| HashSessionManager sessionManager = new HashSessionManager(); | ||
| sessionManager.setHttpOnly(true); | ||
| SessionHandler sessionHandler = rootContext.getSessionHandler(); | ||
| sessionHandler.setHttpOnly(true); | ||
| // Use secure cookies if only serving https | ||
| sessionManager.setSecureRequestOnly( (params.port <= 0 && params.securePort > 0) || | ||
| (params.port > 0 && params.securePort > 0 && settings.getBoolean(Keys.server.redirectToHttpsPort, true)) ); | ||
| rootContext.getSessionHandler().setSessionManager(sessionManager); | ||
| sessionHandler.setSecureRequestOnly( (params.port <= 0 && params.securePort > 0) || | ||
| (params.port > 0 && params.securePort > 0 && settings.getBoolean(Keys.server.redirectToHttpsPort, true)) ); | ||
@@ -455,8 +460,9 @@ // Ensure there is a defined User Service | ||
| // Configure this context to use the Security Handler defined before | ||
| rootContext.setHandler(sh); | ||
| rootContext.setSecurityHandler(sh); | ||
| } | ||
| // Setup the Gitblit context | ||
| GitblitContext gitblit = newGitblit(settings, baseFolder); | ||
| rootContext.addEventListener(gitblit); | ||
| ListenerHolder gitblitHolder = new ListenerHolder(GitblitContext.class); | ||
| gitblitHolder.setListener(newGitblit(settings, baseFolder)); | ||
| rootContext.getServletHandler().addListener(gitblitHolder); | ||
@@ -609,3 +615,3 @@ try { | ||
| @Option(name = "--requireClientCertificates", usage = "Require client X509 certificates for https connections.") | ||
| public Boolean requireClientCertificates = FILESETTINGS.getBoolean(Keys.server.requireClientCertificates, false); | ||
| public String requireClientCertificates = FILESETTINGS.getString(Keys.server.requireClientCertificates, "optional"); | ||
@@ -612,0 +618,0 @@ /* |
@@ -318,3 +318,3 @@ /* | ||
| public synchronized boolean refreshRegistry(boolean verifyChecksum) { | ||
| String dr = "http://gitblit.github.io/gitblit-registry/plugins.json"; | ||
| String dr = "http://gitblit-org.github.io/gitblit-registry/plugins.json"; | ||
| String url = runtimeManager.getSettings().getString(Keys.plugins.registry, dr); | ||
@@ -321,0 +321,0 @@ try { |
@@ -54,5 +54,5 @@ /* | ||
| public ServerStatus() { | ||
| public ServerStatus(String version) { | ||
| this.bootDate = new Date(); | ||
| this.version = Constants.getVersion(); | ||
| this.version = version; | ||
| this.releaseDate = Constants.getBuildDate(); | ||
@@ -80,2 +80,6 @@ | ||
| public ServerStatus() { | ||
| this(Constants.getVersion()); | ||
| } | ||
| private void put(String key) { | ||
@@ -82,0 +86,0 @@ systemProperties.put(key, System.getProperty(key)); |
@@ -1166,2 +1166,3 @@ /* | ||
| public long ticketId; | ||
| public int number; | ||
@@ -1168,0 +1169,0 @@ public int rev; |
@@ -113,3 +113,3 @@ /* | ||
| public List<RegistrantAccessPermission> getRepositoryPermissions() { | ||
| List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>(); | ||
| List<RegistrantAccessPermission> list = new ArrayList<>(); | ||
| if (canAdmin()) { | ||
@@ -139,3 +139,2 @@ // user has REWIND access to all repositories | ||
| // include immutable team permissions, being careful to preserve order | ||
| Set<RegistrantAccessPermission> set = new LinkedHashSet<RegistrantAccessPermission>(list); | ||
| for (TeamModel team : teams) { | ||
@@ -148,6 +147,13 @@ for (RegistrantAccessPermission teamPermission : team.getRepositoryPermissions()) { | ||
| teamPermission.mutable = false; | ||
| set.add(teamPermission); | ||
| int i = list.indexOf(teamPermission); | ||
| if (i < 0) list.add(teamPermission); | ||
| else { | ||
| RegistrantAccessPermission lp = list.get(i); | ||
| if (teamPermission.permission.exceeds(lp.permission)) { | ||
| list.set(i, teamPermission); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return new ArrayList<RegistrantAccessPermission>(set); | ||
| return list; | ||
| } | ||
@@ -154,0 +160,0 @@ |
@@ -9,3 +9,2 @@ package com.gitblit.service; | ||
| import java.net.InetAddress; | ||
| import java.text.MessageFormat; | ||
| import java.util.ArrayList; | ||
@@ -103,4 +102,3 @@ import java.util.Arrays; | ||
| // the status for this registration has downgraded | ||
| logger.warn("Federation pull status of {0} is now {1}", registration.name, | ||
| is.name()); | ||
| logger.warn("Federation pull status of {} is now {}", registration.name, is.name()); | ||
| if (registration.notifyOnError) { | ||
@@ -115,5 +113,3 @@ String message = "Federation pull of " + registration.name + " @ " | ||
| } catch (Throwable t) { | ||
| logger.error(MessageFormat.format( | ||
| "Failed to pull from federated gitblit ({0} @ {1})", registration.name, | ||
| registration.url), t); | ||
| logger.error("Failed to pull from federated gitblit ({} @ {})", registration.name, registration.url, t); | ||
| } finally { | ||
@@ -139,5 +135,4 @@ reschedule(registration); | ||
| if (c != null) { | ||
| logger.error(MessageFormat | ||
| .format("Illegal character ''{0}'' in folder name ''{1}'' of federation registration {2}!", | ||
| c, registrationFolder, registration.name)); | ||
| logger.error("Illegal character '{}' in folder name '{}' of federation registration {}!", | ||
| c, registrationFolder, registration.name); | ||
| return; | ||
@@ -154,5 +149,4 @@ } | ||
| if (!repository.hasCommits) { | ||
| logger.warn(MessageFormat.format( | ||
| "Skipping federated repository {0} from {1} @ {2}. Repository is EMPTY.", | ||
| repository.name, registration.name, registration.url)); | ||
| logger.warn("Skipping federated repository {} from {} @ {}. Repository is EMPTY.", | ||
| repository.name, registration.name, registration.url); | ||
| registration.updateStatus(repository, FederationPullStatus.SKIPPED); | ||
@@ -189,3 +183,3 @@ continue; | ||
| if (existingRepository == null && gitblit.isCollectingGarbage(repositoryName)) { | ||
| logger.warn(MessageFormat.format("Skipping local repository {0}, busy collecting garbage", repositoryName)); | ||
| logger.warn("Skipping local repository {}, busy collecting garbage", repositoryName); | ||
| continue; | ||
@@ -205,5 +199,4 @@ } | ||
| 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)); | ||
| logger.warn("Skipping federated repository {} from {} @ {}. Origin does not match, consider EXCLUDING.", | ||
| repository.name, registration.name, registration.url); | ||
| registration.updateStatus(repository, FederationPullStatus.SKIPPED); | ||
@@ -217,4 +210,3 @@ continue; | ||
| Constants.FEDERATION_USER, registration.token); | ||
| logger.info(MessageFormat.format("Pulling federated repository {0} from {1} @ {2}", | ||
| repository.name, registration.name, registration.url)); | ||
| logger.info("Pulling federated repository {} from {} @ {}", repository.name, registration.name, registration.url); | ||
@@ -231,3 +223,3 @@ CloneResult result = JGitUtils.cloneRepository(registrationFolderFile, repository.name, | ||
| repository.showRemoteBranches = !registration.mirror; | ||
| logger.info(MessageFormat.format(" cloning {0}", repository.name)); | ||
| logger.info(" cloning {}", repository.name); | ||
| registration.updateStatus(repository, FederationPullStatus.MIRRORED); | ||
@@ -252,4 +244,3 @@ } else { | ||
| JGitUtils.setBranchRef(r, branch, hash); | ||
| logger.info(MessageFormat.format(" resetting {0} of {1} to {2}", branch, | ||
| repository.name, hash)); | ||
| logger.info(" resetting {} of {} to {}", branch, repository.name, hash); | ||
| } | ||
@@ -265,4 +256,3 @@ } | ||
| JGitUtils.setHEADtoRef(r, newHead); | ||
| logger.info(MessageFormat.format(" resetting HEAD of {0} to {1}", | ||
| repository.name, newHead)); | ||
| logger.info(" resetting HEAD of {} to {}", repository.name, newHead); | ||
| registration.updateStatus(repository, FederationPullStatus.MIRRORED); | ||
@@ -415,5 +405,3 @@ } else { | ||
| } catch (IOException e) { | ||
| logger.warn(MessageFormat.format( | ||
| "Failed to retrieve USERS from federated gitblit ({0} @ {1})", | ||
| registration.name, registration.url), e); | ||
| logger.warn("Failed to retrieve USERS from federated gitblit ({} @ {})", registration.name, registration.url, e); | ||
| } | ||
@@ -437,5 +425,3 @@ | ||
| } catch (IOException e) { | ||
| logger.warn(MessageFormat.format( | ||
| "Failed to retrieve TEAMS from federated gitblit ({0} @ {1})", | ||
| registration.name, registration.url), e); | ||
| logger.warn("Failed to retrieve TEAMS from federated gitblit ({} @ {})", registration.name, registration.url, e); | ||
| } | ||
@@ -457,5 +443,3 @@ | ||
| } catch (IOException e) { | ||
| logger.warn(MessageFormat.format( | ||
| "Failed to retrieve SETTINGS from federated gitblit ({0} @ {1})", | ||
| registration.name, registration.url), e); | ||
| logger.warn("Failed to retrieve SETTINGS from federated gitblit ({} @ {})", registration.name, registration.url, e); | ||
| } | ||
@@ -480,5 +464,3 @@ | ||
| } catch (IOException e) { | ||
| logger.warn(MessageFormat.format( | ||
| "Failed to retrieve SCRIPTS from federated gitblit ({0} @ {1})", | ||
| registration.name, registration.url), e); | ||
| logger.warn("Failed to retrieve SCRIPTS from federated gitblit ({} @ {})", registration.name, registration.url, e); | ||
| } | ||
@@ -505,4 +487,4 @@ } | ||
| FederationUtils.acknowledgeStatus(addr.getHostAddress(), registration); | ||
| logger.info(MessageFormat.format("Pull status sent to {0}", registration.url)); | ||
| logger.info("Pull status sent to {}", registration.url); | ||
| } | ||
| } |
@@ -18,3 +18,2 @@ /* | ||
| import java.text.MessageFormat; | ||
| import java.util.Calendar; | ||
@@ -147,3 +146,3 @@ import java.util.Date; | ||
| if (isCollectingGarbage(repositoryName)) { | ||
| logger.warn(MessageFormat.format("Already collecting garbage from {0}?!?", repositoryName)); | ||
| logger.warn("Already collecting garbage from {}?!?", repositoryName); | ||
| continue; | ||
@@ -158,3 +157,3 @@ } | ||
| if (repository == null) { | ||
| logger.warn(MessageFormat.format("GCExecutor is missing repository {0}?!?", repositoryName)); | ||
| logger.warn("GCExecutor is missing repository {}?!?", repositoryName); | ||
| continue; | ||
@@ -164,3 +163,3 @@ } | ||
| if (!repositoryManager.isIdle(repository)) { | ||
| logger.debug(MessageFormat.format("GCExecutor is skipping {0} because it is not idle", repositoryName)); | ||
| logger.debug("GCExecutor is skipping {} because it is not idle", repositoryName); | ||
| continue; | ||
@@ -173,7 +172,7 @@ } | ||
| if (!setGCStatus(repositoryName, GCStatus.COLLECTING)) { | ||
| logger.warn(MessageFormat.format("Can not acquire GC lock for {0}, skipping", repositoryName)); | ||
| logger.warn("Can not acquire GC lock for {}, skipping", repositoryName); | ||
| continue; | ||
| } | ||
| logger.debug(MessageFormat.format("GCExecutor locked idle repository {0}", repositoryName)); | ||
| logger.debug("GCExecutor locked idle repository {}", repositoryName); | ||
@@ -204,3 +203,3 @@ Git git = new Git(repository); | ||
| long looseKB = sizeOfLooseObjects/1024L; | ||
| logger.info(MessageFormat.format("Collecting {1} KB of loose objects from {0}", repositoryName, looseKB)); | ||
| logger.info("Collecting {} KB of loose objects from {}", looseKB, repositoryName ); | ||
@@ -213,3 +212,3 @@ // do the deed | ||
| } catch (Exception e) { | ||
| logger.error("Error collecting garbage in " + repositoryName, e); | ||
| logger.error("Error collecting garbage in {}", repositoryName, e); | ||
| } finally { | ||
@@ -229,3 +228,3 @@ // cleanup | ||
| releaseLock(repositoryName); | ||
| logger.debug(MessageFormat.format("GCExecutor released GC lock for {0}", repositoryName)); | ||
| logger.debug("GCExecutor released GC lock for {}", repositoryName); | ||
| } | ||
@@ -232,0 +231,0 @@ } |
@@ -175,3 +175,3 @@ /* | ||
| if (repositoryManager.isCollectingGarbage(model.name)) { | ||
| logger.info(MessageFormat.format("Skipping Lucene index of {0}, busy garbage collecting", repositoryName)); | ||
| logger.info("Skipping Lucene index of {}, busy garbage collecting", repositoryName); | ||
| } | ||
@@ -204,5 +204,4 @@ continue; | ||
| if (result.commitCount > 0) { | ||
| String msg = "Built {0} Lucene index from {1} commits and {2} files across {3} branches in {4} secs"; | ||
| logger.info(MessageFormat.format(msg, model.name, result.commitCount, | ||
| result.blobCount, result.branchCount, result.duration())); | ||
| logger.info("Built {} Lucene index from {} commits and {} files across {} branches in {} secs", | ||
| model.name, result.commitCount, result.blobCount, result.branchCount, result.duration()); | ||
| } | ||
@@ -218,13 +217,11 @@ } else { | ||
| if (result.commitCount > 0) { | ||
| String msg = "Updated {0} Lucene index with {1} commits and {2} files across {3} branches in {4} secs"; | ||
| logger.info(MessageFormat.format(msg, model.name, result.commitCount, | ||
| result.blobCount, result.branchCount, result.duration())); | ||
| logger.info("Updated {} Lucene index with {} commits and {} files across {} branches in {} secs", | ||
| model.name, result.commitCount, result.blobCount, result.branchCount, result.duration()); | ||
| } | ||
| } else { | ||
| String msg = "Could not update {0} Lucene index!"; | ||
| logger.error(MessageFormat.format(msg, model.name)); | ||
| logger.error("Could not update {} Lucene index!", model.name); | ||
| } | ||
| } | ||
| } catch (Throwable t) { | ||
| logger.error(MessageFormat.format("Lucene indexing failure for {0}", model.name), t); | ||
| logger.error("Lucene indexing failure for {}", model.name, t); | ||
| } | ||
@@ -245,3 +242,3 @@ } | ||
| } catch (Exception e) { | ||
| logger.error("Failed to close index searcher for " + repositoryName, e); | ||
| logger.error("Failed to close index searcher for {}", repositoryName, e); | ||
| } | ||
@@ -255,3 +252,3 @@ | ||
| } catch (Exception e) { | ||
| logger.error("Failed to close index writer for " + repositoryName, e); | ||
| logger.error("Failed to close index writer for {}", repositoryName, e); | ||
| } | ||
@@ -270,3 +267,3 @@ } | ||
| } catch (Throwable t) { | ||
| logger.error("Failed to close Lucene writer for " + writer, t); | ||
| logger.error("Failed to close Lucene writer for {}", writer, t); | ||
| } | ||
@@ -281,3 +278,3 @@ } | ||
| } catch (Throwable t) { | ||
| logger.error("Failed to close Lucene searcher for " + searcher, t); | ||
| logger.error("Failed to close Lucene searcher for {}", searcher, t); | ||
| } | ||
@@ -604,3 +601,3 @@ } | ||
| } catch (Exception e) { | ||
| logger.error("Exception while reindexing " + model.name, e); | ||
| logger.error("Exception while reindexing {}", model.name, e); | ||
| } | ||
@@ -684,3 +681,3 @@ return result; | ||
| } catch (Exception e) { | ||
| logger.error(MessageFormat.format("Exception while indexing commit {0} in {1}", commit.getId().getName(), repositoryName), e); | ||
| logger.error("Exception while indexing commit {} in {}", commit.getId().getName(), repositoryName, e); | ||
| } | ||
@@ -713,6 +710,6 @@ return result; | ||
| if (numDocsBefore == numDocsAfter) { | ||
| logger.debug(MessageFormat.format("no records found to delete {0}", query.toString())); | ||
| logger.debug("no records found to delete {}", query.toString()); | ||
| return false; | ||
| } else { | ||
| logger.debug(MessageFormat.format("deleted {0} records with {1}", numDocsBefore - numDocsAfter, query.toString())); | ||
| logger.debug("deleted {} records with {}", numDocsBefore - numDocsAfter, query.toString()); | ||
| return true; | ||
@@ -846,3 +843,3 @@ } | ||
| } catch (Throwable t) { | ||
| logger.error(MessageFormat.format("Exception while updating {0} Lucene index", model.name), t); | ||
| logger.error("Exception while updating {} Lucene index", model.name, t); | ||
| } | ||
@@ -890,3 +887,3 @@ return result; | ||
| } catch (Exception e) { | ||
| logger.error(MessageFormat.format("Exception while incrementally updating {0} Lucene index", repositoryName), e); | ||
| logger.error("Exception while incrementally updating {} Lucene index", repositoryName, e); | ||
| } | ||
@@ -1064,3 +1061,3 @@ return false; | ||
| } catch (Exception e) { | ||
| logger.error(MessageFormat.format("Exception while searching for {0}", text), e); | ||
| logger.error("Exception while searching for {}", text, e); | ||
| } | ||
@@ -1067,0 +1064,0 @@ return new ArrayList<SearchResult>(results); |
@@ -122,3 +122,3 @@ /* | ||
| if (repository == null) { | ||
| logger.warn(MessageFormat.format("MirrorExecutor is missing repository {0}?!?", repositoryName)); | ||
| logger.warn("MirrorExecutor is missing repository {}?!?", repositoryName); | ||
| continue; | ||
@@ -208,3 +208,3 @@ } | ||
| } catch (Exception e) { | ||
| logger.error("Error updating mirror " + repositoryName, e); | ||
| logger.error("Error updating mirror {}", repositoryName, e); | ||
| } finally { | ||
@@ -211,0 +211,0 @@ // cleanup |
@@ -35,2 +35,3 @@ /* | ||
| import com.gitblit.instance.GitblitInstance; | ||
| import org.slf4j.Logger; | ||
@@ -91,2 +92,4 @@ import org.slf4j.LoggerFactory; | ||
| private final GitblitInstance instance = new GitblitInstance(); | ||
| /** | ||
@@ -229,2 +232,6 @@ * Construct a Gitblit WAR/Express context. | ||
| } | ||
| instance.init(runtime); | ||
| // The instance is up and running. Make it count. | ||
| instance.start(); | ||
| } | ||
@@ -309,2 +316,4 @@ | ||
| this.instance.stop(); | ||
| for (IManager manager : managers) { | ||
@@ -311,0 +320,0 @@ logger.debug("stopping {}", manager.getClass().getSimpleName()); |
@@ -297,3 +297,3 @@ /* | ||
| if (c != null) { | ||
| logger.error(MessageFormat.format("Invalid character '{0}' in repository name {1}!", c, repository)); | ||
| logger.error(MessageFormat.format("Invalid character ''{0}'' in repository name {1}!", c, repository)); | ||
| return null; | ||
@@ -300,0 +300,0 @@ } |
@@ -19,3 +19,2 @@ /* | ||
| import java.io.IOException; | ||
| import java.text.MessageFormat; | ||
| import java.util.ArrayList; | ||
@@ -173,4 +172,3 @@ import java.util.Arrays; | ||
| TicketModel ticket = getTicket(repository, ticketId); | ||
| log.info(MessageFormat.format("indexing ticket #{0,number,0}: {1}", | ||
| ticketId, ticket.title)); | ||
| log.info("indexing ticket #{}: {}", ticketId, ticket.title); | ||
| indexer.index(ticket); | ||
@@ -180,3 +178,3 @@ } | ||
| long end = System.nanoTime(); | ||
| log.info("incremental indexing of {0} ticket(s) completed in {1} msecs", | ||
| log.info("incremental indexing of {} ticket(s) completed in {} msecs", | ||
| ids.size(), TimeUnit.NANOSECONDS.toMillis(end - start)); | ||
@@ -188,3 +186,3 @@ } finally { | ||
| default: | ||
| log.warn("Unexpected receive type {} in BranchTicketService.onRefsChanged" + cmd.getType()); | ||
| log.warn("Unexpected receive type {} in BranchTicketService.onRefsChanged", cmd.getType()); | ||
| break; | ||
@@ -223,6 +221,6 @@ } | ||
| case RENAMED: | ||
| log.info(db.getDirectory() + " " + cmd.getRefLogMessage()); | ||
| log.info("{} {}", db.getDirectory(), cmd.getRefLogMessage()); | ||
| return getTicketsBranch(db); | ||
| default: | ||
| log.error("failed to rename " + oldRef.getName() + " => " + BRANCH + " (" + res.name() + ")"); | ||
| log.error("failed to rename {} => {} ({})", oldRef.getName(), BRANCH, res.name()); | ||
| } | ||
@@ -296,3 +294,3 @@ } catch (IOException e) { | ||
| } catch (IOException e) { | ||
| log.error("failed to read " + file, e); | ||
| log.error("failed to read {}", file, e); | ||
| } finally { | ||
@@ -515,4 +513,3 @@ if (rw != null) { | ||
| } catch (Exception e) { | ||
| log.error("failed to deserialize {}/{}\n{}", | ||
| new Object [] { repository, path.path, e.getMessage()}); | ||
| log.error("failed to deserialize {}/{}\n{}", repository, path.path, e.getMessage()); | ||
| log.error(null, e); | ||
@@ -711,4 +708,3 @@ } | ||
| } catch (Throwable t) { | ||
| log.error(MessageFormat.format("Failed to delete ticket {0,number,0} from {1}", | ||
| ticket.number, db.getDirectory()), t); | ||
| log.error("Failed to delete ticket {} from {}", ticket.number, db.getDirectory(), t); | ||
| } finally { | ||
@@ -744,4 +740,3 @@ // release the treewalk | ||
| } catch (Throwable t) { | ||
| log.error(MessageFormat.format("Failed to commit ticket {0,number,0} to {1}", | ||
| ticketId, db.getDirectory()), t); | ||
| log.error("Failed to commit ticket {} to {}", ticketId, db.getDirectory(), t); | ||
| } finally { | ||
@@ -748,0 +743,0 @@ db.close(); |
@@ -20,3 +20,2 @@ /* | ||
| import java.io.IOException; | ||
| import java.text.MessageFormat; | ||
| import java.util.ArrayList; | ||
@@ -278,4 +277,3 @@ import java.util.Collections; | ||
| } catch (Exception e) { | ||
| log.error("failed to deserialize {}/{}\n{}", | ||
| new Object [] { repository, journal, e.getMessage()}); | ||
| log.error("failed to deserialize {}/{}\n{}", repository, journal, e.getMessage()); | ||
| log.error(null, e); | ||
@@ -486,4 +484,3 @@ } | ||
| } catch (Throwable t) { | ||
| log.error(MessageFormat.format("Failed to commit ticket {0,number,0} to {1}", | ||
| ticketId, db.getDirectory()), t); | ||
| log.error("Failed to commit ticket {} to {}", ticketId, db.getDirectory(), t); | ||
| } finally { | ||
@@ -490,0 +487,0 @@ db.close(); |
@@ -366,3 +366,3 @@ /* | ||
| } catch (Exception e) { | ||
| log.error("invalid tickets settings for " + repository, e); | ||
| log.error("invalid tickets settings for {}", repository, e); | ||
| } finally { | ||
@@ -412,3 +412,3 @@ db.close(); | ||
| } catch (IOException e) { | ||
| log.error("failed to create label " + label + " in " + repository, e); | ||
| log.error("failed to create label {} in {}", label, repository, e); | ||
| } finally { | ||
@@ -441,3 +441,3 @@ if (db != null) { | ||
| } catch (IOException e) { | ||
| log.error("failed to update label " + label + " in " + repository, e); | ||
| log.error("failed to update label {} in {}", label, repository, e); | ||
| } finally { | ||
@@ -483,3 +483,3 @@ if (db != null) { | ||
| } catch (IOException e) { | ||
| log.error("failed to rename label " + oldName + " in " + repository, e); | ||
| log.error("failed to rename label {} in {}", oldName, repository, e); | ||
| } finally { | ||
@@ -515,3 +515,3 @@ if (db != null) { | ||
| } catch (IOException e) { | ||
| log.error("failed to delete label " + label + " in " + repository, e); | ||
| log.error("failed to delete label {} in {}", label, repository, e); | ||
| } finally { | ||
@@ -551,4 +551,3 @@ if (db != null) { | ||
| } catch (ParseException e) { | ||
| log.error("failed to parse {} milestone {} due date \"{}\"", | ||
| new Object [] { repository, name, due }); | ||
| log.error("failed to parse {} milestone {} due date \"{}\"", repository, name, due, e); | ||
| } | ||
@@ -560,3 +559,3 @@ } | ||
| } catch (Exception e) { | ||
| log.error("invalid tickets settings for " + repository, e); | ||
| log.error("invalid tickets settings for {}", repository, e); | ||
| } finally { | ||
@@ -627,3 +626,3 @@ db.close(); | ||
| } catch (IOException e) { | ||
| log.error("failed to create milestone " + milestone + " in " + repository, e); | ||
| log.error("failed to create milestone {} in {}", milestone, repository, e); | ||
| } finally { | ||
@@ -662,3 +661,3 @@ if (db != null) { | ||
| } catch (IOException e) { | ||
| log.error("failed to update milestone " + milestone + " in " + repository, e); | ||
| log.error("failed to update milestone {} in {}", milestone, repository, e); | ||
| } finally { | ||
@@ -736,3 +735,3 @@ if (db != null) { | ||
| } catch (IOException e) { | ||
| log.error("failed to rename milestone " + oldName + " in " + repository, e); | ||
| log.error("failed to rename milestone {} in {}", oldName, repository, e); | ||
| } finally { | ||
@@ -801,3 +800,3 @@ if (db != null) { | ||
| } catch (IOException e) { | ||
| log.error("failed to delete milestone " + milestone + " in " + repository, e); | ||
| log.error("failed to delete milestone {} in {}", milestone, repository, e); | ||
| } finally { | ||
@@ -1233,4 +1232,3 @@ if (db != null) { | ||
| if (success) { | ||
| log.info(MessageFormat.format("Deleted {0} ticket #{1,number,0}: {2}", | ||
| repository.name, ticketId, ticket.title)); | ||
| log.info("Deleted {} ticket #{}: {}", repository.name, ticketId, ticket.title); | ||
| ticketsCache.invalidate(new TicketKey(repository, ticketId)); | ||
@@ -1237,0 +1235,0 @@ indexer.delete(ticket); |
@@ -184,3 +184,3 @@ /* | ||
| } catch (JedisException e) { | ||
| log.error("failed to check hasTicket from Redis @ " + getUrl(), e); | ||
| log.error("failed to check hasTicket from Redis @ {}", getUrl(), e); | ||
| pool.returnBrokenResource(jedis); | ||
@@ -209,3 +209,3 @@ jedis = null; | ||
| } catch (JedisException e) { | ||
| log.error("failed to assign new ticket id in Redis @ " + getUrl(), e); | ||
| log.error("failed to assign new ticket id in Redis @ {}", getUrl(), e); | ||
| pool.returnBrokenResource(jedis); | ||
@@ -246,3 +246,3 @@ jedis = null; | ||
| } catch (JedisException e) { | ||
| log.error("failed to assign new ticket id in Redis @ " + getUrl(), e); | ||
| log.error("failed to assign new ticket id in Redis @ {}", getUrl(), e); | ||
| pool.returnBrokenResource(jedis); | ||
@@ -306,3 +306,3 @@ jedis = null; | ||
| } catch (JedisException e) { | ||
| log.error("failed to retrieve tickets from Redis @ " + getUrl(), e); | ||
| log.error("failed to retrieve tickets from Redis @ {}", getUrl(), e); | ||
| pool.returnBrokenResource(jedis); | ||
@@ -345,3 +345,3 @@ jedis = null; | ||
| } catch (JedisException e) { | ||
| log.error("failed to retrieve ticket from Redis @ " + getUrl(), e); | ||
| log.error("failed to retrieve ticket from Redis @ {}", getUrl(), e); | ||
| pool.returnBrokenResource(jedis); | ||
@@ -379,3 +379,3 @@ jedis = null; | ||
| } catch (JedisException e) { | ||
| log.error("failed to retrieve journal from Redis @ " + getUrl(), e); | ||
| log.error("failed to retrieve journal from Redis @ {}", getUrl(), e); | ||
| pool.returnBrokenResource(jedis); | ||
@@ -463,5 +463,5 @@ jedis = null; | ||
| success = true; | ||
| log.debug("deleted ticket {} from Redis @ {}", "" + ticket.number, getUrl()); | ||
| log.debug("deleted ticket {} from Redis @ {}", ticket.number, getUrl()); | ||
| } catch (JedisException e) { | ||
| log.error("failed to delete ticket from Redis @ " + getUrl(), e); | ||
| log.error("failed to delete ticket from Redis @ {}", getUrl(), e); | ||
| pool.returnBrokenResource(jedis); | ||
@@ -507,6 +507,6 @@ jedis = null; | ||
| log.debug("updated ticket {} in Redis @ {}", "" + ticketId, getUrl()); | ||
| log.debug("updated ticket {} in Redis @ {}", ticketId, getUrl()); | ||
| return true; | ||
| } catch (JedisException e) { | ||
| log.error("failed to update ticket cache in Redis @ " + getUrl(), e); | ||
| log.error("failed to update ticket cache in Redis @ {}", getUrl(), e); | ||
| pool.returnBrokenResource(jedis); | ||
@@ -543,3 +543,3 @@ jedis = null; | ||
| } catch (JedisException e) { | ||
| log.error("failed to delete all tickets in Redis @ " + getUrl(), e); | ||
| log.error("failed to delete all tickets in Redis @ {}", getUrl(), e); | ||
| pool.returnBrokenResource(jedis); | ||
@@ -573,3 +573,3 @@ jedis = null; | ||
| } catch (JedisException e) { | ||
| log.error("failed to rename tickets in Redis @ " + getUrl(), e); | ||
| log.error("failed to rename tickets in Redis @ {}", getUrl(), e); | ||
| pool.returnBrokenResource(jedis); | ||
@@ -576,0 +576,0 @@ jedis = null; |
@@ -20,3 +20,2 @@ /* | ||
| import java.io.IOException; | ||
| import java.text.MessageFormat; | ||
| import java.text.ParseException; | ||
@@ -218,6 +217,6 @@ import java.util.ArrayList; | ||
| if (numDocsBefore == numDocsAfter) { | ||
| log.debug(MessageFormat.format("no records found to delete in {0}", repository)); | ||
| log.debug("no records found to delete in {}", repository); | ||
| return false; | ||
| } else { | ||
| log.debug(MessageFormat.format("deleted {0} records in {1}", numDocsBefore - numDocsAfter, repository)); | ||
| log.debug("deleted {} records in {}", numDocsBefore - numDocsAfter, repository); | ||
| return true; | ||
@@ -292,3 +291,3 @@ } | ||
| } catch (Exception e) { | ||
| log.error("Failed to delete ticket " + ticket.number, e); | ||
| log.error("Failed to delete ticket {}", ticket.number, e); | ||
| } | ||
@@ -317,6 +316,6 @@ return false; | ||
| if (numDocsBefore == numDocsAfter) { | ||
| log.debug(MessageFormat.format("no records found to delete in {0}", repository)); | ||
| log.debug("no records found to delete in {}", repository); | ||
| return false; | ||
| } else { | ||
| log.debug(MessageFormat.format("deleted {0} records in {1}", numDocsBefore - numDocsAfter, repository)); | ||
| log.debug("deleted {} records in {}", numDocsBefore - numDocsAfter, repository); | ||
| return true; | ||
@@ -390,3 +389,3 @@ } | ||
| } catch (Exception e) { | ||
| log.error(MessageFormat.format("Exception while searching for {0}", text), e); | ||
| log.error("Exception while searching for {}", text, e); | ||
| } | ||
@@ -443,3 +442,3 @@ return new ArrayList<QueryResult>(results); | ||
| } catch (Exception e) { | ||
| log.error(MessageFormat.format("Exception while searching for {0}", queryText), e); | ||
| log.error("Exception while searching for {}", queryText, e); | ||
| } | ||
@@ -446,0 +445,0 @@ return new ArrayList<QueryResult>(results); |
@@ -20,2 +20,3 @@ /* | ||
| import java.io.InputStream; | ||
| import java.io.UncheckedIOException; | ||
| import java.text.DateFormat; | ||
@@ -38,6 +39,6 @@ import java.text.MessageFormat; | ||
| import org.apache.commons.io.IOUtils; | ||
| import org.apache.log4j.Logger; | ||
| import org.eclipse.jgit.diff.DiffEntry.ChangeType; | ||
| import org.eclipse.jgit.lib.Repository; | ||
| import org.eclipse.jgit.revwalk.RevCommit; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
@@ -101,2 +102,4 @@ | ||
| private final Logger log = LoggerFactory.getLogger(getClass()); | ||
| public TicketNotifier( | ||
@@ -158,3 +161,3 @@ IRuntimeManager runtimeManager, | ||
| } catch (Exception e) { | ||
| Logger.getLogger(getClass()).error("failed to queue mailing for #" + ticket.number, e); | ||
| log.error("failed to queue mailing for #{}", ticket.number, e); | ||
| } | ||
@@ -210,3 +213,3 @@ return null; | ||
| } catch (Exception e) { | ||
| Logger.getLogger(getClass()).error("failed to get changed paths", e); | ||
| log.error("failed to get changed paths", e); | ||
| } finally { | ||
@@ -560,5 +563,3 @@ if (repo != null) { | ||
| } else { | ||
| LoggerFactory.getLogger(getClass()).warn( | ||
| MessageFormat.format("ticket {0}-{1,number,0}: {2} can not receive notification", | ||
| repository.name, ticket.number, user.username)); | ||
| log.warn("ticket {}-{}: {} can not receive notification", repository.name, ticket.number, user.username); | ||
| } | ||
@@ -603,5 +604,3 @@ } | ||
| } else { | ||
| LoggerFactory.getLogger(getClass()).warn( | ||
| MessageFormat.format("ticket {0}-{1,number,0}: {2} can not receive notification", | ||
| repository.name, ticket.number, user.username)); | ||
| log.warn("ticket {}-{}: {} can not receive notification", repository.name, ticket.number, user.username); | ||
| } | ||
@@ -652,3 +651,3 @@ } | ||
| } | ||
| } catch (IOException e) { | ||
| } catch (UncheckedIOException e) { | ||
@@ -655,0 +654,0 @@ } finally { |
@@ -21,5 +21,9 @@ /* | ||
| import java.io.File; | ||
| import java.io.FileInputStream; | ||
| import java.io.InputStreamReader; | ||
| import java.security.KeyFactory; | ||
| import java.security.KeyPair; | ||
| import java.security.PrivateKey; | ||
| import java.security.PublicKey; | ||
| import java.util.Arrays; | ||
@@ -29,15 +33,21 @@ import java.util.Iterator; | ||
| import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder; | ||
| import org.apache.sshd.common.keyprovider.AbstractKeyPairProvider; | ||
| import org.apache.sshd.common.util.SecurityUtils; | ||
| import org.bouncycastle.openssl.PEMDecryptorProvider; | ||
| import org.bouncycastle.openssl.PEMEncryptedKeyPair; | ||
| import org.apache.sshd.common.util.security.SecurityUtils; | ||
| import org.bouncycastle.openssl.PEMKeyPair; | ||
| import org.bouncycastle.openssl.PEMParser; | ||
| import org.bouncycastle.openssl.PasswordFinder; | ||
| import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; | ||
| import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; | ||
| import org.bouncycastle.crypto.params.AsymmetricKeyParameter; | ||
| import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; | ||
| import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; | ||
| import org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil; | ||
| import org.bouncycastle.crypto.util.OpenSSHPublicKeyUtil; | ||
| import org.bouncycastle.jcajce.spec.OpenSSHPrivateKeySpec; | ||
| import org.bouncycastle.jcajce.spec.OpenSSHPublicKeySpec; | ||
| import org.bouncycastle.util.io.pem.PemObject; | ||
| import org.bouncycastle.util.io.pem.PemReader; | ||
| /** | ||
| * This host key provider loads private keys from the specified files. | ||
| * | ||
| * <p> | ||
| * Note that this class has a direct dependency on BouncyCastle and won't work | ||
@@ -48,52 +58,53 @@ * unless it has been correctly registered as a security provider. | ||
| */ | ||
| public class FileKeyPairProvider extends AbstractKeyPairProvider { | ||
| public class FileKeyPairProvider extends AbstractKeyPairProvider | ||
| { | ||
| private String[] files; | ||
| private PasswordFinder passwordFinder; | ||
| public FileKeyPairProvider() { | ||
| public FileKeyPairProvider() | ||
| { | ||
| } | ||
| public FileKeyPairProvider(String[] files) { | ||
| public FileKeyPairProvider(String[] files) | ||
| { | ||
| this.files = files; | ||
| } | ||
| public FileKeyPairProvider(String[] files, PasswordFinder passwordFinder) { | ||
| this.files = files; | ||
| this.passwordFinder = passwordFinder; | ||
| } | ||
| public String[] getFiles() { | ||
| public String[] getFiles() | ||
| { | ||
| return files; | ||
| } | ||
| public void setFiles(String[] files) { | ||
| public void setFiles(String[] files) | ||
| { | ||
| this.files = files; | ||
| } | ||
| public PasswordFinder getPasswordFinder() { | ||
| return passwordFinder; | ||
| } | ||
| public void setPasswordFinder(PasswordFinder passwordFinder) { | ||
| this.passwordFinder = passwordFinder; | ||
| } | ||
| public Iterable<KeyPair> loadKeys() { | ||
| @Override | ||
| public Iterable<KeyPair> loadKeys() | ||
| { | ||
| if (!SecurityUtils.isBouncyCastleRegistered()) { | ||
| throw new IllegalStateException("BouncyCastle must be registered as a JCE provider"); | ||
| } | ||
| return new Iterable<KeyPair>() { | ||
| return new Iterable<KeyPair>() | ||
| { | ||
| @Override | ||
| public Iterator<KeyPair> iterator() { | ||
| return new Iterator<KeyPair>() { | ||
| public Iterator<KeyPair> iterator() | ||
| { | ||
| return new Iterator<KeyPair>() | ||
| { | ||
| private final Iterator<String> iterator = Arrays.asList(files).iterator(); | ||
| private KeyPair nextKeyPair; | ||
| private boolean nextKeyPairSet = false; | ||
| @Override | ||
| public boolean hasNext() { | ||
| public boolean hasNext() | ||
| { | ||
| return nextKeyPairSet || setNextObject(); | ||
| } | ||
| @Override | ||
| public KeyPair next() { | ||
| public KeyPair next() | ||
| { | ||
| if (!nextKeyPairSet) { | ||
@@ -107,9 +118,18 @@ if (!setNextObject()) { | ||
| } | ||
| @Override | ||
| public void remove() { | ||
| public void remove() | ||
| { | ||
| throw new UnsupportedOperationException(); | ||
| } | ||
| private boolean setNextObject() { | ||
| private boolean setNextObject() | ||
| { | ||
| while (iterator.hasNext()) { | ||
| String file = iterator.next(); | ||
| File f = new File(file); | ||
| if (!f.isFile()) { | ||
| log.debug("File does not exist, skipping {}", file); | ||
| continue; | ||
| } | ||
| nextKeyPair = doLoadKey(file); | ||
@@ -129,6 +149,49 @@ if (nextKeyPair != null) { | ||
| protected KeyPair doLoadKey(String file) { | ||
| private KeyPair doLoadKey(String file) | ||
| { | ||
| try { | ||
| PEMParser r = new PEMParser(new InputStreamReader(new FileInputStream(file))); | ||
| try { | ||
| try (PemReader r = new PemReader(new InputStreamReader(new FileInputStream(file)))) { | ||
| PemObject pemObject = r.readPemObject(); | ||
| if ("OPENSSH PRIVATE KEY".equals(pemObject.getType())) { | ||
| // This reads a properly OpenSSH formatted ed25519 private key file. | ||
| // It is currently unused because the SSHD library in play doesn't work with proper keys. | ||
| // This is kept in the hope that in the future the library offers proper support. | ||
| try { | ||
| byte[] privateKeyContent = pemObject.getContent(); | ||
| AsymmetricKeyParameter privateKeyParameters = OpenSSHPrivateKeyUtil.parsePrivateKeyBlob(privateKeyContent); | ||
| if (privateKeyParameters instanceof Ed25519PrivateKeyParameters) { | ||
| OpenSSHPrivateKeySpec privkeySpec = new OpenSSHPrivateKeySpec(privateKeyContent); | ||
| Ed25519PublicKeyParameters publicKeyParameters = ((Ed25519PrivateKeyParameters)privateKeyParameters).generatePublicKey(); | ||
| OpenSSHPublicKeySpec pubKeySpec = new OpenSSHPublicKeySpec(OpenSSHPublicKeyUtil.encodePublicKey(publicKeyParameters)); | ||
| KeyFactory kf = KeyFactory.getInstance("Ed25519", "BC"); | ||
| PrivateKey privateKey = kf.generatePrivate(privkeySpec); | ||
| PublicKey publicKey = kf.generatePublic(pubKeySpec); | ||
| return new KeyPair(publicKey, privateKey); | ||
| } | ||
| else { | ||
| log.warn("OpenSSH format is only supported for Ed25519 key type. Unable to read key " + file); | ||
| } | ||
| } | ||
| catch (Exception e) { | ||
| log.warn("Unable to read key " + file, e); | ||
| } | ||
| return null; | ||
| } | ||
| if ("EDDSA PRIVATE KEY".equals(pemObject.getType())) { | ||
| // This reads the ed25519 key from a file format that we created in SshDaemon. | ||
| // The type EDDSA PRIVATE KEY was given by us and nothing official. | ||
| byte[] privateKeyContent = pemObject.getContent(); | ||
| PrivateKeyEntryDecoder<? extends PublicKey,? extends PrivateKey> decoder = SecurityUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder(); | ||
| PrivateKey privateKey = decoder.decodePrivateKey(null, privateKeyContent, 0, privateKeyContent.length); | ||
| PublicKey publicKey = SecurityUtils. recoverEDDSAPublicKey(privateKey); | ||
| return new KeyPair(publicKey, privateKey); | ||
| } | ||
| } | ||
| try (PEMParser r = new PEMParser(new InputStreamReader(new FileInputStream(file)))) { | ||
| Object o = r.readObject(); | ||
@@ -138,18 +201,16 @@ | ||
| pemConverter.setProvider("BC"); | ||
| if (passwordFinder != null && o instanceof PEMEncryptedKeyPair) { | ||
| JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder(); | ||
| PEMDecryptorProvider pemDecryptor = decryptorBuilder.build(passwordFinder.getPassword()); | ||
| o = pemConverter.getKeyPair(((PEMEncryptedKeyPair) o).decryptKeyPair(pemDecryptor)); | ||
| } | ||
| if (o instanceof PEMKeyPair) { | ||
| o = pemConverter.getKeyPair((PEMKeyPair)o); | ||
| return (KeyPair) o; | ||
| } else if (o instanceof KeyPair) { | ||
| return (KeyPair) o; | ||
| return (KeyPair)o; | ||
| } | ||
| } finally { | ||
| r.close(); | ||
| else if (o instanceof KeyPair) { | ||
| return (KeyPair)o; | ||
| } | ||
| else { | ||
| log.warn("Cannot read unsupported PEM object of type: " + o.getClass().getCanonicalName()); | ||
| } | ||
| } | ||
| } catch (Exception e) { | ||
| } | ||
| catch (Exception e) { | ||
| log.warn("Unable to read key " + file, e); | ||
@@ -156,0 +217,0 @@ } |
@@ -24,21 +24,21 @@ /* | ||
| @Override | ||
| public boolean canConnect(Type type, SshdSocketAddress address, Session session) { | ||
| return false; | ||
| } | ||
| @Override | ||
| public boolean canConnect(Type type, SshdSocketAddress address, Session session) { | ||
| return false; | ||
| } | ||
| @Override | ||
| public boolean canForwardAgent(Session session) { | ||
| return false; | ||
| } | ||
| @Override | ||
| public boolean canForwardAgent(Session session, String requestType) { | ||
| return false; | ||
| } | ||
| @Override | ||
| public boolean canForwardX11(Session session) { | ||
| return false; | ||
| } | ||
| @Override | ||
| public boolean canForwardX11(Session session, String requestType) { | ||
| return false; | ||
| } | ||
| @Override | ||
| public boolean canListen(SshdSocketAddress address, Session session) { | ||
| return false; | ||
| } | ||
| @Override | ||
| public boolean canListen(SshdSocketAddress address, Session session) { | ||
| return false; | ||
| } | ||
| } |
@@ -18,2 +18,3 @@ /* | ||
| import java.io.ByteArrayOutputStream; | ||
| import java.io.File; | ||
@@ -26,2 +27,3 @@ import java.io.FileOutputStream; | ||
| import java.security.KeyPairGenerator; | ||
| import java.security.PrivateKey; | ||
| import java.text.MessageFormat; | ||
@@ -31,9 +33,19 @@ import java.util.List; | ||
| import net.i2p.crypto.eddsa.EdDSAPrivateKey; | ||
| import org.apache.sshd.common.config.keys.KeyEntryResolver; | ||
| import org.apache.sshd.common.io.IoServiceFactoryFactory; | ||
| import org.apache.sshd.common.io.mina.MinaServiceFactoryFactory; | ||
| import org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory; | ||
| import org.apache.sshd.common.util.SecurityUtils; | ||
| import org.apache.sshd.common.util.security.SecurityUtils; | ||
| import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleSecurityProviderRegistrar; | ||
| import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar; | ||
| import org.apache.sshd.common.util.security.eddsa.OpenSSHEd25519PrivateKeyEntryDecoder; | ||
| import org.apache.sshd.server.SshServer; | ||
| import org.apache.sshd.server.auth.pubkey.CachingPublicKeyAuthenticator; | ||
| import org.bouncycastle.openssl.PEMWriter; | ||
| import org.bouncycastle.crypto.params.AsymmetricKeyParameter; | ||
| import org.bouncycastle.crypto.util.OpenSSHPrivateKeyUtil; | ||
| import org.bouncycastle.crypto.util.PrivateKeyFactory; | ||
| import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator; | ||
| import org.bouncycastle.util.io.pem.PemObject; | ||
| import org.bouncycastle.util.io.pem.PemWriter; | ||
| import org.eclipse.jgit.internal.JGitText; | ||
@@ -60,3 +72,3 @@ import org.slf4j.Logger; | ||
| private final Logger log = LoggerFactory.getLogger(SshDaemon.class); | ||
| private static final Logger log = LoggerFactory.getLogger(SshDaemon.class); | ||
@@ -99,6 +111,11 @@ private static final String AUTH_PUBLICKEY = "publickey"; | ||
| // Ensure that Bouncy Castle is our JCE provider | ||
| SecurityUtils.setRegisterBouncyCastle(true); | ||
| SecurityUtils.registerSecurityProvider(new BouncyCastleSecurityProviderRegistrar()); | ||
| if (SecurityUtils.isBouncyCastleRegistered()) { | ||
| log.debug("BouncyCastle is registered as a JCE provider"); | ||
| log.info("BouncyCastle is registered as a JCE provider"); | ||
| } | ||
| // Add support for ED25519_SHA512 | ||
| SecurityUtils.registerSecurityProvider(new EdDSASecurityProviderRegistrar()); | ||
| if (SecurityUtils.isProviderRegistered("EdDSA")) { | ||
| log.info("EdDSA is registered as a JCE provider"); | ||
| } | ||
@@ -108,6 +125,10 @@ // Generate host RSA and DSA keypairs and create the host keypair provider | ||
| File dsaKeyStore = new File(gitblit.getBaseFolder(), "ssh-dsa-hostkey.pem"); | ||
| File ecdsaKeyStore = new File(gitblit.getBaseFolder(), "ssh-ecdsa-hostkey.pem"); | ||
| File eddsaKeyStore = new File(gitblit.getBaseFolder(), "ssh-eddsa-hostkey.pem"); | ||
| File ed25519KeyStore = new File(gitblit.getBaseFolder(), "ssh-ed25519-hostkey.pem"); | ||
| generateKeyPair(rsaKeyStore, "RSA", 2048); | ||
| generateKeyPair(dsaKeyStore, "DSA", 0); | ||
| generateKeyPair(ecdsaKeyStore, "ECDSA", 256); | ||
| generateKeyPair(eddsaKeyStore, "EdDSA", 0); | ||
| FileKeyPairProvider hostKeyPairProvider = new FileKeyPairProvider(); | ||
| hostKeyPairProvider.setFiles(new String [] { rsaKeyStore.getPath(), dsaKeyStore.getPath(), dsaKeyStore.getPath() }); | ||
| hostKeyPairProvider.setFiles(new String [] { ecdsaKeyStore.getPath(), eddsaKeyStore.getPath(), ed25519KeyStore.getPath(), rsaKeyStore.getPath(), dsaKeyStore.getPath() }); | ||
@@ -169,3 +190,3 @@ | ||
| sshd.setFileSystemFactory(new DisabledFilesystemFactory()); | ||
| sshd.setTcpipForwardingFilter(new NonForwardingFilter()); | ||
| sshd.setForwardingFilter(new NonForwardingFilter()); | ||
| sshd.setCommandFactory(new SshCommandFactory(gitblit, workQueue)); | ||
@@ -247,3 +268,3 @@ sshd.setShellFactory(new WelcomeShell(gitblit)); | ||
| private void generateKeyPair(File file, String algorithm, int keySize) { | ||
| static void generateKeyPair(File file, String algorithm, int keySize) { | ||
| if (file.exists()) { | ||
@@ -271,4 +292,34 @@ return; | ||
| FileOutputStream os = new FileOutputStream(file); | ||
| PEMWriter w = new PEMWriter(new OutputStreamWriter(os)); | ||
| w.writeObject(kp); | ||
| PemWriter w = new PemWriter(new OutputStreamWriter(os)); | ||
| if (algorithm.equals("ED25519")) { | ||
| // This generates a proper OpenSSH formatted ed25519 private key file. | ||
| // It is currently unused because the SSHD library in play doesn't work with proper keys. | ||
| // This is kept in the hope that in the future the library offers proper support. | ||
| AsymmetricKeyParameter keyParam = PrivateKeyFactory.createKey(kp.getPrivate().getEncoded()); | ||
| byte[] encKey = OpenSSHPrivateKeyUtil.encodePrivateKey(keyParam); | ||
| w.writeObject(new PemObject("OPENSSH PRIVATE KEY", encKey)); | ||
| } | ||
| else if (algorithm.equals("EdDSA")) { | ||
| // This saves the ed25519 key in a file format that the current SSHD library can work with. | ||
| // We call it EDDSA PRIVATE KEY, but that string is given by us and nothing official. | ||
| PrivateKey privateKey = kp.getPrivate(); | ||
| if (privateKey instanceof EdDSAPrivateKey) { | ||
| OpenSSHEd25519PrivateKeyEntryDecoder encoder = (OpenSSHEd25519PrivateKeyEntryDecoder)SecurityUtils.getOpenSSHEDDSAPrivateKeyEntryDecoder(); | ||
| EdDSAPrivateKey dsaPrivateKey = (EdDSAPrivateKey)privateKey; | ||
| // Jumping through some hoops here, because the decoder expects the key type as a string at the | ||
| // start, but the encoder doesn't put it in. So we have to put it in ourselves. | ||
| ByteArrayOutputStream encos = new ByteArrayOutputStream(); | ||
| String type = encoder.encodePrivateKey(encos, dsaPrivateKey); | ||
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); | ||
| KeyEntryResolver.encodeString(bos, type); | ||
| encos.writeTo(bos); | ||
| w.writeObject(new PemObject("EDDSA PRIVATE KEY", bos.toByteArray())); | ||
| } | ||
| else { | ||
| log.warn("Unable to encode EdDSA key, got key type " + privateKey.getClass().getCanonicalName()); | ||
| } | ||
| } | ||
| else { | ||
| w.writeObject(new JcaMiscPEMGenerator(kp)); | ||
| } | ||
| w.flush(); | ||
@@ -278,5 +329,4 @@ w.close(); | ||
| log.warn(MessageFormat.format("Unable to generate {0} keypair", algorithm), e); | ||
| return; | ||
| } | ||
| } | ||
| } |
@@ -84,2 +84,11 @@ /* | ||
| public void detachPublicKey() | ||
| { | ||
| if (rawData == null) { | ||
| // Make sure the raw data is available | ||
| getRawData(); | ||
| } | ||
| publicKey = null; | ||
| } | ||
| public String getAlgorithm() { | ||
@@ -86,0 +95,0 @@ return getPublicKey().getAlgorithm(); |
@@ -57,6 +57,3 @@ /* | ||
| SshDaemonClient client = session.getAttribute(SshDaemonClient.KEY); | ||
| if (client.getUser() != null) { | ||
| log.info("{} has already authenticated!", identity); | ||
| return true; | ||
| } | ||
| String username = identity.toLowerCase(Locale.US); | ||
@@ -71,2 +68,3 @@ if (stripDomain) { | ||
| if (user != null) { | ||
| // TODO: Check if the user was set in the client and if it is the same as this user. Do not allow changing the user during the SSH auth process. | ||
| client.setUser(user); | ||
@@ -73,0 +71,0 @@ return true; |
@@ -48,6 +48,2 @@ /* | ||
| SshDaemonClient client = session.getAttribute(SshDaemonClient.KEY); | ||
| if (client.getUser() != null) { | ||
| log.info("{} has already authenticated!", username); | ||
| return true; | ||
| } | ||
@@ -57,2 +53,3 @@ username = username.toLowerCase(Locale.US); | ||
| if (user != null) { | ||
| // TODO: Check if the user was set in the client and if it is the same as this user. Do not allow changing the user during the SSH auth process. | ||
| client.setUser(user); | ||
@@ -59,0 +56,0 @@ return true; |
@@ -60,2 +60,7 @@ /* | ||
| @Override | ||
| public Command get() { | ||
| return create(); | ||
| } | ||
| private static class SendMessage implements Command, SessionAware { | ||
@@ -62,0 +67,0 @@ |
@@ -158,6 +158,6 @@ /* | ||
| } | ||
| } catch (IOException e) { | ||
| logger.error(MessageFormat.format("Bugtraq config for {0} is invalid!", repositoryName), e); | ||
| } catch (ConfigInvalidException e) { | ||
| logger.error(MessageFormat.format("Bugtraq config for {0} is invalid!", repositoryName), e); | ||
| logger.warn("Bugtraq config for {} is invalid!", repositoryName, e); | ||
| } catch (Exception e) { | ||
| logger.warn("Failed to parse message through Bugtraq.", e); | ||
| } | ||
@@ -164,0 +164,0 @@ |
@@ -21,3 +21,3 @@ /* | ||
| import org.jsoup.safety.Cleaner; | ||
| import org.jsoup.safety.Whitelist; | ||
| import org.jsoup.safety.Safelist; | ||
@@ -42,3 +42,3 @@ import com.google.inject.Inject; | ||
| public JSoupXssFilter() { | ||
| none = new Cleaner(Whitelist.none()); | ||
| none = new Cleaner(Safelist.none()); | ||
| relaxed = new Cleaner(getRelaxedWhiteList()); | ||
@@ -69,4 +69,4 @@ } | ||
| */ | ||
| protected Whitelist getRelaxedWhiteList() { | ||
| return new Whitelist() | ||
| protected Safelist getRelaxedWhiteList() { | ||
| return new Safelist() | ||
| .addTags( | ||
@@ -73,0 +73,0 @@ "a", "b", "blockquote", "br", "caption", "cite", "code", "col", |
@@ -108,2 +108,4 @@ /* | ||
| int i = 0; | ||
| int l = 0; | ||
| while (i < inStr.length()) { | ||
@@ -121,8 +123,14 @@ if (inStr.charAt(i) == '&') { | ||
| } else if (changeSpace && inStr.charAt(i) == '\t') { | ||
| for (int j = 0; j < tabLength; j++) { | ||
| for (int j = 0; j < tabLength - l; j++) { | ||
| retStr.append(" "); | ||
| } | ||
| l = -1; | ||
| } else { | ||
| retStr.append(inStr.charAt(i)); | ||
| } | ||
| l = (l + 1) % tabLength; | ||
| if (inStr.charAt(i) == '\n') { | ||
| l = 0; | ||
| } | ||
| i++; | ||
@@ -270,12 +278,39 @@ } | ||
| /** | ||
| * Calculates the SHA1 of the string. | ||
| * Calculates the hash sum of the byte array. | ||
| * | ||
| * @param bytes | ||
| * byte array to hash | ||
| * @param algorithm | ||
| * Message digest algorithm name, e.g MD5, SHA-1 or SHA-256. | ||
| * @return sha sum of the byte array | ||
| */ | ||
| private static String getDigest(byte[] bytes, String algorithm) | ||
| { | ||
| try { | ||
| MessageDigest md = MessageDigest.getInstance(algorithm); | ||
| md.update(bytes, 0, bytes.length); | ||
| byte[] digest = md.digest(); | ||
| return toHex(digest); | ||
| } catch (NoSuchAlgorithmException t) { | ||
| throw new RuntimeException(t); | ||
| } | ||
| } | ||
| /** | ||
| * Calculates the hash of the string. | ||
| * | ||
| * @param text | ||
| * string to hash | ||
| * @param algorithm | ||
| * Message digest algorithm name, e.g MD5, SHA-1 or SHA-256. | ||
| * @return sha1 of the string | ||
| */ | ||
| public static String getSHA1(String text) { | ||
| private static String getDigest(String text, String algorithm) | ||
| { | ||
| try { | ||
| byte[] bytes = text.getBytes("iso-8859-1"); | ||
| return getSHA1(bytes); | ||
| return getDigest(bytes, algorithm); | ||
| } catch (UnsupportedEncodingException u) { | ||
@@ -287,2 +322,13 @@ throw new RuntimeException(u); | ||
| /** | ||
| * Calculates the SHA1 of the string. | ||
| * | ||
| * @param text | ||
| * @return sha1 of the string | ||
| */ | ||
| public static String getSHA1(String text) | ||
| { | ||
| return getDigest(text, "SHA-1"); | ||
| } | ||
| /** | ||
| * Calculates the SHA1 of the byte array. | ||
@@ -293,43 +339,50 @@ * | ||
| */ | ||
| public static String getSHA1(byte[] bytes) { | ||
| try { | ||
| MessageDigest md = MessageDigest.getInstance("SHA-1"); | ||
| md.update(bytes, 0, bytes.length); | ||
| byte[] digest = md.digest(); | ||
| return toHex(digest); | ||
| } catch (NoSuchAlgorithmException t) { | ||
| throw new RuntimeException(t); | ||
| } | ||
| public static String getSHA1(byte[] bytes) | ||
| { | ||
| return getDigest(bytes, "SHA-1"); | ||
| } | ||
| /** | ||
| * Calculates the SHA256 of the string. | ||
| * | ||
| * @param text | ||
| * @return sha256 of the string | ||
| */ | ||
| public static String getSHA256(String text) | ||
| { | ||
| return getDigest(text, "SHA-256"); | ||
| } | ||
| /** | ||
| * Calculates the SHA256 of the byte array. | ||
| * | ||
| * @param bytes | ||
| * @return sha256 of the byte array | ||
| */ | ||
| public static String getSHA256(byte[] bytes) | ||
| { | ||
| return getDigest(bytes, "SHA-256"); | ||
| } | ||
| /** | ||
| * Calculates the MD5 of the string. | ||
| * | ||
| * @param string | ||
| * @param text | ||
| * @return md5 of the string | ||
| */ | ||
| public static String getMD5(String string) { | ||
| try { | ||
| return getMD5(string.getBytes("iso-8859-1")); | ||
| } catch (UnsupportedEncodingException u) { | ||
| throw new RuntimeException(u); | ||
| } | ||
| public static String getMD5(String text) | ||
| { | ||
| return getDigest(text, "MD5"); | ||
| } | ||
| /** | ||
| * Calculates the MD5 of the string. | ||
| * Calculates the MD5 of the byte array. | ||
| * | ||
| * @param string | ||
| * @param bytes | ||
| * @return md5 of the string | ||
| */ | ||
| public static String getMD5(byte [] bytes) { | ||
| try { | ||
| MessageDigest md = MessageDigest.getInstance("MD5"); | ||
| md.reset(); | ||
| md.update(bytes); | ||
| byte[] digest = md.digest(); | ||
| return toHex(digest); | ||
| } catch (NoSuchAlgorithmException t) { | ||
| throw new RuntimeException(t); | ||
| } | ||
| public static String getMD5(byte [] bytes) | ||
| { | ||
| return getDigest(bytes, "MD5"); | ||
| } | ||
@@ -336,0 +389,0 @@ |
@@ -75,3 +75,3 @@ /* | ||
| import org.bouncycastle.asn1.x509.KeyUsage; | ||
| import org.bouncycastle.asn1.x509.X509Extension; | ||
| import org.bouncycastle.asn1.x509.Extension; | ||
| import org.bouncycastle.cert.X509CRLHolder; | ||
@@ -86,3 +86,2 @@ import org.bouncycastle.cert.X509v2CRLBuilder; | ||
| import org.bouncycastle.openssl.PEMEncryptor; | ||
| import org.bouncycastle.openssl.PEMWriter; | ||
| import org.bouncycastle.openssl.jcajce.JcaPEMWriter; | ||
@@ -450,5 +449,5 @@ import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder; | ||
| // PEM encoded X509 | ||
| PEMWriter pemWriter = null; | ||
| JcaPEMWriter pemWriter = null; | ||
| try { | ||
| pemWriter = new PEMWriter(new FileWriter(tmpFile)); | ||
| pemWriter = new JcaPEMWriter(new FileWriter(tmpFile)); | ||
| pemWriter.writeObject(cert); | ||
@@ -566,5 +565,5 @@ pemWriter.flush(); | ||
| JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); | ||
| certBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pair.getPublic())); | ||
| certBuilder.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false)); | ||
| certBuilder.addExtension(X509Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert.getPublicKey())); | ||
| certBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pair.getPublic())); | ||
| certBuilder.addExtension(Extension.basicConstraints, false, new BasicConstraints(false)); | ||
| certBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert.getPublicKey())); | ||
@@ -578,3 +577,3 @@ // support alternateSubjectNames for SSL certificates | ||
| GeneralNames subjectAltName = new GeneralNames(altNames.toArray(new GeneralName [altNames.size()])); | ||
| certBuilder.addExtension(X509Extension.subjectAlternativeName, false, subjectAltName); | ||
| certBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltName); | ||
| } | ||
@@ -637,6 +636,6 @@ | ||
| JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); | ||
| caBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(caPair.getPublic())); | ||
| caBuilder.addExtension(X509Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caPair.getPublic())); | ||
| caBuilder.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(true)); | ||
| caBuilder.addExtension(X509Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); | ||
| caBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(caPair.getPublic())); | ||
| caBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caPair.getPublic())); | ||
| caBuilder.addExtension(Extension.basicConstraints, false, new BasicConstraints(true)); | ||
| caBuilder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); | ||
@@ -871,10 +870,10 @@ JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC); | ||
| JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); | ||
| certBuilder.addExtension(X509Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pair.getPublic())); | ||
| certBuilder.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false)); | ||
| certBuilder.addExtension(X509Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert.getPublicKey())); | ||
| certBuilder.addExtension(X509Extension.keyUsage, true, new KeyUsage(KeyUsage.keyEncipherment | KeyUsage.digitalSignature)); | ||
| certBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pair.getPublic())); | ||
| certBuilder.addExtension(Extension.basicConstraints, false, new BasicConstraints(false)); | ||
| certBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert.getPublicKey())); | ||
| certBuilder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyEncipherment | KeyUsage.digitalSignature)); | ||
| if (!StringUtils.isEmpty(clientMetadata.emailAddress)) { | ||
| GeneralNames subjectAltName = new GeneralNames( | ||
| new GeneralName(GeneralName.rfc822Name, clientMetadata.emailAddress)); | ||
| certBuilder.addExtension(X509Extension.subjectAlternativeName, false, subjectAltName); | ||
| certBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltName); | ||
| } | ||
@@ -881,0 +880,0 @@ |
@@ -251,3 +251,3 @@ gb.repository = Repository | ||
| gb.usernameUnavailable = Benutzername ''{0}'' ist nicht verf\u00fcgbar. | ||
| gb.combinedMd5Rename = Gitblit ist f\u00fcr kombiniertes MD5-Passwort-Hashing konfiguriert. Sie m\u00fcssen beim Umbenennen des Kontos ein neues Passwort angeben. | ||
| gb.combinedMd5Rename = Dieser Benutzer ist f\u00fcr kombiniertes MD5-Passwort-Hashing konfiguriert. Sie m\u00fcssen beim Umbenennen des Kontos ein neues Passwort angeben. | ||
| gb.userCreated = Neuer Benutzer ''{0}'' erfolgreich angelegt. | ||
@@ -739,2 +739,4 @@ gb.couldNotFindFederationRegistration = Konnte Verbindungsregistrierung (Federation) nicht finden! | ||
| gb.addSshKey = SSH Key hinzuf\u00fcgen | ||
| gb.addSshKeyErrorEmpty = SSH Public Key leer. Bitte geben Sie einen g\u00fcltigen SSH Public Key an | ||
| gb.addSshKeyErrorFormat = SSH Public Key Format ung�ltig. Bitte geben Sie einen g\u00fcltigen SSH Public Key an | ||
| gb.key = Key | ||
@@ -741,0 +743,0 @@ gb.sshKeyComment = Kommentar |
@@ -251,3 +251,3 @@ gb.repository = Repositorio | ||
| gb.usernameUnavailable = El usuario ''{0}'' no est\u00E1 disponible. | ||
| gb.combinedMd5Rename = GitBlit est\u00E1 configurado para Hashes combinados md5. Debes introducir una nueva contrase\u00F1a para renombrar la cuenta. | ||
| gb.combinedMd5Rename = El usuario est\u00E1 configurado para Hashes combinados md5. Debes introducir una nueva contrase\u00F1a para renombrar la cuenta. | ||
| gb.userCreated = Nuevo usuario ''{0}'' creado satisfactoriamente. | ||
@@ -254,0 +254,0 @@ gb.couldNotFindFederationRegistration = \u00A1No se pudo encontrar el registro de federaci\u00F3n! |
@@ -251,3 +251,3 @@ gb.repository = d\u00e9p\u00f4t | ||
| gb.usernameUnavailable = L'identifiant ''{0}'' est indisponible. | ||
| gb.combinedMd5Rename = Gitblit est configur\u00e9 pour des mots de passe hash\u00e9s combined-md5. Vous devez entrer un nouveau mot de passe pour ce compte. | ||
| gb.combinedMd5Rename = L'identifiant est configur\u00e9 pour des mots de passe hash\u00e9s combined-md5. Vous devez entrer un nouveau mot de passe pour ce compte. | ||
| gb.userCreated = Le nouveau utilisateur ''{0}'' est cr\u00e9\u00e9 avec succ\u00e8s. | ||
@@ -254,0 +254,0 @@ gb.couldNotFindFederationRegistration = N'arrive pas \u00e0 joindre l'enregistrement de la f\u00e9d\u00e9ration ! |
@@ -251,3 +251,3 @@ gb.repository = repository | ||
| gb.usernameUnavailable = Il nome utente ''{0}'' non � disponibile. | ||
| gb.combinedMd5Rename = Gitblit � configurato per effettuare un hashing delle password di tipo combinato-md5. E' quindi necessario specificare una nuova password quando si rinomina un utenza. | ||
| gb.combinedMd5Rename = Il nome utente � configurato per effettuare un hashing delle password di tipo combinato-md5. E' quindi necessario specificare una nuova password quando si rinomina un utenza. | ||
| gb.userCreated = Nuovo utente ''{0}'' creato con successo. | ||
@@ -254,0 +254,0 @@ gb.couldNotFindFederationRegistration = Impossibile trovare la registrazione di federazione! |
@@ -251,3 +251,3 @@ gb.repository = repositorie | ||
| gb.usernameUnavailable = Gebruikersnaam ''{0}'' is niet beschikbaar. | ||
| gb.combinedMd5Rename = Gitblit is geconfigureerd voor combined-md5 wachtwoord hashing. U moet een nieuw wachtwoord opgeven bij het hernoemen van een account. | ||
| gb.combinedMd5Rename = Gebruikersnaam is geconfigureerd voor combined-md5 wachtwoord hashing. U moet een nieuw wachtwoord opgeven bij het hernoemen van een account. | ||
| gb.userCreated = Nieuwe gebruiker ''{0}'' succesvol aangemaakt. | ||
@@ -254,0 +254,0 @@ gb.couldNotFindFederationRegistration = Kon de federatie registratie niet vinden! |
@@ -251,3 +251,3 @@ gb.repository = repository | ||
| gb.usernameUnavailable = Brukernavnet ''{0}'' er ikke tilgjengelig. | ||
| gb.combinedMd5Rename = Gitblit er satt opp med combined-md5 passord hashing. Du m\u00e5 angi et nytt passord n\u00e5r du gir en konto et nytt navn. | ||
| gb.combinedMd5Rename = Brukernavnet er satt opp med combined-md5 passord hashing. Du m\u00e5 angi et nytt passord n\u00e5r du gir en konto et nytt navn. | ||
| gb.userCreated = Ny bruker ''{0}'' opprettet. | ||
@@ -254,0 +254,0 @@ gb.couldNotFindFederationRegistration = Kunne ikke finne federeringsoppf\u00F8ringen! |
@@ -249,4 +249,4 @@ gb.repository = Repozytorium | ||
| gb.pleaseSetUsername = Wpisz nazw\u0119 u\u017Cytkownika! | ||
| gb.usernameUnavailable = Nazwa u\u017Cytkownika''{0}'' jest niedost\u0119pna. | ||
| gb.combinedMd5Rename = Gitblit jest skonfigurowany na po\u0142\u0105czone haszowanie hase\u0142 md5. Musisz wpisa\u0107 nowe has\u0142o przy zmianie nazwy konta. | ||
| gb.usernameUnavailable = Nazwa u\u017Cytkownika ''{0}'' jest niedost\u0119pna. | ||
| gb.combinedMd5Rename = Nazwa u\u017Cytkownika jest skonfigurowany na po\u0142\u0105czone haszowanie hase\u0142 md5. Musisz wpisa\u0107 nowe has\u0142o przy zmianie nazwy konta. | ||
| gb.userCreated = U\u017Cytkownik ''{0}'' zosta\u0142 utworzony. | ||
@@ -253,0 +253,0 @@ gb.couldNotFindFederationRegistration = Nie mo\u017Cna znale\u017A\u0107 rejestracji federacji! |
@@ -250,3 +250,3 @@ gb.repository = reposit\u00f3rio | ||
| gb.usernameUnavailable = Username ''{0}'' est\u00e1 indispon\u00edvel. | ||
| gb.combinedMd5Rename = Gitblit est\u00e1 configurado para usar um hash combinado-md5. Voc\u00ea deve inserir um novo password ao renamear a conta. | ||
| gb.combinedMd5Rename = Username est\u00e1 configurado para usar um hash combinado-md5. Voc\u00ea deve inserir um novo password ao renamear a conta. | ||
| gb.userCreated = Novo usu\u00e1rio ''{0}'' criado com sucesso. | ||
@@ -253,0 +253,0 @@ gb.couldNotFindFederationRegistration = N\u00e3o foi poss\u00edvel localizar o registro da federa\u00e7\u00e3o! |
@@ -322,21 +322,21 @@ gb.repository = \u7248\u672c\u5e93 | ||
| gb.copyToClipboard = \u590d\u5236\u5230\u526a\u8d34\u677f | ||
| gb.fork = \u5efa\u7acb\u5206\u652f | ||
| gb.forks = \u5206\u652f | ||
| gb.forkRepository = \u5efa\u7acb {0} \u7684\u5206\u652f\uff1f | ||
| gb.repositoryForked = {0} \u5df2\u5efa\u7acb\u5206\u652f | ||
| gb.repositoryForkFailed = \u5efa\u7acb\u5206\u652f\u5931\u8d25 | ||
| gb.fork = \u5efa\u7acb\u590d\u523b | ||
| gb.forks = \u590d\u523b | ||
| gb.forkRepository = \u5efa\u7acb {0} \u7684\u590d\u523b\uff1f | ||
| gb.repositoryForked = {0} \u5df2\u5efa\u7acb\u590d\u523b | ||
| gb.repositoryForkFailed = \u5efa\u7acb\u590d\u523b\u5931\u8d25 | ||
| gb.personalRepositories = \u79c1\u6709\u7248\u672c\u5e93 | ||
| gb.allowForks = \u5141\u8bb8\u5efa\u7acb\u5206\u652f | ||
| gb.allowForksDescription = \u5141\u8bb8\u6388\u6743\u7684\u7528\u6237\u5efa\u7acb\u6b64\u7248\u672c\u5e93\u7684\u5206\u652f | ||
| gb.forkedFrom = \u5206\u652f\u81ea | ||
| gb.canFork = \u5141\u8bb8\u5206\u652f | ||
| gb.canForkDescription = \u5141\u8bb8\u5206\u652f\u7248\u672c\u5e93\u5e76\u590d\u5236\u5230\u79c1\u6709\u7248\u672c\u5e93\u4e2d | ||
| gb.myFork = \u67e5\u770b\u6211\u7684\u5206\u652f | ||
| gb.forksProhibited = \u7981\u6b62\u5efa\u7acb\u5206\u652f | ||
| gb.forksProhibitedWarning = \u5f53\u524d\u7248\u672c\u5e93\u7981\u6b62\u5efa\u7acb\u5206\u652f | ||
| gb.noForks = {0} \u6ca1\u6709\u5206\u652f | ||
| gb.forkNotAuthorized = \u62b1\u6b49\uff0c\u60a8\u65e0\u6743\u5206\u652f {0} | ||
| gb.allowForks = \u5141\u8bb8\u5efa\u7acb\u590d\u523b | ||
| gb.allowForksDescription = \u5141\u8bb8\u6388\u6743\u7684\u7528\u6237\u5efa\u7acb\u6b64\u7248\u672c\u5e93\u7684\u590d\u523b | ||
| gb.forkedFrom = \u590d\u523b\u81ea | ||
| gb.canFork = \u5141\u8bb8\u590d\u523b | ||
| gb.canForkDescription = \u5141\u8bb8\u590d\u523b\u7248\u672c\u5e93\u5e76\u590d\u5236\u5230\u79c1\u6709\u7248\u672c\u5e93\u4e2d | ||
| gb.myFork = \u67e5\u770b\u6211\u7684\u590d\u523b | ||
| gb.forksProhibited = \u7981\u6b62\u5efa\u7acb\u590d\u523b | ||
| gb.forksProhibitedWarning = \u5f53\u524d\u7248\u672c\u5e93\u7981\u6b62\u5efa\u7acb\u590d\u523b | ||
| gb.noForks = {0} \u6ca1\u6709\u590d\u523b | ||
| gb.forkNotAuthorized = \u62b1\u6b49\uff0c\u60a8\u65e0\u6743\u590d\u523b {0} | ||
| gb.forkInProgress = \u6b63\u5728\u590d\u5236 | ||
| gb.preparingFork = \u6b63\u5728\u4e3a\u60a8\u51c6\u5907\u5206\u652f... | ||
| gb.isFork = \u5df2\u5efa\u7acb\u5206\u652f | ||
| gb.preparingFork = \u6b63\u5728\u4e3a\u60a8\u51c6\u5907\u590d\u523b... | ||
| gb.isFork = \u5df2\u5efa\u7acb\u590d\u523b | ||
| gb.canCreate = \u5141\u8bb8\u521b\u5efa | ||
@@ -343,0 +343,0 @@ gb.canCreateDescription = \u5141\u8bb8\u521b\u5efa\u79c1\u6709\u7248\u672c\u5e93 |
@@ -251,3 +251,3 @@ gb.repository = repository | ||
| gb.usernameUnavailable = Username ''{0}'' is unavailable. | ||
| gb.combinedMd5Rename = Gitblit is configured for combined-md5 password hashing. You must enter a new password on account rename. | ||
| gb.combinedMd5Rename = This user is configured for combined-md5 password hashing. You must enter a new password on account rename. | ||
| gb.userCreated = New user ''{0}'' successfully created. | ||
@@ -742,2 +742,4 @@ gb.couldNotFindFederationRegistration = Could not find federation registration! | ||
| gb.addSshKey = Add SSH Key | ||
| gb.addSshKeyErrorEmpty = SSH public key empty. Please provide a valid SSH public key | ||
| gb.addSshKeyErrorFormat = Not a valid SSH public key format. Please provide a valid SSH public key | ||
| gb.key = Key | ||
@@ -744,0 +746,0 @@ gb.sshKeyComment = Comment |
@@ -20,2 +20,3 @@ /* | ||
| import java.io.InputStream; | ||
| import java.io.UncheckedIOException; | ||
| import java.text.MessageFormat; | ||
@@ -499,3 +500,3 @@ import java.util.ArrayList; | ||
| } | ||
| } catch (IOException e) { | ||
| } catch (UncheckedIOException e) { | ||
@@ -502,0 +503,0 @@ } finally { |
@@ -96,4 +96,7 @@ /* | ||
| final Model<String> confirmPassword = new Model<String>( | ||
| StringUtils.isEmpty(userModel.password) ? "" : userModel.password); | ||
| final Model<String> confirmPassword = new Model<String>(""); | ||
| // Saving current password of user and clearing the one in the model so that it doesn't show up in the page. | ||
| final String oldPassword = userModel.password; | ||
| userModel.password = ""; | ||
| CompoundPropertyModel<UserModel> model = new CompoundPropertyModel<UserModel>(userModel); | ||
@@ -153,9 +156,11 @@ | ||
| if (app().authentication().supportsCredentialChanges(userModel)) { | ||
| if (!userModel.password.equals(confirmPassword.getObject())) { | ||
| error(getString("gb.passwordsDoNotMatch")); | ||
| return; | ||
| } | ||
| String password = userModel.password; | ||
| if (!PasswordHash.isHashedEntry(password)) { | ||
| // This is a plain text password. | ||
| if (!StringUtils.isEmpty(userModel.password)) { | ||
| // The password was changed | ||
| String password = userModel.password; | ||
| if (!password.equals(confirmPassword.getObject())) { | ||
| error(getString("gb.passwordsDoNotMatch")); | ||
| return; | ||
| } | ||
| // Check length. | ||
@@ -175,3 +180,3 @@ int minLength = app().settings().getInteger(Keys.realm.minPasswordLength, 5); | ||
| // Optionally store the password MD5 digest. | ||
| // Optionally store the password hash digest. | ||
| String type = app().settings().getString(Keys.realm.passwordStorage, PasswordHash.getDefaultType().name()); | ||
@@ -182,6 +187,9 @@ PasswordHash pwdh = PasswordHash.instanceOf(type); | ||
| } | ||
| } else if (rename | ||
| && password.toUpperCase().startsWith(PasswordHash.Type.CMD5.name())) { | ||
| error(getString("gb.combinedMd5Rename")); | ||
| return; | ||
| } else { | ||
| if (rename && oldPassword.toUpperCase().startsWith(PasswordHash.Type.CMD5.name())) { | ||
| error(getString("gb.combinedMd5Rename")); | ||
| return; | ||
| } | ||
| // Set back saved password so that it is kept in the DB. | ||
| userModel.password = oldPassword; | ||
| } | ||
@@ -258,2 +266,3 @@ } | ||
| passwordField.setResetPassword(false); | ||
| passwordField.setRequired(false); | ||
| form.add(passwordField.setEnabled(editCredentials)); | ||
@@ -263,2 +272,3 @@ NonTrimmedPasswordTextField confirmPasswordField = new NonTrimmedPasswordTextField("confirmPassword", | ||
| confirmPasswordField.setResetPassword(false); | ||
| confirmPasswordField.setRequired(false); | ||
| form.add(confirmPasswordField.setEnabled(editCredentials)); | ||
@@ -265,0 +275,0 @@ form.add(new TextField<String>("displayName").setEnabled(editDisplayName)); |
@@ -38,3 +38,2 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>oficiální, z příkazové řádky</td></tr> | ||
| <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Integrace do Průzkumníka Windows (vyžaduje oficiální řádkový Git)</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git pro Eclipse IDE (založený na JGit, jako Gitblit)</td></tr> | ||
@@ -50,2 +49,3 @@ <tr><td><a href="http://gitextensions.github.io">Git Extensions</a></td><td>C# frontend pro Git, který obsahuje integraci do Průzkumníka Windows a do Visual Studia</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>Na Javě založený klient pro Git and Mercurial pro Windows, Mac a Linux</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>Volný Git klient pro Windows a Mac</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Volný Git a Mercurial klient pro Windows a Mac</td></tr> | ||
@@ -52,0 +52,0 @@ <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>Mac OS X Git klient</td></tr> |
@@ -38,3 +38,2 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>der offizielle Kommandozeilen-Git-Client</td></tr> | ||
| <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Windows Datei Explorer Integration (erfordert den offiziellen Kommandozeilen-Client)</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git für die Eclipse IDE (basiert auf JGit, ebenso wie Gitblit)</td></tr> | ||
@@ -50,2 +49,3 @@ <tr><td><a href="http://gitextensions.github.io">Git Extensions</a></td><td>C# Frontend für Git mit Windows Explorer und Visual Studio Integration</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>Ein Java Git und Mercurial Client für Windows, Mac und Linux</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>Ein freier Git Client für Windows und Mac</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Ein freier Git und Mercurial Client für Windows und Mac</td></tr> | ||
@@ -52,0 +52,0 @@ <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>Ein Mac OS X Git Client</td></tr> |
@@ -40,3 +40,2 @@ | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>El Git oficial en línea de comandos</td></tr> | ||
| <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Explorador de archivos integrado en Windows (necesita Git oficial en línea de comandos)</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git para el IDE de Eclipse (basado en JGit, como Gitblit)</td></tr> | ||
@@ -52,3 +51,4 @@ <tr><td><a href="http://gitextensions.github.io">Git Extensions</a></td><td>Interfaz de usuario gráfico Git en C# con integración en IE y en Visual Studio</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>aplicación Java (necesita Git oficial en línea de comandos)</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Un cliente Git gratuito para Mac, Mercurial, y SVN</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>Un cliente Git gratuito para Mac y Windows</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Un cliente Git y Mercurial gratuito para Mac y Windows</td></tr> | ||
| <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>Cliente Git para Mac OS X</td></tr> | ||
@@ -55,0 +55,0 @@ </tbody> |
@@ -38,3 +38,2 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>la versione ufficiale di Git, da riga di comando</td></tr> | ||
| <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Integrazione per Windows Explorer (richiede la versione ufficiale di Git da riga di comando)</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git per ambienti di sviluppo basati su Eclipse (basato su JGit, come Gitblit)</td></tr> | ||
@@ -50,3 +49,4 @@ <tr><td><a href="http://gitextensions.github.io">Git Extensions</a></td><td>applicazione C# che integra Git in Windows Explorer e Visual Studio</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>Un client Git e Mercurial scritto in Java per Windows, Mac, and Linux</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Un client Git e Mercurial gratuito per Windows & Mac</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>Un client Git gratuito per Windows & Mac</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Un client Git e Mercurial gratuito per Windows & Mac</td></tr> | ||
| <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>Un client Git per Mac OS X</td></tr> | ||
@@ -53,0 +53,0 @@ </tbody> |
@@ -38,3 +38,2 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>本家コマンドライン版 Git</td></tr> | ||
| <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Windows エクスプローラ統合型 GUI (要 本家コマンドライン版 Git)</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>エクリプス IDE 向け Git (Gitblit に似た JGit 使用 )</td></tr> | ||
@@ -50,2 +49,3 @@ <tr><td><a href="http://gitextensions.github.io">Git Extensions</a></td><td>Windows エクスプローラとVisual Studio に統合された、Git の C# 製 UI</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>Windows, Mac, Linux 向け、Java製 Git & Mercurial クライアント</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>フリーの Windows, Mac 向け Git クライアント</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>フリーの Windows, Mac 向け Git & Mercurial クライアント</td></tr> | ||
@@ -52,0 +52,0 @@ <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>Mac OS X 向け Git クライアント</td></tr> |
@@ -39,3 +39,2 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>명령어 기반 공식 Git</td></tr> | ||
| <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>윈도의 파일 탐색기에 통합된 UI 클라이언트 (명령어 기반 공식 Git 필요)</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>이클립스 IDE 플러그인 (Gitblit 과 같은 JGit 기반)</td></tr> | ||
@@ -51,2 +50,3 @@ <tr><td><a href="http://gitextensions.github.io">Git Extensions</a></td><td>윈도 탐색기와 비주얼스튜디어를 위한 C#으로 개발된 기능</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>자바 어플리케이션 (명령어 기반 공식 Git 필요)</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>윈도와 맥에서 사용 가능한 Git 용 무료 클라이언트</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>윈도와 맥에서 사용 가능한 Git 과 Mercurial용 무료 클라이언트</td></tr> | ||
@@ -53,0 +53,0 @@ <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>맥 OS X 용 Git 클라이언트</td></tr> |
@@ -38,3 +38,2 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>de officiele, command-line Git</td></tr> | ||
| <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Windows bestandsverkenner integratie (officiele command-line Git is wel nodig)</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git voor de Eclipse IDE (gebaseerd op JGit, zoals Gitblit)</td></tr> | ||
@@ -50,3 +49,4 @@ <tr><td><a href="http://gitextensions.github.io">Git Extensions</a></td><td>C# frontend voor Git met Windows Explorer en Visual Studio integratie</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>Een Java Git, Mercurial, en SVN client applicatie (officiele command-line Git is wel nodig)</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Een gratis Mac Client voor Git en Mercurial</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>Een gratis Mac en Windows Client voor Git</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Een gratis Mac en Windows Client voor Git en Mercurial</td></tr> | ||
| <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>een Mac OS X Git client</td></tr> | ||
@@ -53,0 +53,0 @@ </tbody> |
@@ -37,4 +37,3 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <tbody> | ||
| <tr><td>a href="http://git-scm.com">Git</a> - den offisielle, kommando-linje git</td></tr> | ||
| <tr><td>a href="http://tortoisegit.googlecode.com">TortoiseGit</a> - Windows filutforsker integrasjon (krever den offisielle kommando-linje git versjonen installert</td></tr> | ||
| <tr><td><a href="http://git-scm.com">Git</a> - den offisielle, kommando-linje git</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a> - Git for Eclipse IDE (basert p\u00e5 JGit, akkurat som Gitblit er)</tr> | ||
@@ -50,4 +49,5 @@ <tr><td><a href="http://gitextensions.github.io">Git Extensions</a> - En C# frontend for Git som integrerer med filutforskeren og Visual Studio.</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a> - En Git og Mercurial klient for Windows, Mac, og Linux</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a> - En gratis Git klient for Windows og Mac</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a> - En gratis Git og Mercurial klient for Windows og Mac</td></tr> | ||
| <tr><td>a href="http://www.git-tower.com/">Tower</a> - En git klient for Mac OS X </td></tr> | ||
| <tr><td><a href="http://www.git-tower.com/">Tower</a> - En git klient for Mac OS X </td></tr> | ||
| </tbody> | ||
@@ -54,0 +54,0 @@ </table> |
@@ -40,3 +40,2 @@ | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>Oficjalny klient, dostępny przez linię poleceń</td></tr> | ||
| <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Rozszerzenie eksploratora Windows (wymaga oficjalnego, dostępnego przez linię poleceń klienta)</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>GIT dla edytora Eclipse (oparty o JGit, podobnie jak Gitblit)</td></tr> | ||
@@ -52,3 +51,4 @@ <tr><td><a href="http://gitextensions.github.io">Git Extensions</a></td><td>napisana w C# fasada na GIT, udostępniająca integrację dla Windows Explorer oraz Visual Studio</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>aplikacja napisana w Javie (wymaga oficjalnego, dostępnego przez linię poleceń klienta)</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>darmowy klient GIT, Mercurial i SVN na Mac OS X</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>darmowy klient GIT na Mac OS X i Windows</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>darmowy klient GIT i Mercurial na Mac OS X i Windows</td></tr> | ||
| <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>klient GIT na Mac OS X</td></tr> | ||
@@ -55,0 +55,0 @@ </tbody> |
@@ -38,3 +38,2 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>o Git oficial através de linhas de comando</td></tr> | ||
| <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Faz integração do Explorer do Windows com o Git (por isso requer o Git Oficial)</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git para a IDE Eclipse (baseada no JGit, como o Gitblit)</td></tr> | ||
@@ -50,3 +49,4 @@ <tr><td><a href="http://gitextensions.github.io">Git Extensions</a></td><td>Interface (em C#) para o Git cuja a característica é a integração com o Windows Explorer e o Visual Studio</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>Aplicação Client (em Java) para Git e Mercurial (por isso requer o Git Oficial)</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Client gratuito para o Mac que suporta Git e Mercurial</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>Client gratuito para o Mac e Windows que suporta Git</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>Client gratuito para o Mac e Windows que suporta Git e Mercurial</td></tr> | ||
| <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>um Cliente do Git para Mac OS X</td></tr> | ||
@@ -53,0 +53,0 @@ </tbody> |
@@ -38,3 +38,2 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>the official, command-line Git</td></tr> | ||
| <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Windows file explorer integration (requires official, command-line Git)</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git for the Eclipse IDE (based on JGit, like Gitblit)</td></tr> | ||
@@ -50,3 +49,4 @@ <tr><td><a href="hhttp://gitextensions.github.io">Git Extensions</a></td><td>C# frontend for Git that features Windows Explorer and Visual Studio integration</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>A Java Git and Mercurial client for Windows, Mac, and Linux</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>A free Git and Mercurial client for Windows & Mac</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>A free Git client for Windows & Mac</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>A free Git and Mercurial client for Windows & Mac</td></tr> | ||
| <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>a Mac OS X Git client</td></tr> | ||
@@ -53,0 +53,0 @@ </tbody> |
@@ -39,3 +39,2 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>官方, 命令行版本 Git</td></tr> | ||
| <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>与 Windows 资源管理器集成 (需要官方, 命令行 Git 的支持)</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git for the Eclipse IDE (基于 JGit, 类似 Gitblit)</td></tr> | ||
@@ -51,3 +50,4 @@ <tr><td><a href="http://gitextensions.github.io">Git Extensions</a></td><td>C# 版本的 Git 前端,与 Windows 资源管理器和 Visual Studio 集成</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>Java 版本的支持 Git, Mercurial 和 SVN 客户端应用 </td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>免费的 Mac Git Mercurial 以及 SVN 客户端 and Mercurial</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>免费的 Mac 以及 Windows Git 客户端</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>免费的 Mac 以及 Windows Git 以及 Mercurial 客户端</td></tr> | ||
| <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>Mac OS X Git 客户端</td></tr> | ||
@@ -54,0 +54,0 @@ </tbody> |
@@ -38,3 +38,2 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <tr><td><a href="http://git-scm.com">Git</a></td><td>the official, command-line Git</td></tr> | ||
| <tr><td><a href="http://tortoisegit.googlecode.com">TortoiseGit</a></td><td>Windows file explorer integration (requires official, command-line Git)</td></tr> | ||
| <tr><td><a href="http://eclipse.org/egit">Eclipse/EGit</a></td><td>Git for the Eclipse IDE (based on JGit, like Gitblit)</td></tr> | ||
@@ -50,3 +49,4 @@ <tr><td><a href="http://gitextensions.github.io">Git Extensions</a></td><td>C# frontend for Git that features Windows Explorer and Visual Studio integration</td></tr> | ||
| <tr><td><a href="http://www.syntevo.com/smartgithg">SmartGit/Hg</a></td><td>A Java Git and Mercurial client for Windows, Mac, and Linux</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>A free Git and Mercurial client for Windows & Mac</td></tr> | ||
| <tr><td><a href="https://git-fork.com/">Fork</a></td><td>A free Git client for Windows & Mac</td></tr> | ||
| <tr><td><a href="http://www.sourcetreeapp.com/">SourceTree</a></td><td>A free Git and Mercurial client for Windows & Mac</td></tr> | ||
| <tr><td><a href="http://www.git-tower.com/">Tower</a></td><td>a Mac OS X Git client</td></tr> | ||
@@ -53,0 +53,0 @@ </tbody> |
@@ -174,2 +174,7 @@ /* | ||
| // load clipboard library to copy repository URL | ||
| addBottomScript("../../clipboard/clipboard.min.js"); | ||
| // instantiate clipboard | ||
| addBottomScript("../../clipboard/gitblit-ctcbtn.js"); | ||
| // set stateless page preference | ||
@@ -176,0 +181,0 @@ setStatelessHint(true); |
@@ -64,5 +64,5 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| </wicket:fragment> | ||
| </wicket:extend> | ||
| </wicket:extend> | ||
| </body> | ||
| </html> |
@@ -589,16 +589,10 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| </wicket:fragment> | ||
| <!-- flash-based button-press copy & paste --> | ||
| <wicket:fragment wicket:id="clippyPanel"> | ||
| <object wicket:message="title:gb.copyToClipboard" style="vertical-align:middle;" | ||
| wicket:id="clippy" | ||
| width="14" | ||
| height="14" | ||
| bgcolor="#ffffff" | ||
| quality="high" | ||
| wmode="transparent" | ||
| scale="noscale" | ||
| allowScriptAccess="sameDomain"></object> | ||
| </wicket:fragment> | ||
| <!-- JavaScript automatic copy to clipboard --> | ||
| <wicket:fragment wicket:id="clippyPanel"> | ||
| <span class="tooltipped tooltipped-n"> | ||
| <img class="ctcbtn" wicket:id="copyIcon" wicket:message="title:gb.copyToClipboard" /> | ||
| </span> | ||
| </wicket:fragment> | ||
@@ -605,0 +599,0 @@ </wicket:extend> |
@@ -15,8 +15,10 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <div class="btn-toolbar" style="margin: 0px;"> | ||
| <div class="btn-group repositoryUrlContainer"> | ||
| <img style="vertical-align: middle;padding: 0px 0px 1px 3px;" wicket:id="accessRestrictionIcon"></img> | ||
| <div class="btn-group repositoryUrlContainer tooltipped tooltipped-w"> | ||
| <img style="max-width:inherit; vertical-align: middle;padding: 0px 0px 1px 3px;" wicket:id="accessRestrictionIcon"></img> | ||
| <span wicket:id="menu"></span> | ||
| <div class="repositoryUrl"> | ||
| <span wicket:id="primaryUrl">[repository primary url]</span> | ||
| <span class="hidden-phone hidden-tablet" wicket:id="copyFunction"></span> | ||
| <span class="tooltipped tooltipped-n"> | ||
| <span class="hidden-phone hidden-tablet" wicket:id="copyFunction"></span> | ||
| </span> | ||
| </div> | ||
@@ -37,3 +39,3 @@ <span class="hidden-phone hidden-tablet repositoryUrlRightCap" wicket:id="primaryUrlPermission">[repository primary url permission]</span> | ||
| <div class="btn-toolbar" style="margin: 4px 0px 0px 0px;"> | ||
| <div class="btn-group" wicket:id="appMenus"> | ||
| <div class="btn-group tooltipped tooltipped-w" wicket:id="appMenus"> | ||
| <span wicket:id="appMenu"></span> | ||
@@ -90,13 +92,5 @@ </div> | ||
| <!-- flash-based button-press copy & paste --> | ||
| <!-- JavaScript automatic copy to clipboard --> | ||
| <wicket:fragment wicket:id="clippyPanel"> | ||
| <object wicket:message="title:gb.copyToClipboard" style="vertical-align:middle;" | ||
| wicket:id="clippy" | ||
| width="14" | ||
| height="14" | ||
| bgcolor="#ffffff" | ||
| quality="high" | ||
| wmode="transparent" | ||
| scale="noscale" | ||
| allowScriptAccess="sameDomain"></object> | ||
| <img class="ctcbtn" style="max-width:inherit;" wicket:id="copyIcon" wicket:message="title:gb.copyToClipboard" /> | ||
| </wicket:fragment> | ||
@@ -103,0 +97,0 @@ |
@@ -28,2 +28,3 @@ /* | ||
| import org.apache.wicket.RequestCycle; | ||
| import org.apache.wicket.behavior.SimpleAttributeModifier; | ||
| import org.apache.wicket.markup.html.basic.Label; | ||
@@ -144,4 +145,3 @@ import org.apache.wicket.markup.html.image.ContextImage; | ||
| Fragment fragment = new Fragment("repoUrl", "actionFragment", this); | ||
| Component content = new Label("content", repoUrl.url).setRenderBodyOnly(true); | ||
| WicketUtils.setCssClass(content, "commandMenuItem"); | ||
| Component content = new Label("content", repoUrl.url).setOutputMarkupId(true); | ||
| fragment.add(content); | ||
@@ -155,3 +155,3 @@ item.add(fragment); | ||
| fragment.add(permissionLabel); | ||
| fragment.add(createCopyFragment(repoUrl.url)); | ||
| fragment.add(createCopyFragment(repoUrl.url, content.getMarkupId())); | ||
| } | ||
@@ -205,3 +205,5 @@ }; | ||
| urlPanel.add(new Label("primaryUrl", primaryUrl.url).setRenderBodyOnly(true)); | ||
| Label primaryUrlLabel = new Label("primaryUrl", primaryUrl.url); | ||
| primaryUrlLabel.setOutputMarkupId(true); | ||
| urlPanel.add(primaryUrlLabel); | ||
@@ -212,3 +214,3 @@ Label permissionLabel = new Label("primaryUrlPermission", primaryUrl.hasPermission() ? primaryUrl.permission.toString() : externalPermission); | ||
| urlPanel.add(permissionLabel); | ||
| urlPanel.add(createCopyFragment(primaryUrl.url)); | ||
| urlPanel.add(createCopyFragment(primaryUrl.url, primaryUrlLabel.getMarkupId())); | ||
@@ -325,2 +327,3 @@ return urlPanel; | ||
| Label content = new Label("content", command); | ||
| content.setOutputMarkupId(true); | ||
| WicketUtils.setCssClass(content, "commandMenuItem"); | ||
@@ -331,3 +334,3 @@ fragment.add(content); | ||
| // copy function for command | ||
| fragment.add(createCopyFragment(command)); | ||
| fragment.add(createCopyFragment(command, content.getMarkupId())); | ||
| } | ||
@@ -356,12 +359,13 @@ }}; | ||
| protected Fragment createCopyFragment(String text) { | ||
| protected Fragment createCopyFragment(String text, String target) { | ||
| if (app().settings().getBoolean(Keys.web.allowFlashCopyToClipboard, true)) { | ||
| // clippy: flash-based copy & paste | ||
| // javascript: browser JS API based copy to clipboard | ||
| Fragment copyFragment = new Fragment("copyFunction", "clippyPanel", this); | ||
| String baseUrl = WicketUtils.getGitblitURL(getRequest()); | ||
| ShockWaveComponent clippy = new ShockWaveComponent("clippy", baseUrl + "/clippy.swf"); | ||
| clippy.setValue("flashVars", "text=" + StringUtils.encodeURL(text)); | ||
| copyFragment.add(clippy); | ||
| ContextImage img = WicketUtils.newImage("copyIcon", "clippy.png"); | ||
| // Add the ID of the target element that holds the text to copy to clipboard | ||
| img.add(new SimpleAttributeModifier("data-clipboard-target", "#"+target)); | ||
| copyFragment.add(img); | ||
| return copyFragment; | ||
| } else { | ||
| } | ||
| else { | ||
| // javascript: manual copy & paste with modal browser prompt dialog | ||
@@ -368,0 +372,0 @@ Fragment copyFragment = new Fragment("copyFunction", "jsPanel", this); |
@@ -40,3 +40,4 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
| <div wicket:id="addKeyComment"></div> | ||
| <div wicket:id="addKeyFeedback"></div> | ||
| <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Add" wicket:message="value:gb.add" wicket:id="addKeyButton" /></div> | ||
@@ -43,0 +44,0 @@ </form> |
@@ -22,2 +22,4 @@ /* | ||
| import com.gitblit.wicket.WicketUtils; | ||
| import org.apache.wicket.Component; | ||
| import org.apache.wicket.ajax.AjaxRequestTarget; | ||
@@ -67,4 +69,27 @@ import org.apache.wicket.ajax.markup.html.AjaxLink; | ||
| final IModel<String> keyFeedback = Model.of(""); | ||
| final List<SshKey> keys = new ArrayList<SshKey>(app().keys().getKeys(user.username)); | ||
| final ListDataProvider<SshKey> dp = new ListDataProvider<SshKey>(keys); | ||
| // Create list data provider that gets rid of the (not serializable EdDSA) PublicKey. | ||
| final ListDataProvider<SshKey> dp = new ListDataProvider<SshKey>(keys) { | ||
| @Override | ||
| public IModel<SshKey> model(final SshKey key) { | ||
| return new IModel<SshKey>() { | ||
| @Override | ||
| public SshKey getObject() { | ||
| return key; | ||
| } | ||
| @Override | ||
| public void setObject(SshKey object) { | ||
| // Cannot get set | ||
| } | ||
| @Override | ||
| public void detach() { | ||
| key.detachPublicKey(); | ||
| } | ||
| }; | ||
| } | ||
| }; | ||
| final DataView<SshKey> keysView = new DataView<SshKey>("keys", dp) { | ||
@@ -95,2 +120,3 @@ private static final long serialVersionUID = 1L; | ||
| } | ||
| keyFeedback.setObject(""); | ||
| } | ||
@@ -129,2 +155,6 @@ }; | ||
| Component addKeyFeedback = new Label("addKeyFeedback", keyFeedback).setOutputMarkupId(true); | ||
| WicketUtils.setCssStyle(addKeyFeedback, "color: red; font-weight: bold;"); | ||
| addKeyForm.add(addKeyFeedback); | ||
| addKeyForm.add(new AjaxButton("addKeyButton") { | ||
@@ -141,2 +171,4 @@ | ||
| // do not submit empty key | ||
| keyFeedback.setObject(getString("gb.addSshKeyErrorEmpty")); | ||
| target.addComponent(addKeyFeedback); | ||
| return; | ||
@@ -150,2 +182,4 @@ } | ||
| // failed to parse the key | ||
| keyFeedback.setObject(getString("gb.addSshKeyErrorFormat")); | ||
| target.addComponent(addKeyFeedback); | ||
| return; | ||
@@ -172,5 +206,8 @@ } | ||
| keyFeedback.setObject(""); | ||
| // update the panel | ||
| target.addComponent(SshKeysPanel.this); | ||
| } | ||
| else keyFeedback.setObject("Key not added."); | ||
| } | ||
@@ -177,0 +214,0 @@ }); |
@@ -91,3 +91,3 @@ #!/usr/bin/env python3 | ||
| if args.patchset is None or args.patchset is 0: | ||
| if args.patchset is None or args.patchset == 0: | ||
| branch = 'ticket/{:d}'.format(args.id) | ||
@@ -108,3 +108,3 @@ illegals = set(branches) & {'ticket'} | ||
| if args.patchset is None or args.patchset is 0: | ||
| if args.patchset is None or args.patchset == 0: | ||
| # checkout the current ticket patchset | ||
@@ -136,3 +136,3 @@ if args.force: | ||
| # pull the patchset from the remote repository | ||
| if args.patchset is None or args.patchset is 0: | ||
| if args.patchset is None or args.patchset == 0: | ||
| print("Pulling ticket {} from the '{}' repository".format(args.id, args.remote)) | ||
@@ -556,7 +556,7 @@ patchset_ref = 'ticket/{:d}'.format(args.id) | ||
| merge = __call(['git', 'merge', '--ff-only', branch, 'FETCH_HEAD'], echo=True, fail=False) | ||
| if len(merge) is 1: | ||
| if len(merge) == 1: | ||
| up_to_date = merge[0].lower().index('up-to-date') > 0 | ||
| if up_to_date: | ||
| return | ||
| elif len(merge) is 0: | ||
| elif len(merge) == 0: | ||
| print('') | ||
@@ -624,3 +624,3 @@ print("Your '{}' branch has diverged from patchset {} on the '{}' repository.".format(branch, patchset, remote)) | ||
| line_str = str(line).strip() | ||
| if len(line_str) is 0: | ||
| if len(line_str) == 0: | ||
| break | ||
@@ -631,3 +631,3 @@ lines.append(line_str) | ||
| p.wait() | ||
| if fail and p.returncode is not 0: | ||
| if fail and p.returncode != 0: | ||
| exit(p.returncode) | ||
@@ -634,0 +634,0 @@ |
@@ -2411,2 +2411,172 @@ body { | ||
| font-variant: normal; | ||
| } | ||
| } | ||
| /* | ||
| Copy-to-clipboard tooltip styling from Github's primer.css | ||
| https://primer.style/css/components/tooltips | ||
| Adjusted to not hover but fade-in/out on clipboard events. | ||
| */ | ||
| .tooltipped { | ||
| position:relative | ||
| } | ||
| .tooltipped:after { | ||
| position: absolute; | ||
| z-index: 1000000; | ||
| padding: 5px 8px; | ||
| font: normal normal 11px/1.5 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"; | ||
| color: #fff; | ||
| background: rgba(42, 42, 42, .8); | ||
| text-align: center; | ||
| text-decoration: none; | ||
| text-shadow: none; | ||
| text-transform: none; | ||
| letter-spacing: normal; | ||
| word-wrap: break-word; | ||
| white-space: pre; | ||
| pointer-events: none; | ||
| content: attr(data-tt-text); | ||
| border-radius: 3px; | ||
| -webkit-font-smoothing:subpixel-antialiased; | ||
| opacity: 0; | ||
| transition: 0.5s opacity; | ||
| } | ||
| .tooltipped:before { | ||
| position: absolute; | ||
| z-index: 1000001; | ||
| width: 0; | ||
| height: 0; | ||
| color: rgba(42, 42, 42, .8); | ||
| pointer-events: none; | ||
| content: ""; | ||
| border:5px solid transparent; | ||
| opacity: 0; | ||
| transition: 0.5s opacity; | ||
| } | ||
| .tooltipped-active:before, .tooltipped-active:after { | ||
| opacity: 1; | ||
| text-decoration:none | ||
| } | ||
| .tooltipped-s:after, .tooltipped-se:after, .tooltipped-sw:after { | ||
| top: 100%; | ||
| right: 50%; | ||
| margin-top:5px | ||
| } | ||
| .tooltipped-s:before, .tooltipped-se:before, .tooltipped-sw:before { | ||
| top: auto; | ||
| right: 50%; | ||
| bottom: -5px; | ||
| margin-right: -5px; | ||
| border-bottom-color:rgba(42, 42, 42, .8) | ||
| } | ||
| .tooltipped-se:after { | ||
| right: auto; | ||
| left: 50%; | ||
| margin-left:-15px | ||
| } | ||
| .tooltipped-sw:after { | ||
| margin-right:-15px | ||
| } | ||
| .tooltipped-n:after, .tooltipped-ne:after, .tooltipped-nw:after { | ||
| right: 50%; | ||
| bottom: 100%; | ||
| margin-bottom:5px | ||
| } | ||
| .tooltipped-n:before, .tooltipped-ne:before, .tooltipped-nw:before { | ||
| top: -5px; | ||
| right: 50%; | ||
| bottom: auto; | ||
| margin-right: -5px; | ||
| border-top-color:rgba(42, 42, 42, .8) | ||
| } | ||
| .tooltipped-ne:after { | ||
| right: auto; | ||
| left: 50%; | ||
| margin-left:-15px | ||
| } | ||
| .tooltipped-nw:after { | ||
| margin-right:-15px | ||
| } | ||
| .tooltipped-s:after, .tooltipped-n:after { | ||
| -webkit-transform: translateX(50%); | ||
| -ms-transform: translateX(50%); | ||
| transform:translateX(50%) | ||
| } | ||
| .tooltipped-w:after { | ||
| right: 100%; | ||
| bottom: 50%; | ||
| margin-right: 5px; | ||
| -webkit-transform: translateY(50%); | ||
| -ms-transform: translateY(50%); | ||
| transform:translateY(50%) | ||
| } | ||
| .tooltipped-w:before { | ||
| top: 50%; | ||
| bottom: 50%; | ||
| left: -5px; | ||
| margin-top: -5px; | ||
| border-left-color:rgba(42, 42, 42, .8) | ||
| } | ||
| .tooltipped-e:after { | ||
| bottom: 50%; | ||
| left: 100%; | ||
| margin-left: 5px; | ||
| -webkit-transform: translateY(50%); | ||
| -ms-transform: translateY(50%); | ||
| transform:translateY(50%) | ||
| } | ||
| .tooltipped-e:before { | ||
| top: 50%; | ||
| right: -5px; | ||
| bottom: 50%; | ||
| margin-top: -5px; | ||
| border-right-color:rgba(42, 42, 42, .8) | ||
| } | ||
| .tooltipped-sticky:before, .tooltipped-sticky:after { | ||
| display:inline-block | ||
| } | ||
| .fullscreen-overlay-enabled.dark-theme .tooltipped:after { | ||
| color: #000; | ||
| background:rgba(200, 200, 200, .8) | ||
| } | ||
| .fullscreen-overlay-enabled.dark-theme .tooltipped .tooltipped-s:before, .fullscreen-overlay-enabled.dark-theme .tooltipped .tooltipped-se:before, .fullscreen-overlay-enabled.dark-theme .tooltipped .tooltipped-sw:before { | ||
| border-bottom-color:rgba(200, 200, 200, .8) | ||
| } | ||
| .fullscreen-overlay-enabled.dark-theme .tooltipped.tooltipped-n:before, .fullscreen-overlay-enabled.dark-theme .tooltipped.tooltipped-ne:before, .fullscreen-overlay-enabled.dark-theme .tooltipped.tooltipped-nw:before { | ||
| border-top-color:rgba(200, 200, 200, .8) | ||
| } | ||
| .fullscreen-overlay-enabled.dark-theme .tooltipped.tooltipped-e:before { | ||
| border-right-color:rgba(200, 200, 200, .8) | ||
| } | ||
| .fullscreen-overlay-enabled.dark-theme .tooltipped.tooltipped-w:before { | ||
| border-left-color:rgba(200, 200, 200, .8) | ||
| } |
@@ -35,2 +35,3 @@ /* | ||
| import java.util.List; | ||
| import java.util.StringTokenizer; | ||
@@ -47,3 +48,3 @@ import junit.framework.TestCase; | ||
| public void testSimpleWithExtendedLink() throws BugtraqException { | ||
| final BugtraqFormatter formatter = createFormatter(createEntry("https://jira.atlassian.com/browse/JRA-%BUGID%", null, "JRA-\\d+", "\\d+", null)); | ||
| final BugtraqFormatter formatter = createFormatter(createEntry("https://jira.atlassian.com/browse/JRA-%BUGID%", null, "JRA-\\d+", "\\d+", null, null)); | ||
| doTest(formatter, "JRA-7399: Email subject formatting", l("JRA-7399", "https://jira.atlassian.com/browse/JRA-7399"), t(": Email subject formatting")); | ||
@@ -55,3 +56,3 @@ doTest(formatter, " JRA-7399, JRA-7398: Email subject formatting", t(" "), l("JRA-7399", "https://jira.atlassian.com/browse/JRA-7399"), t(", "), l("JRA-7398", "https://jira.atlassian.com/browse/JRA-7398"), t(": Email subject formatting")); | ||
| public void testLinkText() throws BugtraqException { | ||
| final BugtraqFormatter formatter = createFormatter(createEntry("https://jira.atlassian.com/browse/JRA-%BUGID%", null, "JRA-\\d+", "\\d+", "JIRA-%BUGID%")); | ||
| final BugtraqFormatter formatter = createFormatter(createEntry("https://jira.atlassian.com/browse/JRA-%BUGID%", null, "JRA-\\d+", "\\d+", "JIRA-%BUGID%", null)); | ||
| doTest(formatter, " JRA-7399, JRA is text, JRA-7398: Email subject formatting", t(" "), l("JIRA-7399", "https://jira.atlassian.com/browse/JRA-7399"), t(", JRA is text, "), l("JIRA-7398", "https://jira.atlassian.com/browse/JRA-7398"), t(": Email subject formatting")); | ||
@@ -61,4 +62,4 @@ } | ||
| public void testTwoNonIntersectingConfigurations() throws BugtraqException { | ||
| final BugtraqFormatter formatter = createFormatter(createEntry("https://jira.atlassian.com/browse/%BUGID%", null, null, "JRA-\\d+", null), | ||
| createEntry("https://issues.apache.org/jira/browse/%BUGID%", null, null, "VELOCITY-\\d+", null)); | ||
| final BugtraqFormatter formatter = createFormatter(createEntry("https://jira.atlassian.com/browse/%BUGID%", null, null, "JRA-\\d+", null, null), | ||
| createEntry("https://issues.apache.org/jira/browse/%BUGID%", null, null, "VELOCITY-\\d+", null, null)); | ||
| doTest(formatter, "JRA-7399, VELOCITY-847: fix", l("JRA-7399", "https://jira.atlassian.com/browse/JRA-7399"), t(", "), l("VELOCITY-847", "https://issues.apache.org/jira/browse/VELOCITY-847"), t(": fix")); | ||
@@ -70,4 +71,4 @@ doTest(formatter, " JRA-7399: fix/VELOCITY-847", t(" "), l("JRA-7399", "https://jira.atlassian.com/browse/JRA-7399"), t(": fix/"), l("VELOCITY-847", "https://issues.apache.org/jira/browse/VELOCITY-847")); | ||
| public void testTwoIntersectingConfigurations() throws BugtraqException { | ||
| final BugtraqFormatter formatter = createFormatter(createEntry("https://host1/%BUGID%", null, null, "A[AB]", null), | ||
| createEntry("https://host2/%BUGID%", null, null, "BA[A]?", null)); | ||
| final BugtraqFormatter formatter = createFormatter(createEntry("https://host1/%BUGID%", null, null, "A[AB]", null, null), | ||
| createEntry("https://host2/%BUGID%", null, null, "BA[A]?", null, null)); | ||
| doTest(formatter, "AA: fix", l("AA", "https://host1/AA"), t(": fix")); | ||
@@ -87,10 +88,30 @@ doTest(formatter, "AB: fix", l("AB", "https://host1/AB"), t(": fix")); | ||
| public void testMultipleProjects() throws BugtraqException { | ||
| final BugtraqFormatter formatter = createFormatter(createEntry("https://jira.atlassian.com/browse/%PROJECT%-%BUGID%", null, "%PROJECT%-\\d+", "\\d+", null, "JRA,JRB,JRC")); | ||
| doTest(formatter, "JRA-7399: Email subject formatting", l("JRA-7399", "https://jira.atlassian.com/browse/JRA-7399"), t(": Email subject formatting")); | ||
| doTest(formatter, " JRA-7399, JRB-7398: Email subject formatting", t(" "), l("JRA-7399", "https://jira.atlassian.com/browse/JRA-7399"), t(", "), l("JRB-7398", "https://jira.atlassian.com/browse/JRB-7398"), t(": Email subject formatting")); | ||
| doTest(formatter, "Fixed JRC-7399", t("Fixed "), l("JRC-7399", "https://jira.atlassian.com/browse/JRC-7399")); | ||
| } | ||
| // Utils ================================================================== | ||
| private BugtraqFormatter createFormatter(BugtraqEntry ... entries) { | ||
| private BugtraqFormatter createFormatter(BugtraqConfigEntry ... entries) { | ||
| return new BugtraqFormatter(new BugtraqConfig(Arrays.asList(entries))); | ||
| } | ||
| private BugtraqConfigEntry createEntry(String url, @Nullable String filterRegex, @Nullable String linkRegex, @NotNull String idRegex, @Nullable String linkText, @Nullable String projectsList) throws BugtraqException { | ||
| final List<String> projects; | ||
| if (projectsList != null) { | ||
| projects = new ArrayList<>(); | ||
| private BugtraqEntry createEntry(String url, @Nullable String filterRegex, @Nullable String linkRegex, @NotNull String idRegex, @Nullable String linkText) throws BugtraqException { | ||
| return new BugtraqEntry(url, idRegex, linkRegex, filterRegex, linkText); | ||
| final StringTokenizer tokenizer = new StringTokenizer(projectsList, ",", false); | ||
| while (tokenizer.hasMoreTokens()) { | ||
| projects.add(tokenizer.nextToken()); | ||
| } | ||
| } | ||
| else { | ||
| projects = null; | ||
| } | ||
| return new BugtraqConfigEntry(url, idRegex, linkRegex, filterRegex, linkText, projects); | ||
| } | ||
@@ -97,0 +118,0 @@ |
| # | ||
| # Gitblit Unit Testing properties | ||
| # | ||
| gitblit.testRun = true | ||
| git.allowAnonymousPushes = true | ||
@@ -5,0 +6,0 @@ git.defaultAccessRestriction = NONE |
@@ -54,3 +54,3 @@ # | ||
| # e.g. git.submoduleUrlPatterns = .*?://github.com/(.*) will extract | ||
| # *gitblit/gitblit.git* from *git://github.com/gitblit/gitblit.git* | ||
| # *gitblit-org/gitblit.git* from *git://github.com/gitblit-org/gitblit.git* | ||
| # If no matches are found then the submodule repository name is assumed to be | ||
@@ -57,0 +57,0 @@ # whatever trails the last / character. (e.g. gitblit.git). |
@@ -29,2 +29,5 @@ /* | ||
| import com.gitblit.instance.GitblitInstanceIdTest; | ||
| import com.gitblit.instance.GitblitInstanceStatTest; | ||
| import com.gitblit.instance.GitblitInstanceTest; | ||
| import com.gitblit.utils.TimeUtilsTest; | ||
@@ -78,3 +81,4 @@ import org.eclipse.jgit.api.Git; | ||
| SshKeysDispatcherTest.class, UITicketTest.class, PathUtilsTest.class, SshKerberosAuthenticationTest.class, | ||
| GravatarTest.class, FilestoreManagerTest.class, FilestoreServletTest.class, TicketReferenceTest.class }) | ||
| GravatarTest.class, FilestoreManagerTest.class, FilestoreServletTest.class, TicketReferenceTest.class, | ||
| GitblitInstanceIdTest.class, GitblitInstanceStatTest.class, GitblitInstanceTest.class }) | ||
| public class GitBlitSuite { | ||
@@ -221,3 +225,3 @@ | ||
| cloneOrFetch("ticgit.git", TICGIT_REPO_SOURCE.getAbsolutePath()); | ||
| cloneOrFetch("test/jgit.git", "https://github.com/eclipse/jgit.git"); | ||
| cloneOrFetch("test/jgit.git", "https://github.com/eclipse-jgit/jgit.git"); | ||
| cloneOrFetch("test/helloworld.git", HELLOWORLD_REPO_SOURCE.getAbsolutePath()); | ||
@@ -224,0 +228,0 @@ cloneOrFetch("test/ambition.git", AMBITION_REPO_SOURCE.getAbsolutePath()); |
@@ -34,3 +34,6 @@ /* | ||
| public static IStoredSettings settings() { | ||
| return runtime().getSettings(); | ||
| IStoredSettings settings = runtime().getSettings(); | ||
| // Insert marker that this is running as a test | ||
| settings.overrideSetting("gitblit.testRun", "true"); | ||
| return settings; | ||
| } | ||
@@ -37,0 +40,0 @@ |
@@ -31,3 +31,3 @@ /* | ||
| import org.apache.sshd.common.util.SecurityUtils; | ||
| import org.apache.sshd.common.util.security.SecurityUtils; | ||
| import org.junit.BeforeClass; | ||
@@ -34,0 +34,0 @@ import org.junit.Test; |
@@ -55,2 +55,4 @@ /* | ||
| this.settings = settings; | ||
| // Insert marker that this is running as a test | ||
| settings.overrideSetting("gitblit.testRun", "true"); | ||
@@ -98,2 +100,7 @@ this.serverStatus = new ServerStatus(); | ||
| public void setStatus(ServerStatus status) | ||
| { | ||
| this.serverStatus = status; | ||
| } | ||
| @Override | ||
@@ -100,0 +107,0 @@ public ServerStatus getStatus() { |
@@ -19,2 +19,3 @@ /* | ||
| import java.io.File; | ||
| import java.security.KeyPair; | ||
| import java.text.MessageFormat; | ||
@@ -24,2 +25,3 @@ import java.util.List; | ||
| import org.apache.sshd.client.SshClient; | ||
| import org.apache.sshd.client.future.AuthFuture; | ||
| import org.apache.sshd.client.session.ClientSession; | ||
@@ -47,10 +49,71 @@ import org.eclipse.jgit.api.CloneCommand; | ||
| @Test | ||
| public void testPasswordAuthentication() throws Exception { | ||
| SshClient client = getClient(); | ||
| ClientSession session = client.connect(username, "localhost", GitBlitSuite.sshPort).verify().getSession(); | ||
| session.addPasswordIdentity(password); | ||
| AuthFuture authFuture = session.auth(); | ||
| assertTrue(authFuture.await()); | ||
| assertTrue(authFuture.isSuccess()); | ||
| } | ||
| @Test | ||
| public void testPublicKeyAuthentication() throws Exception { | ||
| SshClient client = getClient(); | ||
| ClientSession session = client.connect(username, "localhost", GitBlitSuite.sshPort).verify().getSession(); | ||
| session.addPublicKeyIdentity(rwKeyPair); | ||
| assertTrue(session.auth().await()); | ||
| AuthFuture authFuture = session.auth(); | ||
| assertTrue(authFuture.await()); | ||
| assertTrue(authFuture.isSuccess()); | ||
| } | ||
| @Test | ||
| public void testWrongPublicKeyAuthentication() throws Exception { | ||
| SshClient client = getClient(); | ||
| ClientSession session = client.connect(username, "localhost", GitBlitSuite.sshPort).verify().getSession(); | ||
| KeyPair attackKeyPair = generator.generateKeyPair(); | ||
| session.addPublicKeyIdentity(attackKeyPair); | ||
| AuthFuture authFuture = session.auth(); | ||
| assertTrue(authFuture.await()); | ||
| assertFalse(authFuture.isSuccess()); | ||
| } | ||
| @Test | ||
| public void testWrongPublicKeyThenPasswordAuthentication() throws Exception { | ||
| SshClient client = getClient(); | ||
| ClientSession session = client.connect(username, "localhost", GitBlitSuite.sshPort).verify().getSession(); | ||
| KeyPair otherKeyPair = generator.generateKeyPair(); | ||
| session.addPublicKeyIdentity(otherKeyPair); | ||
| AuthFuture authFuture = session.auth(); | ||
| assertTrue(authFuture.await()); | ||
| assertFalse(authFuture.isSuccess()); | ||
| session.addPasswordIdentity(password); | ||
| authFuture = session.auth(); | ||
| assertTrue(authFuture.await()); | ||
| assertTrue(authFuture.isSuccess()); | ||
| } | ||
| @Test | ||
| public void testWrongPublicKeyThenWrongPasswordAuthentication() throws Exception { | ||
| SshClient client = getClient(); | ||
| ClientSession session = client.connect(username, "localhost", GitBlitSuite.sshPort).verify().getSession(); | ||
| KeyPair otherKeyPair = generator.generateKeyPair(); | ||
| KeyPair attackKeyPair = new KeyPair(rwKeyPair.getPublic(), otherKeyPair.getPrivate()); | ||
| session.addPublicKeyIdentity(attackKeyPair); | ||
| AuthFuture authFuture = session.auth(); | ||
| assertTrue(authFuture.await()); | ||
| assertFalse(authFuture.isSuccess()); | ||
| session.addPasswordIdentity("nothing"); | ||
| authFuture = session.auth(); | ||
| assertTrue(authFuture.await()); | ||
| assertFalse(authFuture.isSuccess()); | ||
| } | ||
| @Test | ||
| public void testVersionCommand() throws Exception { | ||
@@ -57,0 +120,0 @@ String result = testSshCommand("version"); |
@@ -39,3 +39,3 @@ /* | ||
| import org.apache.sshd.common.config.keys.FilePasswordProvider; | ||
| import org.apache.sshd.common.util.SecurityUtils; | ||
| import org.apache.sshd.common.util.security.SecurityUtils; | ||
| import org.eclipse.jgit.lib.Config; | ||
@@ -42,0 +42,0 @@ import org.eclipse.jgit.storage.file.FileBasedConfig; |
@@ -64,7 +64,35 @@ /* | ||
| public void testEscapeForHtml() throws Exception { | ||
| String input = "& < > \" \t"; | ||
| String outputNoChange = "& < > " \t"; | ||
| String outputChange = "& < > " "; | ||
| String input = "\t & < > \""; | ||
| String outputNoChange = "\t & < > ""; | ||
| String outputChange = " & < > ""; | ||
| assertEquals(outputNoChange, StringUtils.escapeForHtml(input, false)); | ||
| assertEquals(outputChange, StringUtils.escapeForHtml(input, true)); | ||
| input = "a\tb"; | ||
| outputNoChange = "a\tb"; | ||
| outputChange = "a b"; | ||
| assertEquals(outputNoChange, StringUtils.escapeForHtml(input, false)); | ||
| assertEquals(outputChange, StringUtils.escapeForHtml(input, true)); | ||
| input = "\ta b\t"; | ||
| outputNoChange = "\ta b\t"; | ||
| outputChange = " a b "; | ||
| assertEquals(outputNoChange, StringUtils.escapeForHtml(input, false)); | ||
| assertEquals(outputChange, StringUtils.escapeForHtml(input, true)); | ||
| input = "\t <> \t"; | ||
| outputNoChange = "\t <> \t"; | ||
| outputChange = " <> "; | ||
| assertEquals(outputNoChange, StringUtils.escapeForHtml(input, false)); | ||
| assertEquals(outputChange, StringUtils.escapeForHtml(input, true)); | ||
| String tabs = "\t"; | ||
| int tabSpaces; | ||
| int expectedLength; | ||
| for (int i = 0; i < 50; i++) { | ||
| tabSpaces = 4 - i % 4; | ||
| expectedLength = (i + tabSpaces) * 6; // = 6 chars | ||
| assertEquals(expectedLength, StringUtils.escapeForHtml(tabs, true).length()); | ||
| tabs = " " + tabs; | ||
| } | ||
| } | ||
@@ -110,2 +138,8 @@ | ||
| @Test | ||
| public void testSHA256() throws Exception { | ||
| assertEquals("badf72532e259f2b67a40475486c7e71bf48bc71d7b0d43d8e99acfb3ac24e1b", | ||
| StringUtils.getSHA256("margaret@london.uk")); | ||
| } | ||
| @Test | ||
| public void testMD5() throws Exception { | ||
@@ -112,0 +146,0 @@ assertEquals("77fb8d95331f0d557472f6776d3aedf6", |
@@ -18,2 +18,5 @@ /* | ||
| import com.gitblit.Constants; | ||
| import com.gitblit.models.RegistrantAccessPermission; | ||
| import com.gitblit.models.TeamModel; | ||
| import org.junit.Test; | ||
@@ -23,2 +26,4 @@ | ||
| import java.util.List; | ||
| /** | ||
@@ -54,2 +59,46 @@ * @author Alfred Schmid | ||
| @Test | ||
| public void getRepositoryPermissionsMultipleTeams() | ||
| { | ||
| TeamModel aTeam = new TeamModel("A team"); | ||
| aTeam.addRepositoryPermission("RW+:acerepo.git"); | ||
| aTeam.addRepositoryPermission("V:boobrepo.git"); | ||
| TeamModel bTeam = new TeamModel("Team B"); | ||
| bTeam.addRepositoryPermission("R:acerepo.git"); | ||
| bTeam.addRepositoryPermission("RWC:boobrepo.git"); | ||
| UserModel user = new UserModel("tessiur"); | ||
| user.teams.add(aTeam); | ||
| user.teams.add(bTeam); | ||
| user.addRepositoryPermission("RW+:myrepo.git"); | ||
| List<RegistrantAccessPermission> repoPerms = user.getRepositoryPermissions(); | ||
| int found = 0; | ||
| for (RegistrantAccessPermission p : repoPerms) { | ||
| switch (p.registrant) { | ||
| case "acerepo.git": | ||
| assertEquals("Expected REWIND(RW+) permission for " + p.registrant, Constants.AccessPermission.REWIND, p.permission); | ||
| found++; | ||
| break; | ||
| case "boobrepo.git": | ||
| assertEquals("Expected CREATE(RWC) permission for " + p.registrant, Constants.AccessPermission.CREATE, p.permission); | ||
| found++; | ||
| break; | ||
| case "myrepo.git": | ||
| assertEquals("Expected REWIND(RW+) permission for " + p.registrant, Constants.AccessPermission.REWIND, p.permission); | ||
| found++; | ||
| break; | ||
| default: | ||
| fail("Unknown repository registrant " + p.registrant); | ||
| break; | ||
| } | ||
| } | ||
| assertEquals("Repostory permissions missing in list.", 3, found); | ||
| } | ||
| } |
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 too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
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