/*
* 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.server.service;
import com.thoughtworks.go.config.*;
import com.thoughtworks.go.domain.ServerSiteUrlConfig;
import com.thoughtworks.go.domain.materials.ValidationBean;
import com.thoughtworks.go.i18n.LocalizedMessage;
import com.thoughtworks.go.security.GoCipher;
import com.thoughtworks.go.server.controller.beans.GoMailSenderProvider;
import com.thoughtworks.go.server.security.LdapContextSourceConfigurator;
import com.thoughtworks.go.server.security.LdapUserSearch;
import com.thoughtworks.go.server.service.result.DefaultLocalizedResult;
import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult;
import com.thoughtworks.go.server.service.result.LocalizedOperationResult;
import com.thoughtworks.go.server.service.result.LocalizedResult;
import com.thoughtworks.go.server.web.BaseUrlProvider;
import com.thoughtworks.go.validators.HostNameValidator;
import com.thoughtworks.go.validators.PortValidator;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.stereotype.Service;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import static com.thoughtworks.go.util.ExceptionUtils.bomb;
import static com.thoughtworks.go.util.GoConstants.TEST_EMAIL_SUBJECT;
@Service
public class ServerConfigService implements BaseUrlProvider {
private GoConfigService goConfigService;
private UserService userService;
private GoMailSenderProvider provider = GoMailSenderProvider.DEFAULT_PROVIDER;
static final String ANY_USER = "GO_TEST_USER";
@Autowired
public ServerConfigService(GoConfigService goConfigService, UserService userService) {
this.goConfigService = goConfigService;
this.userService = userService;
}
public void updateServerConfig(MailHost mailHost, LdapConfig ldapConfig, PasswordFileConfig passwordFileConfig, String artifactsDir,
Double purgeStart, Double purgeUpto, String jobTimeout, boolean shouldAllowAutoLogin, String siteUrl, String secureSiteUrl,
String taskRepositoryLocation, final HttpLocalizedOperationResult result, final String md5) {
if (!mailHost.equals(new MailHost(new GoCipher()))) {
validate(mailHost, result);
}
if (!shouldAllowAutoLogin && !userService.canUserTurnOffAutoLogin()) {
result.notAcceptable(LocalizedMessage.string("CANNOT_TURN_OFF_AUTO_LOGIN"));
return;
}
if (result.isSuccessful()) {
try {
ConfigSaveState configSaveState = goConfigService.updateServerConfig(mailHost, ldapConfig, passwordFileConfig, shouldAllowAutoLogin, md5, artifactsDir, purgeStart,
purgeUpto, jobTimeout, siteUrl,
secureSiteUrl, taskRepositoryLocation);
if (ConfigSaveState.MERGED.equals(configSaveState)) {
result.setMessage(LocalizedMessage.composite(LocalizedMessage.string("SAVED_CONFIGURATION_SUCCESSFULLY"), LocalizedMessage.string("CONFIG_MERGED")));
} else if (ConfigSaveState.UPDATED.equals(configSaveState)) {
result.setMessage(LocalizedMessage.string("SAVED_CONFIGURATION_SUCCESSFULLY"));
}
} catch (RuntimeException exception) {
updateFailed(exception.getMessage(), result);
}
}
}
private void updateFailed(String description, HttpLocalizedOperationResult result) {
result.badRequest(LocalizedMessage.string("FAILED_TO_SAVE_THE_SERVER_CONFIGURATION", description));
}
private void validate(MailHost mailHost, LocalizedOperationResult operationResult) {
DefaultLocalizedResult result = (DefaultLocalizedResult) validateHostName(mailHost.getHostName());
if (!result.isSuccessful()) {
operationResult.notAcceptable(result.localizable());
}
result = (DefaultLocalizedResult) validatePort(mailHost.getPort());
if (!result.isSuccessful()) {
operationResult.notAcceptable(result.localizable());
}
result = (DefaultLocalizedResult) validateEmail(mailHost.getFrom());
if (!result.isSuccessful()) {
operationResult.notAcceptable(LocalizedMessage.string("INVALID_FROM_ADDRESS", mailHost.getFrom()));
}
result = (DefaultLocalizedResult) validateEmail(mailHost.getAdminMail());
if (!result.isSuccessful()) {
operationResult.notAcceptable(LocalizedMessage.string("INVALID_ADMIN_ADDRESS", mailHost.getAdminMail()));
}
}
public LocalizedResult validateHostName(String hostName) {
DefaultLocalizedResult result = new DefaultLocalizedResult();
new HostNameValidator().validate(hostName, result);
return result;
}
public LocalizedResult validatePort(int port) {
DefaultLocalizedResult result = new DefaultLocalizedResult();
new PortValidator().validate(port, result);
return result;
}
public LocalizedResult validateEmail(String email) {
DefaultLocalizedResult result = new DefaultLocalizedResult();
try {
new InternetAddress(email, true);
} catch (AddressException e) {
result.invalid("INVALID_EMAIL", email);
}
return result;
}
public void sendTestMail(MailHost mailHost, LocalizedOperationResult result) {
validate(mailHost, result);
if (!result.isSuccessful()) {
return;
}
GoMailSender sender = provider.createSender(mailHost);
ValidationBean validationBean = sender.send(TEST_EMAIL_SUBJECT, GoSmtpMailSender.emailBody(), mailHost.getAdminMail());
if (!validationBean.isValid()) {
result.notAcceptable(LocalizedMessage.string("FAILED_TO_SEND_TEST_MAIL", validationBean.getError()));
}
}
public void validateLdapSettings(LdapConfig ldapConfig, HttpLocalizedOperationResult result) {
try {
new LdapUserSearch(goConfigService, ldapContextSource(ldapConfig)).search(ANY_USER, ldapConfig);
} catch (LdapUserSearch.NotAllResultsShownException ex) {
// Connected to ldap sucessfully. Dont care about results.
} catch (RuntimeException e) {
result.connectionError(LocalizedMessage.string("CANNOT_CONNECT_TO_LDAP", e.getMessage()));
}
}
DefaultSpringSecurityContextSource ldapContextSource(LdapConfig ldapConfig) {
DefaultSpringSecurityContextSource source = new DefaultSpringSecurityContextSource(ldapConfig.uri());
//so user can define the variable java.naming.referral=follow in the server.sh
source.setBaseEnvironmentProperties(System.getProperties());
new LdapContextSourceConfigurator(ldapConfig).configure(source);
try {
source.afterPropertiesSet();
} catch (Exception e) {
bomb("Cannot create ldap context", e);
}
return source;
}
public String siteUrlFor(String url, boolean forceSsl) throws URISyntaxException {
String scheme = new URI(url).getScheme();
ServerSiteUrlConfig siteUrl = forceSsl || (scheme != null && scheme.equals("https")) ? getSecureSiteUrl() : serverConfig().getSiteUrl();
return siteUrl.siteUrlFor(url, false);
}
private ServerSiteUrlConfig getSecureSiteUrl() {
return serverConfig().getHttpsUrl();
}
private ServerConfig serverConfig() {
return goConfigService.getCurrentConfig().server();
}
public String getAutoregisterKey(){
return serverConfig().getAgentAutoRegisterKey();
}
public boolean hasAutoregisterKey() {
return StringUtils.isNotBlank(getAutoregisterKey());
}
public boolean hasAnyUrlConfigured() {
return serverConfig().hasAnyUrlConfigured();
}
public Long elasticJobStarvationThreshold() {
return serverConfig().getElasticConfig().getJobStarvationTimeout();
}
}