/* * Copyright 2017 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.perforce; 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 javax.annotation.PostConstruct; import java.util.Map; import static com.thoughtworks.go.util.ExceptionUtils.bomb; import static com.thoughtworks.go.util.ExceptionUtils.bombIfNull; import static java.lang.String.format; import static org.apache.commons.lang.StringUtils.isNotEmpty; @ConfigTag(value = "p4", label = "Perforce") public class P4MaterialConfig extends ScmMaterialConfig implements ParamsAttributeAware, PasswordEncrypter, PasswordAwareMaterial { @ConfigAttribute(value = "port") private String serverAndPort; @ConfigAttribute(value = "username", allowNull = true) private String userName; @SkipParameterResolution @ConfigAttribute(value = "password", allowNull = true) private String password; @ConfigAttribute(value = "encryptedPassword", allowNull = true) private String encryptedPassword; @ConfigAttribute(value = "useTickets") private Boolean useTickets = false; @ConfigSubtag(optional = false) private P4MaterialViewConfig view; public static final String TYPE = "P4Material"; public static final String USERNAME = "userName"; public static final String VIEW = "view"; public static final String SERVER_AND_PORT = "serverAndPort"; public static final String USE_TICKETS = "useTickets"; private final GoCipher goCipher; public P4MaterialConfig() { this(new GoCipher()); } private P4MaterialConfig(GoCipher goCipher) { super(TYPE); this.goCipher = goCipher; } public P4MaterialConfig(String serverAndPort, String view, GoCipher goCipher) { this(goCipher); bombIfNull(serverAndPort, "null serverAndPort"); this.serverAndPort = serverAndPort; setView(view); } public P4MaterialConfig(String serverAndPort, String view) { this(serverAndPort, view, new GoCipher()); } public P4MaterialConfig(String url, String view, String userName) { this(url, view); this.userName = userName; } public P4MaterialConfig(String serverAndPort, String userName, String password, Boolean useTickets, String viewStr, GoCipher goCipher, CaseInsensitiveString name, boolean autoUpdate, Filter filter, boolean invertFilter, String folder) { super(name, filter, invertFilter, folder, autoUpdate, TYPE, new ConfigErrors()); this.serverAndPort = serverAndPort; this.goCipher = goCipher; setPassword(password); this.userName = userName; this.useTickets = useTickets; setView(viewStr); } //for tests only protected P4MaterialConfig(String serverAndPort, String password, String encryptedPassword, GoCipher goCipher) { this(goCipher); this.password = password; this.encryptedPassword = encryptedPassword; this.serverAndPort = serverAndPort; } public GoCipher getGoCipher() { return goCipher; } @Override protected void appendCriteria(Map<String, Object> parameters) { parameters.put(ScmMaterialConfig.URL, serverAndPort); parameters.put(ScmMaterialConfig.USERNAME, userName); parameters.put("view", view.getValue()); } @Override protected void appendAttributes(Map<String, Object> parameters) { appendCriteria(parameters); } public String getServerAndPort() { return serverAndPort; } public void setServerAndPort(String serverAndPort) { this.serverAndPort = serverAndPort; } public String getView() { return view == null ? null : view.getValue(); } //USED IN RSPEC TESTS public P4MaterialViewConfig getP4MaterialView() { return view; } //USED IN RSPEC TESTS public void setP4MaterialView(P4MaterialViewConfig view) { resetCachedIdentityAttributes(); this.view = view; } @Override public boolean isCheckExternals() { return false; } @Override public String getUrl() { return serverAndPort; } @Override public void setUrl(String serverAndPort) { this.serverAndPort = serverAndPort; } @Override protected UrlArgument getUrlArgument() { return new UrlArgument(serverAndPort); } @Override public String getLongDescription() { return String.format("URL: %s, View: %s, Username: %s", serverAndPort, view.getValue(), userName); } @Override public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override public String getPassword() { return currentPassword(); } @Override public void setPassword(String password) { resetPassword(password); } @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; } P4MaterialConfig that = (P4MaterialConfig) o; if (serverAndPort != null ? !serverAndPort.equals(that.serverAndPort) : that.serverAndPort != null) { return false; } if (useTickets != null ? !useTickets.equals(that.useTickets) : that.useTickets != null) { return false; } if (userName != null ? !userName.equals(that.userName) : that.userName != null) { return false; } if (view != null ? !view.equals(that.view) : that.view != null) { return false; } return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (serverAndPort != null ? serverAndPort.hashCode() : 0); result = 31 * result + (userName != null ? userName.hashCode() : 0); result = 31 * result + (useTickets != null ? useTickets.hashCode() : 0); result = 31 * result + (view != null ? view.hashCode() : 0); return result; } @Override public void validateConcreteScmMaterial() { if (getView() == null || getView().trim().isEmpty()) { errors.add(VIEW, "P4 view cannot be empty."); } if (StringUtil.isBlank(getServerAndPort())) { errors.add(SERVER_AND_PORT, "P4 port cannot be empty."); } 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{ currentPassword(); }catch (Exception e) { addError("encryptedPassword", format("Encrypted password value for P4 material with serverAndPort '%s' is invalid. This usually happens when the cipher text is modified to have an invalid value.", this.getServerAndPort())); } } } @Override protected String getLocation() { return getServerAndPort(); } @Override public String getTypeForDisplay() { return "Perforce"; } @Override public String toString() { return "P4MaterialConfig{" + "serverAndPort='" + serverAndPort + '\'' + ", userName='" + userName + '\'' + ", view=" + view.getValue() + '}'; } @Override public void setConfigAttributes(Object attributes) { if (attributes == null) { return; } super.setConfigAttributes(attributes); Map map = (Map) attributes; if (map.containsKey(SERVER_AND_PORT)) { this.serverAndPort = (String) map.get(SERVER_AND_PORT); } if (map.containsKey(VIEW)) { setView((String) map.get(VIEW)); } if (map.containsKey(USERNAME)) { this.userName = (String) map.get(USERNAME); } if (map.containsKey(PASSWORD_CHANGED) && "1".equals(map.get(PASSWORD_CHANGED))) { String passwordToSet = (String) map.get(PASSWORD); resetPassword(passwordToSet); } setUseTickets("true".equals(map.get(USE_TICKETS))); } public void setView(String viewStr) { this.view = new P4MaterialViewConfig(viewStr); } private void resetPassword(String password) { if (StringUtil.isBlank(password)) { this.encryptedPassword = null; } setPasswordIfNotBlank(password); } 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; } @PostConstruct public void ensureEncrypted() { setPasswordIfNotBlank(password); } public String currentPassword() { try { return StringUtil.isBlank(encryptedPassword) ? null : this.goCipher.decrypt(encryptedPassword); } catch (Exception e) { throw new RuntimeException("Could not decrypt the password to get the real password", e); } } @Override public String getEncryptedPassword() { return encryptedPassword; } public void setEncryptedPassword(String encryptedPassword) { this.encryptedPassword = encryptedPassword; } private String p4RepoId() { return hasUser() ? userName + "@" + serverAndPort : serverAndPort; } private boolean hasUser() { return userName != null && !userName.trim().isEmpty(); } public boolean getUseTickets() { return this.useTickets; } public void setUseTickets(boolean useTickets) { this.useTickets = useTickets; } @Override public String getFolder() { return folder; } }