/*************************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;
import javax.annotation.PostConstruct;
import com.thoughtworks.go.config.server.security.ldap.BaseConfig;
import com.thoughtworks.go.config.server.security.ldap.BasesConfig;
import com.thoughtworks.go.domain.ConfigErrors;
import com.thoughtworks.go.security.GoCipher;
import com.thoughtworks.go.util.StringUtil;
import org.bouncycastle.crypto.InvalidCipherTextException;
import static com.thoughtworks.go.util.ExceptionUtils.bomb;
import static com.thoughtworks.go.util.StringUtil.nullToBlank;
@ConfigTag("ldap")
public class LdapConfig implements Validatable, PasswordEncrypter{
@ConfigAttribute(value = "uri") private String uri = "";
@ConfigAttribute(value = "managerDn") private String managerDn = "";
@ConfigAttribute(value = "managerPassword") private String managerPassword = "";
@ConfigAttribute(value = "encryptedManagerPassword", allowNull = true) private String encryptedManagerPassword = null;
@ConfigAttribute(value = "searchFilter") private String searchFilter = "";
@ConfigSubtag(optional = false) private BasesConfig basesConfig = new BasesConfig();
private ConfigErrors errors = new ConfigErrors();
private boolean passwordChanged;
private final GoCipher goCipher;
private static final LdapConfig EMPTY_LDAP_CONFIG = new LdapConfig(new GoCipher());
public LdapConfig(GoCipher goCipher) {
this.goCipher = goCipher;
}
public LdapConfig(String uri, String managerDn, String managerPassword, String encryptedManagerPassword, boolean passwordChanged, BasesConfig basesConfig, String searchFilter) {
this(new GoCipher());
this.uri = nullToBlank(uri);
this.managerDn = nullToBlank(managerDn);
this.encryptedManagerPassword = encryptedManagerPassword;
this.passwordChanged = passwordChanged;
if (passwordChanged) {
resetPassword(managerPassword);
this.managerPassword = nullToBlank(managerPassword); // 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.basesConfig = basesConfig;
this.searchFilter = nullToBlank(searchFilter);
}
public String uri() {
return uri;
}
public String managerDn() {
return managerDn;
}
public String managerPassword() {
return currentManagerPassword();
}
public BasesConfig searchBases() {
return basesConfig;
}
public String searchFilter() {
return searchFilter;
}
public String getEncryptedManagerPassword() {
return encryptedManagerPassword;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LdapConfig that = (LdapConfig) o;
if (basesConfig != null ? !basesConfig.equals(that.basesConfig) : that.basesConfig != null) {
return false;
}
if (encryptedManagerPassword != null ? !encryptedManagerPassword.equals(that.encryptedManagerPassword) : that.encryptedManagerPassword != null) {
return false;
}
if (managerDn != null ? !managerDn.equals(that.managerDn) : that.managerDn != null) {
return false;
}
if (searchFilter != null ? !searchFilter.equals(that.searchFilter) : that.searchFilter != null) {
return false;
}
if (uri != null ? !uri.equals(that.uri) : that.uri != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = uri != null ? uri.hashCode() : 0;
result = 31 * result + (managerDn != null ? managerDn.hashCode() : 0);
result = 31 * result + (encryptedManagerPassword != null ? encryptedManagerPassword.hashCode() : 0);
result = 31 * result + (searchFilter != null ? searchFilter.hashCode() : 0);
result = 31 * result + (basesConfig != null ? basesConfig.hashCode() : 0);
return result;
}
public boolean isEnabled() {
return !equals(EMPTY_LDAP_CONFIG);
}
public void validate(ValidationContext validationContext) {
if (isEnabled()) {
basesConfig.validateBases();
for (BaseConfig baseConfig : basesConfig) {
baseConfig.validateBase();
}
}
}
public ConfigErrors errors() {
return errors;
}
public void addError(String fieldName, String message) {
errors.add(fieldName, message);
}
@PostConstruct
public void ensureEncrypted() {
setPasswordIfNotBlank(managerPassword);
}
public void updateWithNew(LdapConfig newLdapConfig) {
if (newLdapConfig.passwordChanged) {
this.encryptedManagerPassword = newLdapConfig.encryptedManagerPassword;
}
this.uri = newLdapConfig.uri;
this.managerDn = newLdapConfig.managerDn;
this.basesConfig = newLdapConfig.getBasesConfig();
this.searchFilter = newLdapConfig.searchFilter;
}
private void resetPassword(String password) {
if (StringUtil.isBlank(password)) {
this.encryptedManagerPassword = null;
}
setPasswordIfNotBlank(password);
}
private void setPasswordIfNotBlank(String password) {
if (StringUtil.isBlank(password)) {
return;
}
try {
this.encryptedManagerPassword = this.goCipher.encrypt(password);
} catch (Exception e) {
bomb("Password encryption failed. Please verify your cipher key.", e);
}
this.managerPassword = "";
}
public String currentManagerPassword() {
try {
return StringUtil.isBlank(encryptedManagerPassword) ? "" : this.goCipher.decrypt(encryptedManagerPassword);
} catch (InvalidCipherTextException e) {
throw new RuntimeException("Could not decrypt the password to get the real password", e);
}
}
public boolean isPasswordChanged() {
return passwordChanged;
}
public BasesConfig getBasesConfig() {
return basesConfig;
}
}