/*
* 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 java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.thoughtworks.go.config.preprocessor.SkipParameterResolution;
import com.thoughtworks.go.domain.ConfigErrors;
import com.thoughtworks.go.domain.config.Admin;
import com.thoughtworks.go.util.StringUtil;
@ConfigTag(value = "approval")
//TODO: ChrisS: Make this a proper enumeration
public class Approval implements Validatable, ParamsAttributeAware {
@ConfigSubtag(optional = true)
private AuthConfig authConfig = new AuthConfig();
@ConfigAttribute(value = "type", optional = false, alwaysWrite = true)
@SkipParameterResolution
private String type = SUCCESS;
public static final String MANUAL = "manual";
public static final String SUCCESS = "success";
public static final String TYPE = "type";
private ConfigErrors errors = new ConfigErrors();
public static final Approval automaticApproval() {
return new Approval(SUCCESS);
}
public static final Approval manualApproval() {
return new Approval(MANUAL);
}
public Approval() {
this(MANUAL);
}
private Approval(String type) {
this.type = type;
}
//for test
public Approval(AuthConfig authConfig) {
this(MANUAL);
this.authConfig = authConfig;
}
public boolean isManual() {
return type.equals(MANUAL);
}
public AuthConfig getAuthConfig() {
return authConfig;
}
public void setAuthConfig(AuthConfig authConfig) {
this.authConfig = authConfig;
}
public boolean isAuthorizationDefined() {
return !this.authConfig.isEmpty();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Approval approval = (Approval) o;
if (!authConfig.equals(approval.authConfig)) {
return false;
}
if (!type.equals(approval.type)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = authConfig.hashCode();
result = 31 * result + type.hashCode();
return result;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type= type;
}
public boolean validateTree(ValidationContext validationContext) {
validate(validationContext);
boolean isValid = errors.isEmpty();
for (Admin admin : authConfig) {
admin.validate(validationContext);
authConfig.errors().addAll(admin.errors());
isValid = admin.errors().isEmpty() && isValid;
}
return isValid;
}
public void validate(ValidationContext validationContext) {
if (!isValidTypeValue()) {
errors.add(TYPE, String.format("You have defined approval type as '%s'. Approval can only be of the type '%s' or '%s'.", type, MANUAL, SUCCESS));
}
if (validationContext.isWithinPipelines()) {
PipelineConfigs group = validationContext.getPipelineGroup();
if (!group.hasOperationPermissionDefined()) {
return;
}
AdminsConfig groupOperators = group.getAuthorization().getOperationConfig();
SecurityConfig serverSecurityConfig = validationContext.getServerSecurityConfig();
RolesConfig roles = serverSecurityConfig.getRoles();
for (Admin approver : authConfig) {
boolean approverIsASuperAdmin = serverSecurityConfig.isAdmin(approver);
boolean approverIsAGroupAdmin = group.isUserAnAdmin(approver.getName(), roles.memberRoles(approver));
boolean approverIsNotAnAdmin = !(approverIsASuperAdmin || approverIsAGroupAdmin);
boolean approverIsNotAGroupOperator = !groupOperators.has(approver, roles.memberRoles(approver));
if (approverIsNotAnAdmin && approverIsNotAGroupOperator) {
approver.addError(String.format("%s \"%s\" who is not authorized to operate pipeline group can not be authorized to approve stage", approver.describe(), approver, group.getGroup()));
}
}
}
}
private boolean isValidTypeValue() {
return type.equals(MANUAL) || type.equals(SUCCESS);
}
public void setConfigAttributes(Object attributes) {
Map attributeMap = (Map) attributes;
if (attributeMap.containsKey(TYPE)) {
type = (String) attributeMap.get(TYPE);
}
}
public ConfigErrors errors() {
return errors;
}
public void addError(String fieldName, String message) {
errors.add(fieldName, message);
}
public void addAdmin(Admin... admins) {
for (Admin admin : admins) {
if (!authConfig.contains(admin)) {
authConfig.add(admin);
}
}
}
public String getDisplayName() {
return type.equals(MANUAL) ? "Manual" : "On Success";
}
public void setOperatePermissions(List<Map<String, String>> usersMap, List<Map<String, String>> rolesMap) {
authConfig.clear();
Iterable<String> i;
if (usersMap != null) {
addAdmin(extractAdminUsers(usersMap));
}
if (rolesMap != null) {
addAdmin(extractAdminRole(rolesMap));
}
}
private Admin[] extractAdminUsers(List<Map<String, String>> map) {
List<Admin> result = new ArrayList<>(map.size());
for (Map<String, String> usernameMap : map) {
String value = usernameMap.get("name").trim();
if (!StringUtil.isBlank(value)) {
result.add(new AdminUser(new CaseInsensitiveString(value)));
}
}
return result.toArray(new Admin[result.size()]);
}
private Admin[] extractAdminRole(List<Map<String, String>> map) {
List<Admin> result = new ArrayList<>(map.size());
for (Map<String, String> usernameMap : map) {
String value = usernameMap.get("name").trim();
if (!StringUtil.isBlank(value)) {
result.add(new AdminRole(new CaseInsensitiveString(value)));
}
}
return result.toArray(new Admin[result.size()]);
}
public void removeOperatePermissions() {
authConfig.clear();
}
}