view mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/HgVcsRoot.java @ 983:ea7a543ecf2f Indore-2017.2.x

TW-50054 introduce server hg path whitelist Hg path from VCS root is used on the server only if it is in whitelist
author Dmitry Neverov <dmitry.neverov@gmail.com>
date Thu, 25 Jan 2018 09:07:17 +0100
parents 4a0ea921c214
children ec20a7691c8b f342d25311c1
line wrap: on
line source
/*
 * Copyright 2000-2018 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;

import jetbrains.buildServer.buildTriggers.vcs.mercurial.Constants;
import jetbrains.buildServer.buildTriggers.vcs.mercurial.PathUtil;
import jetbrains.buildServer.serverSide.TeamCityProperties;
import jetbrains.buildServer.util.StringUtil;
import jetbrains.buildServer.vcs.VcsRoot;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Represents mercurial VCS root
 */
public class HgVcsRoot {
  public static final String DEFAULT_BRANCH_NAME = "default";

  private final Map<String, String> myVcsRootProperties;
  private final String myRepository;
  private final String myHgCommandPath;
  private final String myBranchName;
  private final boolean myUncompressedTransfer;
  private final String myCustomClonePath;
  private final String myUserForTag;
  private final AuthSettings myAuthSettings;
  private File myCustomWorkingDir;
  private final boolean myDetectSubrepoChanges;
  private final boolean myUseTagsAsBranches;
  private final boolean myIncludeSubreposInPatch;
  private final boolean myUseArchiveForPatch;
  private final PurgePolicy myPurgePolicy;
  private final boolean myIgnoreMissingDefaultBranch;
  private final String myCustomHgConfig;
  private final Boolean myUseAgentMirrors;

  public HgVcsRoot(@NotNull final VcsRoot vcsRoot) {
    this(vcsRoot.getProperties());
  }

  public HgVcsRoot(@NotNull Map<String, String> vcsRootProperties) {
    myVcsRootProperties = vcsRootProperties;
    myRepository = getProperty(Constants.REPOSITORY_PROP);
    myHgCommandPath = getProperty(Constants.HG_COMMAND_PATH_PROP);
    myBranchName = getProperty(Constants.BRANCH_NAME_PROP);
    myCustomClonePath = readCustomClonePath();
    myUncompressedTransfer = "true".equals(getProperty(Constants.UNCOMPRESSED_TRANSFER));
    myUserForTag = getProperty(Constants.USER_FOR_TAG);
    myAuthSettings = new AuthSettings(getProperty(Constants.USERNAME), getProperty(Constants.PASSWORD));
    myDetectSubrepoChanges = Boolean.parseBoolean(getProperty(Constants.DETECT_SUBREPO_CHANGES));
    myUseTagsAsBranches = Boolean.parseBoolean(getProperty(Constants.USE_TAGS_AS_BRANCHES));
    myIncludeSubreposInPatch = Boolean.parseBoolean(getProperty(Constants.INCLUDE_SUBREPOS_IN_PATCH, "true"));
    myUseArchiveForPatch = Boolean.parseBoolean(getProperty(Constants.USE_ARCHIVE_FOR_PATCH));
    myPurgePolicy = readPurgePolicy(vcsRootProperties);
    myIgnoreMissingDefaultBranch = Boolean.valueOf(getProperty(Constants.IGNORE_MISSING_DEFAULT_BRANCH, "false"));
    myCustomHgConfig = readCustomHgConfig();
    myUseAgentMirrors = readUseSharedMirrors();
  }

  @NotNull
  private String readCustomHgConfig() {
    if (TeamCityProperties.getBooleanOrTrue(Constants.CUSTOM_HG_CONFIG_ENABLED)) {
      return getProperty(Constants.CUSTOM_HG_CONFIG_PROP, "");
    } else {
      return "";
    }
  }

  @Nullable
  private Boolean readUseSharedMirrors() {
    String prop = getProperty(Constants.USE_AGENT_MIRRORS);
    if (StringUtil.isEmpty(prop))
      return null;
    return Boolean.parseBoolean(prop);
  }

  @NotNull
  private PurgePolicy readPurgePolicy(@NotNull Map<String, String> properties) {
    String policy = properties.get(Constants.PURGE_POLICY);
    if (StringUtil.isEmpty(policy))
      return PurgePolicy.DONT_RUN;
    return PurgePolicy.valueOf(policy);
  }

  public HgVcsRoot withUrl(@NotNull String repositoryUrl) {
    Map<String, String> customUrlProperties = new HashMap<String, String>(getProperties());
    customUrlProperties.put(Constants.REPOSITORY_PROP, repositoryUrl);
    return new HgVcsRoot(customUrlProperties);
  }

  public String getCustomClonePath() {
    return myCustomClonePath;
  }

  public String getRepository() {
    return myRepository;
  }

  public boolean includeSubreposInPatch() {
    return myIncludeSubreposInPatch;
  }

