/*************************GO-LICENSE-START********************************* * Copyright 2014 ThoughtWorks, Inc. * * 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. *************************GO-LICENSE-END***********************************/ package com.thoughtworks.go.config.materials.tfs; import com.thoughtworks.go.config.PasswordEncrypter; import com.thoughtworks.go.config.materials.PasswordAwareMaterial; import com.thoughtworks.go.config.materials.ScmMaterial; import com.thoughtworks.go.config.materials.ScmMaterialConfig; import com.thoughtworks.go.config.materials.SubprocessExecutionContext; import com.thoughtworks.go.domain.MaterialInstance; import com.thoughtworks.go.domain.materials.*; import com.thoughtworks.go.domain.materials.tfs.TfsCommand; import com.thoughtworks.go.domain.materials.tfs.TfsCommandFactory; import com.thoughtworks.go.domain.materials.tfs.TfsMaterialInstance; import com.thoughtworks.go.security.GoCipher; import com.thoughtworks.go.util.GoConstants; import com.thoughtworks.go.util.StringUtil; import com.thoughtworks.go.util.command.ConsoleOutputStreamConsumer; import com.thoughtworks.go.util.command.UrlArgument; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.apache.log4j.Logger; import org.bouncycastle.crypto.InvalidCipherTextException; import javax.annotation.PostConstruct; import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import static com.thoughtworks.go.util.ExceptionUtils.bomb; import static java.lang.String.format; public class TfsMaterial extends ScmMaterial implements PasswordAwareMaterial, PasswordEncrypter { private static final Logger LOGGER = Logger.getLogger(TfsMaterial.class); public static final String TYPE = "TfsMaterial"; private UrlArgument url; private String userName; private String domain = ""; private String password; private String encryptedPassword; private String projectPath; private final GoCipher goCipher; public TfsMaterial(GoCipher goCipher) { super(TYPE); this.goCipher = goCipher; } public TfsMaterial(GoCipher goCipher, UrlArgument url, String userName, String domain, String password, String projectPath) { this(goCipher); this.url = url; this.userName = userName; this.domain = domain; setPassword(password); this.projectPath = projectPath; } public TfsMaterial(TfsMaterialConfig config) { this(config.getGoCipher(), config.getUrlArgument(), config.getUserName(), config.getDomain(), config.getPassword(), config.getProjectPath()); this.autoUpdate = config.getAutoUpdate(); this.filter = config.rawFilter(); this.invertFilter = config.getInvertFilter(); this.folder = config.getFolder(); this.name = config.getName(); } @Override public MaterialConfig config() { return new TfsMaterialConfig(url, userName, domain, getPassword(), projectPath, goCipher, autoUpdate, filter, invertFilter, folder, name); } public String getDomain() { return domain; } @Override public String getUserName() { return userName; } public void setPassword(String password) { resetPassword(password); } @Override public String getPassword() { try { return StringUtil.isBlank(encryptedPassword) ? null : this.goCipher.decrypt(encryptedPassword); } catch (InvalidCipherTextException e) { throw new RuntimeException("Could not decrypt the password to get the real password", e); } } @Override public String getEncryptedPassword() { return encryptedPassword; } public String getProjectPath() { return projectPath; } @Override public boolean isCheckExternals() { return false; } @Override public String getUrl() { return url == null ? null : url.forCommandline(); } @Override protected UrlArgument getUrlArgument() { return url; } public String getLongDescription() { return String.format("URL: %s, Username: %s, Domain: %s, ProjectPath: %s", url.forDisplay(), userName, domain, projectPath); } @Override protected String getLocation() { return url == null ? null : url.forDisplay(); } @Override protected void appendCriteria(Map<String, Object> parameters) { parameters.put(ScmMaterialConfig.URL, url.forCommandline()); parameters.put(ScmMaterialConfig.USERNAME, userName); parameters.put(TfsMaterialConfig.DOMAIN, domain); parameters.put(TfsMaterialConfig.PROJECT_PATH, projectPath); } @Override protected void appendAttributes(Map<String, Object> parameters) { appendCriteria(parameters); } public void updateTo(ConsoleOutputStreamConsumer outputStreamConsumer, File baseDir, RevisionContext revisionContext, final SubprocessExecutionContext execCtx) { Revision revision = revisionContext.getLatestRevision(); File workingDir = execCtx.isServer() ? baseDir : workingdir(baseDir); if (LOGGER.isDebugEnabled()) { LOGGER.debug("[TFS] Updating to revision: " + revision + " in workingdirectory " + workingDir); } outputStreamConsumer.stdOutput(format("[%s] Start updating %s at revision %s from %s", GoConstants.PRODUCT_NAME, updatingTarget(), revision.getRevision(), url)); tfs(execCtx).checkout(workingDir, revision); if (LOGGER.isDebugEnabled()) { LOGGER.debug("[TFS] done with update"); } outputStreamConsumer.stdOutput(format("[%s] Done.\n", GoConstants.PRODUCT_NAME)); } TfsCommand tfs(final SubprocessExecutionContext execCtx) { return new TfsCommandFactory().create(execCtx, url, domain, userName, getPassword(), getFingerprint(), projectPath); } public ValidationBean checkConnection(final SubprocessExecutionContext execCtx) { try { tfs(execCtx).checkConnection(); return ValidationBean.valid(); } catch (Exception e) { LOGGER.error("[TFS] Error during check connection", e); return ValidationBean.notValid(e.getMessage()); } } public List<Modification> latestModification(File workDir, final SubprocessExecutionContext execCtx) { return tfs(execCtx).latestModification(workDir); } public List<Modification> modificationsSince(File workDir, Revision revision, final SubprocessExecutionContext execCtx) { return tfs(execCtx).modificationsSince(workDir, revision); } public MaterialInstance createMaterialInstance() { return new TfsMaterialInstance(url.forCommandline(), userName, domain, projectPath, UUID.randomUUID().toString()); } public String getTypeForDisplay() { return "Tfs"; } @Override public Map<String, Object> getAttributes(boolean addSecureFields) { Map<String, Object> materialMap = new HashMap<>(); materialMap.put("type", "tfs"); Map<String, Object> configurationMap = new HashMap<>(); if (addSecureFields) { configurationMap.put("url", url.forCommandline()); } else { configurationMap.put("url", url.forDisplay()); } configurationMap.put("domain", domain); configurationMap.put("username", userName); if (addSecureFields) { configurationMap.put("password", getPassword()); } configurationMap.put("project-path", projectPath); materialMap.put("tfs-configuration", configurationMap); return materialMap; } public Class getInstanceType() { return TfsMaterialInstance.class; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } if (!super.equals(o)) { return false; } TfsMaterial material = (TfsMaterial) o; if (projectPath != null ? !projectPath.equals(material.projectPath) : material.projectPath != null) { return false; } if (url != null ? !url.equals(material.url) : material.url != null) { return false; } if (userName != null ? !userName.equals(material.userName) : material.userName != null) { return false; } if (domain != null ? !domain.equals(material.domain) : material.domain != null) { return false; } return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (url != null ? url.hashCode() : 0); result = 31 * result + (userName != null ? userName.hashCode() : 0); result = 31 * result + (domain != null ? domain.hashCode() : 0); result = 31 * result + (projectPath != null ? projectPath.hashCode() : 0); return result; } @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE, true); } @PostConstruct public void ensureEncrypted() { setPasswordIfNotBlank(password); } private void resetPassword(String passwordToSet) { if (StringUtil.isBlank(passwordToSet)) { encryptedPassword = null; } setPasswordIfNotBlank(passwordToSet); } private void setPasswordIfNotBlank(String password) { if (StringUtil.isBlank(password)) { return; } try { this.encryptedPassword = this.goCipher.encrypt(password); } catch (Exception e) { bomb("Password encryption failed. Please verify your cipher key.", e); } this.password = null; } /* Needed although there is a getUserName above */ public String getUsername() { return userName; } }