/*
* Copyright 2015 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;
import javax.annotation.PostConstruct;
import com.thoughtworks.go.domain.ConfigErrors;
import com.thoughtworks.go.security.GoCipher;
import com.thoughtworks.go.util.StringUtil;
import org.bouncycastle.crypto.InvalidCipherTextException;
import java.util.LinkedHashMap;
import java.util.Map;
import static com.thoughtworks.go.util.ExceptionUtils.bomb;
import static java.lang.String.format;
import static java.lang.String.valueOf;
import static org.apache.commons.lang.StringUtils.defaultString;
@ConfigTag("mailhost")
public class MailHost implements Validatable, PasswordEncrypter {
@ConfigAttribute(value = "hostname", optional = false) private String hostName;
@ConfigAttribute(value = "port", optional = false) private int port;
@ConfigAttribute(value = "username", optional = true, allowNull = true) private String username = "";
@ConfigAttribute(value = "password", optional = true, allowNull = true) private String password = "";//should never be used, will be converted to encrypted password on load, is used by magical-loader
@ConfigAttribute(value = "encryptedPassword", optional = true, allowNull = true) private String encryptedPassword = null;
@ConfigAttribute(value = "tls", optional = false) private Boolean tls;
@ConfigAttribute(value = "from", optional = false) private String from;
@ConfigAttribute(value = "admin", optional = false) private String adminMail;
private final ConfigErrors configErrors = new ConfigErrors();
private final GoCipher goCipher;
private boolean passwordChanged = false;
public MailHost(String hostName, int port, String username, String password, boolean passwordChanged, boolean tls, String from, String adminMail) {
this(hostName, port, username, password, null, passwordChanged, tls, from, adminMail, new GoCipher());
}
public MailHost(String hostName, int port, String username, String password, String encryptedPassword, boolean passwordChanged, boolean tls, String from, String adminMail, GoCipher goCipher) {
this(goCipher);
this.hostName = hostName;
this.port = port;
this.username = username;
this.encryptedPassword = encryptedPassword;
if (passwordChanged) {
resetPassword(password);
this.password = password; // This is for the double save that happens through rails. We do not want to flip passwordChanged after encrypting as it modifies model state unknown to the user
}
this.passwordChanged = passwordChanged;
this.tls = tls;
this.from = from;
this.adminMail = adminMail;
}
public MailHost(String hostName, int port, String username, String password, String encryptedPassword, boolean passwordChanged, boolean tls, String from, String adminMail) {
this(hostName, port, username, password, encryptedPassword, passwordChanged, tls, from, adminMail, new GoCipher());
}
public MailHost(GoCipher goCipher) {
this.goCipher = goCipher;
}
public void validate(ValidationContext validationContext) {
}
public ConfigErrors errors() {
return configErrors;
}
public void addError(String fieldName, String message) {
configErrors.add(fieldName, message);
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MailHost mailHost = (MailHost) o;
if (port != mailHost.port) {
return false;
}
if (tls != mailHost.tls) {
return false;
}
if (getAdminMail() != null ? !getAdminMail().equals(
mailHost.getAdminMail()) : mailHost.getAdminMail() != null) {
return false;
}
if (getFrom() != null ? !getFrom().equals(mailHost.getFrom()) : mailHost.getFrom() != null) {
return false;
}
if (hostName != null ? !hostName.equals(mailHost.hostName) : mailHost.hostName != null) {
return false;
}
if (username != null ? !username.equals(mailHost.username) : mailHost.username != null) {
return false;
}
return true;
}
public int hashCode() {
int result;
result = (hostName != null ? hostName.hashCode() : 0);
result = 31 * result + port;
result = 31 * result + (username != null ? username.hashCode() : 0);
result = 31 * result + (Boolean.TRUE.equals(tls) ? 1 : 0);
result = 31 * result + (getFrom() != null ? getFrom().hashCode() : 0);
result = 31 * result + (getAdminMail() != null ? getAdminMail().hashCode() : 0);
return result;
}
public String toString() {
return format("MailHost[%s, %s, %s, %s, %s, %s, %s]", hostName, port, username, password, encryptedPassword, tls, getFrom(), getAdminMail());
}
public Map json() {
Map<String, Object> model = new LinkedHashMap<>();
model.put("hostName", defaultString(hostName));
model.put("port", port == 0 ? "" : valueOf(port));
model.put("username", defaultString(username));
model.put("password", defaultString(getPassword()));
model.put("tls", tls == null ? "false" : tls.toString());
model.put("from", defaultString(getFrom()));
model.put("adminMail", defaultString(getAdminMail()));
return model;
}
public String getFrom() {
return from;
}
public String getAdminMail() {
return adminMail;
}
public String getHostName() {
return hostName;
}
public String getUserName() {
return username;
}
public String getPassword() {
return getCurrentPassword();
}
public int getPort() {
return port;
}
public Boolean getTls() {
return tls;
}
@Deprecated // Only for test
public void setAdminMail(String adminMail) {
this.adminMail = adminMail;
}
@PostConstruct
public void ensureEncrypted() {
setPasswordIfNotBlank(password);
}
public String getEncryptedPassword() {
return encryptedPassword;
}
public boolean isPasswordChanged() {
return passwordChanged;
}
public void updateWithNew(MailHost newMailHost) {//TODO: #5086 kill passing mailhost around, pass arguments instead
if (newMailHost.isPasswordChanged()) {
resetPassword(newMailHost.password);
}
this.hostName = newMailHost.hostName;
this.port = newMailHost.port;
this.username = newMailHost.username;
this.tls = newMailHost.tls;
this.from = newMailHost.from;
this.adminMail = newMailHost.adminMail;
}
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 = "";
}
public String getCurrentPassword() {
try {
return StringUtil.isBlank(encryptedPassword) ? "" : this.goCipher.decrypt(encryptedPassword);
} catch (InvalidCipherTextException e) {
throw new RuntimeException(e);
}
}
}