/*
* 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 com.thoughtworks.go.config.preprocessor.SkipParameterResolution;
import com.thoughtworks.go.config.remote.ConfigOrigin;
import com.thoughtworks.go.config.validation.NameTypeValidator;
import com.thoughtworks.go.domain.BaseCollection;
import com.thoughtworks.go.domain.ConfigErrors;
import com.thoughtworks.go.domain.PiplineConfigVisitor;
import org.apache.commons.lang.StringUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.thoughtworks.go.util.ExceptionUtils.bomb;
@ConfigTag("pipelines")
@ConfigCollection(PipelineConfig.class)
public class BasicPipelineConfigs extends BaseCollection<PipelineConfig> implements PipelineConfigs, Serializable {
@ConfigAttribute(value = "group", optional = true) @SkipParameterResolution
private String group;
@ConfigSubtag @SkipParameterResolution
private Authorization authorization = new Authorization();
private ConfigOrigin configOrigin;
private final ConfigErrors configErrors = new ConfigErrors();
public BasicPipelineConfigs() {
}
public BasicPipelineConfigs(ConfigOrigin configOrigin) {
this.configOrigin = configOrigin;
}
public BasicPipelineConfigs(PipelineConfig... pipelineConfigs) {
this(new Authorization(), pipelineConfigs);
}
public BasicPipelineConfigs(Authorization authorization, PipelineConfig... pipelineConfigs) {
super(pipelineConfigs);
this.authorization = authorization;
}
public BasicPipelineConfigs(String group, Authorization authorization, PipelineConfig... pipelineConfigs) {
super(pipelineConfigs);
this.group = group;
this.authorization = authorization;
}
@Override
public boolean contains(PipelineConfig pipelineConfig) {
return super.contains(pipelineConfig);
}
@Override
public ConfigOrigin getOrigin() {
return configOrigin;
}
@Override
public void setOrigins(ConfigOrigin origins) {
this.configOrigin = origins;
for(PipelineConfig pipe : this)
{
pipe.setOrigins(origins);
}
this.authorization.setOrigins(origins);
}
@Override
public PipelineConfig findBy(final CaseInsensitiveString pipelineName) {
for (int i=0; i< this.size(); i++) {
PipelineConfig pipelineConfig = this.get(i);
if (pipelineConfig.name().equals(pipelineName)) {
return pipelineConfig;
}
}
return null;
}
@Override
public boolean add(PipelineConfig pipelineConfig) {
verifyUniqueName(pipelineConfig);
return addWithoutValidation(pipelineConfig);
}
@Override
public boolean addWithoutValidation(PipelineConfig pipelineConfig) {
return super.add(pipelineConfig);
}
@Override
public PipelineConfig set(int index, PipelineConfig pipelineConfig) {
verifyUniqueName(pipelineConfig, index);
return super.set(index, pipelineConfig);
}
@Override
public void addToTop(PipelineConfig pipelineConfig) {
this.add(0, pipelineConfig);
}
@Override
public void add(int index, PipelineConfig pipelineConfig) {
verifyUniqueName(pipelineConfig);
super.add(index, pipelineConfig);
}
private void verifyUniqueName(PipelineConfig pipelineConfig) {
if (alreadyContains(pipelineConfig)) {
throw bomb("You have defined multiple pipelines called '" + pipelineConfig.name() + "'. Pipeline names must be unique.");
}
}
private void verifyUniqueName(PipelineConfig pipelineConfig, int index) {
if (pipelineConfig.name().equals(super.get(index).name())) {
return;
}
verifyUniqueName(pipelineConfig);
}
private boolean alreadyContains(PipelineConfig pipelineConfig) {
return findBy(pipelineConfig.name()) != null;
}
@Override
public String getGroup() {
return group;
}
@Override
public void setGroup(String group) {
this.group = sanitizedGroupName(group);
}
public static String sanitizedGroupName(String group) {
return StringUtils.isBlank(group) ? DEFAULT_GROUP : group;
}
@Override
public boolean isNamed(String groupName) {
return isSameGroup(groupName);
}
@Override
public void update(String groupName, PipelineConfig pipeline, String pipelineName) {
if (!isSameGroup(groupName)) {
return;
}
this.set(getIndex(pipelineName), pipeline);
}
private int getIndex(String pipelineName) {
return this.indexOf(this.findBy(new CaseInsensitiveString(pipelineName)));
}
@Override
public boolean save(PipelineConfig pipeline, String groupName) {
if (isSameGroup(groupName)) {
this.addToTop(pipeline);
return true;
} else {
return false;
}
}
private boolean isSameGroup(String groupName) {
return StringUtils.equals(groupName, this.getGroup());
}
@Override
public void add(List<String> allGroup) {
allGroup.add(group);
}
@Override
public boolean exist(int pipelineIndex) {
return pipelineIndex < this.size();
}
@Override
public boolean hasPipeline(CaseInsensitiveString pipelineName) {
for (PipelineConfig pipelineConfig : this) {
if (pipelineConfig.name().equals(pipelineName)) {
return true;
}
}
return false;
}
@Override
public Authorization getAuthorization() {
return this.authorization;
}
@Override
public void accept(PiplineConfigVisitor visitor) {
for (PipelineConfig pipelineConfig : this) {
visitor.visit(pipelineConfig);
}
}
@Override
public void setAuthorization(Authorization authorization) {
this.authorization = authorization;
}
@Override
public boolean hasViewPermission(final CaseInsensitiveString username, UserRoleMatcher userRoleMatcher) {
return !hasAuthorizationDefined() || authorization.hasViewPermission(username, userRoleMatcher);
}
@Override
public boolean hasViewPermissionDefined() {
return authorization.hasViewPermissionDefined();
}
@Override
public boolean hasOperationPermissionDefined() {
return authorization.hasOperationPermissionDefined();
}
@Override
public boolean hasOperatePermission(final CaseInsensitiveString username, UserRoleMatcher userRoleMatcher) {
return !hasAuthorizationDefined() || authorization.hasOperatePermission(username, userRoleMatcher);
}
@Override
public boolean hasAuthorizationDefined() {
return !authorization.equals(new Authorization());
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
BasicPipelineConfigs pipelines = (BasicPipelineConfigs) o;
if (authorization != null ? !authorization.equals(pipelines.authorization) : pipelines.authorization != null) {
return false;
}
if (group != null ? !group.equals(pipelines.group) : pipelines.group != null) {
return false;
}
return true;
}
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (group != null ? group.hashCode() : 0);
result = 31 * result + (authorization != null ? authorization.hashCode() : 0);
return result;
}
@Override
public boolean hasTemplate() {
for (PipelineConfig pipelineConfig : this) {
if (pipelineConfig.hasTemplate()) { return true; }
}
return false;
}
@Override
public PipelineConfigs getCopyForEditing() {
BasicPipelineConfigs clone = (BasicPipelineConfigs) clone();
clone.clear();
for (PipelineConfig pipeline : this) {
clone.add(pipeline.getCopyForEditing());
}
return clone;
}
@Override
public boolean isUserAnAdmin(final CaseInsensitiveString userName, List<Role> memberRoles) {
return authorization.hasAdminsDefined() && authorization.isUserAnAdmin(userName, memberRoles);
}
@Override
public void validate(ValidationContext validationContext) {
this.validateGroupNameAndAddErrorsTo(this.configErrors);
if(this.configOrigin != null && //when there is no origin specified we should not check it at all
!(this.configOrigin.isLocal()) &&
this.hasAuthorizationDefined())
{
this.configErrors.add(NO_REMOTE_AUTHORIZATION,
"Authorization can be defined only in configuration file");
}
verifyPipelineNameUniqueness();
}
private void verifyPipelineNameUniqueness() {
HashMap<CaseInsensitiveString, PipelineConfig> hashMap = new HashMap<>();
for(PipelineConfig pipelineConfig : this){
pipelineConfig.validateNameUniqueness(hashMap);
}
}
@Override
public void validateNameUniqueness(Map<String, PipelineConfigs> groupNameMap) {
String currentName = sanitizedGroupName(group).toLowerCase();
PipelineConfigs groupWithSameName = groupNameMap.get(currentName);
if (groupWithSameName == null) {
groupNameMap.put(currentName, this);
} else {
groupWithSameName.addError(GROUP, createNameConflictError());
this.nameConflictError();
}
}
private void nameConflictError() {
this.configErrors.add(GROUP, createNameConflictError());
}
private String createNameConflictError() {
return String.format("Group with name '%s' already exists", group);
}
@Override
public ConfigErrors errors() {
return configErrors;
}
@Override
public List<PipelineConfig> getPipelines() {
return this;
}
@Override
public void addError(String fieldName, String message) {
configErrors.add(fieldName, message);
}
@Override
public List<AdminUser> getOperateUsers() {
return authorization.getOperationConfig().getUsers();
}
@Override
public List<AdminRole> getOperateRoles() {
return authorization.getOperationConfig().getRoles();
}
@Override
public List<String> getOperateRoleNames() {
List<String> roles = new ArrayList<>();
for (AdminRole role : getOperateRoles()) {
roles.add(CaseInsensitiveString.str(role.getName()));
}
return roles;
}
@Override
public List<String> getOperateUserNames() {
List<String> users = new ArrayList<>();
for (AdminUser user : getOperateUsers()) {
users.add(CaseInsensitiveString.str(user.getName()));
}
return users;
}
@Override
public void setConfigAttributes(Object attributes) {
Map attributeMap = (Map) attributes;
if (attributeMap == null) {
return;
}
if (attributeMap.containsKey(GROUP)) {
this.group = (String) attributeMap.get(GROUP);
}
if (attributeMap.containsKey(AUTHORIZATION)) {
this.authorization = new Authorization();
this.authorization.setConfigAttributes(attributeMap.get(AUTHORIZATION));
}
else {
this.authorization = new Authorization();
}
}
@Override
public void cleanupAllUsagesOfRole(Role roleToDelete) {
getAuthorization().removeAllUsagesOfRole(roleToDelete);
for (PipelineConfig pipelineConfig : this){
pipelineConfig.cleanupAllUsagesOfRole(roleToDelete);
}
}
@Override
public int indexOf(PipelineConfig pipelineConfig) {
return super.indexOf(pipelineConfig);
}
@Override
public void remove(PipelineConfig pipelineConfig) {
super.remove(pipelineConfig);
}
@Override
public PipelineConfig remove(int i) {
return super.remove(i);
}
@Override
public void validateGroupNameAndAddErrorsTo(ConfigErrors errors) {
if (StringUtils.isBlank(group) || new NameTypeValidator().isNameInvalid(group)) {
String errorText = NameTypeValidator.errorMessage("group", group);
errors.add(GROUP, errorText);
}
}
public PipelineConfigs getLocal() {
if(this.isLocal())
return this;
return null;
}
@Override
public boolean isLocal() {
return getOrigin() == null || getOrigin().isLocal();
}
public void setOrigin(ConfigOrigin origin) {
this.configOrigin = origin;
}
@Override
public boolean hasRemoteParts() {
return getOrigin() != null && !getOrigin().isLocal();
}
}