  public boolean useArchiveForPatch() {
    if (TeamCityProperties.getBoolean("teamcity.hg.useArchiveForPatch"))
      return true;
    return myUseArchiveForPatch;
  }

  /**
   * Returns name of the branch to use (returns 'default' if no branch specified)
   * @return see above
   */
  @NotNull
  public String getBranchName() {
    return StringUtil.isEmpty(myBranchName) ? DEFAULT_BRANCH_NAME : myBranchName;
  }

  public boolean isUncompressedTransfer() {
    return myUncompressedTransfer;
  }

  /**
   * @return path to hg command as it is set in VCS root settings
   */
  public String getHgPath() {
    return myHgCommandPath;
  }

  @Nullable
  public String getUserForTag() {
    return myUserForTag;
  }

  public String getRepositoryUrlWithCredentials() {
    return myAuthSettings.getRepositoryUrlWithCredentials(myRepository);
  }

  /**
   * Set custom working dir for vcs root. This option make sence only for server-side checkout
   * @param customWorkingDir custom working dir
   */
  public void setCustomWorkingDir(@NotNull final File customWorkingDir) {
    myCustomWorkingDir = PathUtil.getCanonicalFile(customWorkingDir);
  }

  /**
   * Returns custom working dir for root or null if default working dir should be used.
   * This options make sence only with server-side checkout.
   * @return see above
   */
  @Nullable
  public File getCustomWorkingDir() {
    return myCustomWorkingDir;
  }

  public AuthSettings getAuthSettings() {
    return myAuthSettings;
  }

  public boolean detectSubrepoChanges() {
    return myDetectSubrepoChanges;
  }

  public boolean useTagsAsBranches() {
    return myUseTagsAsBranches;
  }

  public boolean isSubrepo() {
    return Boolean.valueOf(getProperty("teamcity.internal.subrepo"));
  }

  public String getSubrepoPath() {
    return getProperty("teamcity.internal.subrepo.path");
  }

  public String expandSubrepoPath(@NotNull String subrepoPath) {
    if (!isSubrepo())
      return subrepoPath;
    String mySubrepoPath = getSubrepoPath();
    if (mySubrepoPath == null)
      return subrepoPath;
    if (!mySubrepoPath.endsWith("/"))
      mySubrepoPath += "/";
    return mySubrepoPath + subrepoPath;
  }

  public String getProperty(@NotNull String propertyName) {
    return myVcsRootProperties.get(propertyName);
  }

  private String readCustomClonePath() {
    String clonePath = getProperty(Constants.SERVER_CLONE_PATH_PROP);
    if (TeamCityProperties.getBoolean(Constants.CUSTOM_CLONE_PATH_ENABLED))
      return clonePath;
    if (isAllowedCustomClonePath(clonePath))
      return clonePath;
    return null;
  }

  @NotNull
  public String getProperty(@NotNull String propertyName, @NotNull String defaultValue) {
    String value = myVcsRootProperties.get(propertyName);
    if (value != null)
      return value;
    return defaultValue;
  }

  @NotNull
  public PurgePolicy getPurgePolicy() {
    return myPurgePolicy;
  }

  @NotNull
  public Map<String, String> getProperties() {
    return myVcsRootProperties;
  }

  public boolean isIgnoreMissingDefaultBranch() {
    return myIgnoreMissingDefaultBranch;
  }

  @NotNull
  public String getCustomHgConfig() {
    return myCustomHgConfig;
  }

  @Nullable
  public Boolean getUseAgentMirrors() {
    return myUseAgentMirrors;
  }

  public static enum PurgePolicy {
    DONT_RUN,
    PURGE_UNKNOWN,
    PURGE_ALL
  }


  public static boolean hasCustomClonePathWhitelist() {
    return StringUtil.isNotEmpty(TeamCityProperties.getProperty(Constants.CUSTOM_CLONE_PATH_WHITELIST));
  }

  public static boolean isAllowedCustomClonePath(@Nullable String path) {
    if (StringUtil.isEmptyOrSpaces(path))
      return false;
    String whitelist = TeamCityProperties.getProperty(Constants.CUSTOM_CLONE_PATH_WHITELIST);
    if (StringUtil.isEmpty(whitelist))
      return false;
    List<String> paths = StringUtil.split(whitelist, ";");
    for (String p : paths) {
      if (p.endsWith("/*")) {
        return new File(path).getParentFile().equals(new File(p.substring(0, p.length() - 2)));
      } else if (p.equals(path)) {
        return true;
      }
    }
    return false;
  }

  public static boolean isAllowedHgPath(@Nullable String path) {
    if (StringUtil.isEmptyOrSpaces(path))
      return false;
    String whitelist = TeamCityProperties.getProperty(Constants.CUSTOM_SERVER_HG_PATH_WHITELIST);
    if (StringUtil.isEmpty(whitelist))
      return false;
    List<String> paths = StringUtil.split(whitelist, ";");
    for (String p : paths) {
      if (p.equals(path))
        return true;
    }
    return false;
  }
}