/* * Copyright 2016 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. */ package com.thoughtworks.go.config.materials.tfs; import com.thoughtworks.go.config.*; import com.thoughtworks.go.config.materials.Filter; import com.thoughtworks.go.config.materials.PasswordAwareMaterial; import com.thoughtworks.go.config.materials.ScmMaterialConfig; import com.thoughtworks.go.config.preprocessor.SkipParameterResolution; import com.thoughtworks.go.domain.ConfigErrors; import com.thoughtworks.go.security.GoCipher; import com.thoughtworks.go.util.StringUtil; import com.thoughtworks.go.util.command.UrlArgument; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.bouncycastle.crypto.InvalidCipherTextException; import javax.annotation.PostConstruct; import java.util.Map; import static com.thoughtworks.go.util.ExceptionUtils.bomb; import static java.lang.String.format; import static org.apache.commons.lang.StringUtils.isNotEmpty; @ConfigTag(value = "tfs", label = "TFS") public class TfsMaterialConfig extends ScmMaterialConfig implements ParamsAttributeAware, PasswordAwareMaterial, PasswordEncrypter { public static final String TYPE = "TfsMaterial"; @ConfigAttribute(value = "url") private UrlArgument url; @ConfigAttribute(value = "username") private String userName; @ConfigAttribute(value = "domain", optional = true) private String domain = ""; @SkipParameterResolution @ConfigAttribute(value = "password", allowNull = true) private String password; @ConfigAttribute(value = "encryptedPassword", allowNull = true) private String encryptedPassword; @ConfigAttribute(value = "projectPath") private String projectPath; private final GoCipher goCipher; public static final String PROJECT_PATH = "projectPath"; public static final String DOMAIN = "domain"; public TfsMaterialConfig() { this(new GoCipher()); } public TfsMaterialConfig(GoCipher goCipher) { super(TYPE); this.goCipher = goCipher; } public TfsMaterialConfig(GoCipher goCipher, UrlArgument url, String userName, String domain, String projectPath) { this(goCipher); this.url = url; this.userName = userName; this.domain = domain; this.projectPath = projectPath; } public TfsMaterialConfig(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 TfsMaterialConfig(UrlArgument url, String userName, String domain, String password, String projectPath, GoCipher goCipher, boolean autoUpdate, Filter filter, boolean invertFilter, String folder, CaseInsensitiveString name) { super(name, filter, invertFilter, folder, autoUpdate, TYPE, new ConfigErrors()); this.url = url; this.userName = userName; this.domain = domain; this.goCipher = goCipher; setPassword(password); this.projectPath = projectPath; } //for tests only protected TfsMaterialConfig(UrlArgument urlArgument, String password, String encryptedPassword, GoCipher goCipher) { this(goCipher); this.url = urlArgument; this.password = password; this.encryptedPassword = encryptedPassword; } public GoCipher getGoCipher() { return goCipher; } @Override public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override 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; } @Override public boolean isCheckExternals() { return false; } @Override public String getUrl() { return url != null ? url.forCommandline() : null; } @Override public void setUrl(String url) { if (url != null) { this.url = new UrlArgument(url); } } @Override protected UrlArgument getUrlArgument() { return url; } @Override 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 public void validateConcreteScmMaterial() { if (url == null || StringUtil.isBlank(url.forDisplay())) { errors().add(URL, "URL cannot be blank"); } if (StringUtil.isBlank(userName)) { errors().add(USERNAME, "Username cannot be blank"); } if (StringUtil.isBlank(projectPath)) { errors().add(PROJECT_PATH, "Project Path cannot be blank"); } if (isNotEmpty(this.password) && isNotEmpty(this.encryptedPassword)){ addError("password", "You may only specify `password` or `encrypted_password`, not both!"); addError("encryptedPassword", "You may only specify `password` or `encrypted_password`, not both!"); } if(isNotEmpty(this.encryptedPassword)) { try{ goCipher.decrypt(encryptedPassword); }catch (Exception e) { addError("encryptedPassword", format("Encrypted password value for TFS material with url '%s' is invalid. This usually happens when the cipher text is modified to have an invalid value.", this.getUriForDisplay())); } } } @Override protected void appendCriteria(Map<String, Object> parameters) { parameters.put(ScmMaterialConfig.URL, url.forCommandline()); parameters.put(ScmMaterialConfig.USERNAME, userName); parameters.put(DOMAIN, domain); parameters.put(PROJECT_PATH, projectPath); } @Override protected void appendAttributes(Map<String, Object> parameters) { appendCriteria(parameters); } @Override public String getTypeForDisplay() { return "Tfs"; } @PostConstruct @Override public void ensureEncrypted() { setPasswordIfNotBlank(password); } @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE, true); } 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; } public String getProjectPath() { return projectPath; } public void setProjectPath(String projectPath) { this.projectPath = projectPath; } public String getDomain() { return this.domain; } public void setDomain(String domain) { this.domain = domain; } public void setEncryptedPassword(String encryptedPassword) { this.encryptedPassword = encryptedPassword; } /* Needed although there is a getUserName above */ public String getUsername() { return userName; } @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; } TfsMaterialConfig material = (TfsMaterialConfig) 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 void setConfigAttributes(Object attributes) { if (attributes == null) { return; } super.setConfigAttributes(attributes); Map map = (Map) attributes; if (map.containsKey(URL)) { this.url = new UrlArgument((String) map.get(URL)); } if (map.containsKey(USERNAME)) { this.userName = (String) map.get(USERNAME); } if (map.containsKey(DOMAIN)) { this.domain = (String) map.get(DOMAIN); } if (map.containsKey(PASSWORD_CHANGED) && "1".equals(map.get(PASSWORD_CHANGED))) { String passwordToSet = (String) map.get(PASSWORD); resetPassword(passwordToSet); } if (map.containsKey(PROJECT_PATH)) { this.projectPath = (String) map.get(PROJECT_PATH); } } }