/* * 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.config; import com.thoughtworks.go.config.remote.ConfigOrigin; import com.thoughtworks.go.config.remote.ConfigOriginTraceable; import com.thoughtworks.go.domain.ConfigErrors; import com.thoughtworks.go.domain.config.Admin; import com.thoughtworks.go.util.StringUtil; import java.util.ArrayList; import java.util.List; import java.util.Map; import static java.util.Collections.sort; @ConfigTag("authorization") public class Authorization implements Validatable, ParamsAttributeAware, ConfigOriginTraceable { @ConfigSubtag private ViewConfig viewConfig = new ViewConfig(); @ConfigSubtag private OperationConfig operationConfig = new OperationConfig(); @ConfigSubtag private AdminsConfig adminsConfig = new AdminsConfig(); @ConfigAttribute(value = "allGroupAdminsAreViewers") private boolean allowGroupAdmins = true; private final ConfigErrors configErrors = new ConfigErrors(); public static final String NAME = "name"; public static final String ALLOW_GROUP_ADMINS="allowGroupAdmins"; public static final String PRIVILEGES = "privileges"; public static final String VALUE = "value"; public static final String PRIVILEGE_TYPE = "privilege_type"; public static final String TYPE = "type"; private ConfigOrigin origin; @Override public ConfigOrigin getOrigin() { return origin; } @Override public void setOrigins(ConfigOrigin origins) { origin = origins; } public static enum UserType { USER { @Override public Admin makeUser(String name) { return new AdminUser(new CaseInsensitiveString(name)); }}, ROLE { @Override public Admin makeUser(String name) { return new AdminRole(new CaseInsensitiveString(name)); }}; abstract public Admin makeUser(String name); } public static enum PrivilegeType { ADMIN { @Override public AdminsConfig group(Authorization authorization) { return authorization.getAdminsConfig(); } @Override public void set(PresentationElement el) { el.makeAdmin(); }}, OPERATE { @Override public AdminsConfig group(Authorization authorization) { return authorization.getOperationConfig(); } @Override public void set(PresentationElement el) { el.makeOperator(); }}, VIEW { @Override public AdminsConfig group(Authorization authorization) { return authorization.getViewConfig(); } @Override public void set(PresentationElement el) { el.makeViewer(); }}; abstract public AdminsConfig group(Authorization authorization); public abstract void set(PresentationElement el); } public static enum PrivilegeState { ON { @Override public void apply(AdminsConfig adminsConfig, Admin userOrRole) { adminsConfig.add(userOrRole); }}, OFF, DISABLED; public void apply(AdminsConfig adminsConfig, Admin userOrRole) { } } public Authorization() { } public Authorization(ViewConfig viewConfig, OperationConfig operationConfig, AdminsConfig adminsConfig) { this.viewConfig = viewConfig; this.operationConfig = operationConfig; this.adminsConfig = adminsConfig; } public Authorization(ViewConfig viewConfig) { this.viewConfig = viewConfig; } public Authorization(OperationConfig operationConfig) { this.operationConfig = operationConfig; } public Authorization(AdminsConfig adminsConfig) { this.adminsConfig = adminsConfig; } public Authorization(OperationConfig operationConfig, ViewConfig viewConfig) { this.operationConfig = operationConfig; this.viewConfig = viewConfig; } //only for test public ViewConfig getViewConfig() { return viewConfig; } public AdminsConfig getOperationConfig() { return operationConfig; } public boolean hasViewPermission(final CaseInsensitiveString username, UserRoleMatcher userRoleMatcher) { return viewConfig.hasUser(username, userRoleMatcher); } public boolean hasOperationPermissionDefined() { return !operationConfig.equals(new OperationConfig()); } public boolean hasViewPermissionDefined() { return !viewConfig.equals(new ViewConfig()); } public boolean hasOperatePermission(final CaseInsensitiveString username, UserRoleMatcher userRoleMatcher) { return operationConfig.hasUser(username, userRoleMatcher); } public boolean hasAdminsDefined() { return !adminsConfig.equals(new AdminsConfig()); } public boolean isUserAnAdmin(final CaseInsensitiveString userName, List<Role> memberRoles) { return adminsConfig.isAdmin(new AdminUser(userName), memberRoles); } public boolean isViewUser(final CaseInsensitiveString username, List<Role> memberRoles) { return viewConfig.isAdmin(new AdminUser(username), memberRoles); } public boolean hasAdminOrViewPermissions(final CaseInsensitiveString userName, List<Role> memberRoles) { return isUserAnAdmin(userName, memberRoles) || isViewUser(userName, memberRoles); } public AdminsConfig getAdminsConfig() { return adminsConfig; } public void setAdminsConfig(AdminsConfig adminsConfig) { this.adminsConfig = adminsConfig; } public void setAllowGroupAdmins(boolean allowGroupAdmins) { this.allowGroupAdmins = allowGroupAdmins; } public boolean isAllowGroupAdmins() { return allowGroupAdmins; } public List<PrivilegeType> privilagesOfRole(final CaseInsensitiveString roleName){ List<PrivilegeType> result = new ArrayList<>(); if (isRoleAnAdmin(roleName)){ result.add(PrivilegeType.ADMIN); } if (isRoleAnOperator(roleName)){ result.add(PrivilegeType.OPERATE); } if (isRoleAViewer(roleName)){ result.add(PrivilegeType.VIEW); } return result; } public void validate(ValidationContext validationContext) { return; } public ConfigErrors errors() { return configErrors; } public void addError(String fieldName, String message) { configErrors.add(fieldName, message); } public void setConfigAttributes(Object attributes) { List<Map<String, Object>> attributeMap = (List) attributes; for (Map<String, Object> userMap : attributeMap) { String name = (String) userMap.get(NAME); if (StringUtil.isBlank(name)) { continue; } UserType type = UserType.valueOf((String) userMap.get(TYPE)); Admin admin = type.makeUser(name); for (Map.Entry<String, String> privilegeEntry : ((Map<String, String>) ((List) userMap.get(PRIVILEGES)).get(0)).entrySet()) { PrivilegeType privilegeType = PrivilegeType.valueOf(privilegeEntry.getKey().toString().toUpperCase()); AdminsConfig privilegeGroup = privilegeType.group(this); PrivilegeState state = PrivilegeState.valueOf(privilegeEntry.getValue()); state.apply(privilegeGroup, admin); } //Give default view permission if no checkbox has been checked in the admin UI if(!(adminsConfig.contains(admin) || operationConfig.contains(admin) || viewConfig.contains(admin))){ viewConfig.add(admin); } } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Authorization that = (Authorization) o; if (adminsConfig != null ? !adminsConfig.equals(that.adminsConfig) : that.adminsConfig != null) { return false; } if (operationConfig != null ? !operationConfig.equals(that.operationConfig) : that.operationConfig != null) { return false; } if (viewConfig != null ? !viewConfig.equals(that.viewConfig) : that.viewConfig != null) { return false; } if (allowGroupAdmins != that.allowGroupAdmins) { return false; } return true; } @Override public int hashCode() { int result = viewConfig != null ? viewConfig.hashCode() : 0; result = 31 * result + (operationConfig != null ? operationConfig.hashCode() : 0); result = 31 * result + (adminsConfig != null ? adminsConfig.hashCode() : 0); result = 31 * result + (allowGroupAdmins ? 1 : 0); return result; } public void removeAllUsagesOfRole(Role role) { adminsConfig.removeRole(role); viewConfig.removeRole(role); operationConfig.removeRole(role); } public static class PresentationElement implements Comparable<PresentationElement>, Validatable { public static final String NAME = "name"; private String name; public static final String TYPE = "type"; private UserType type; public static final String ADMIN_PRIVILEGE = "admin"; private PrivilegeState adminPrivilege; public static final String OPERATE_PRIVILEGE = "operate"; private PrivilegeState operatePrivilege; public static final String VIEW_PRIVILEGE = "view"; private PrivilegeState viewPrivilege; private ConfigErrors configErrors = new ConfigErrors(); public PresentationElement(String name, UserType type) { this.name = name; this.type = type; adminPrivilege = operatePrivilege = viewPrivilege = PrivilegeState.OFF; } public String getName() { return name; } public PrivilegeState getAdmin() { return adminPrivilege; } public PrivilegeState getView() { return viewPrivilege; } public PrivilegeState getOperate() { return operatePrivilege; } public void makeAdmin() { adminPrivilege = PrivilegeState.ON; viewPrivilege = PrivilegeState.DISABLED; operatePrivilege = PrivilegeState.DISABLED; } public void makeOperator() { operatePrivilege = PrivilegeState.ON; } public void makeViewer() { viewPrivilege = PrivilegeState.ON; } public UserType getType() { return type; } public int compareTo(PresentationElement other) { return this.name.compareTo(other.name); } public void validate(ValidationContext validationContext) { } public ConfigErrors errors() { return configErrors; } public void addError(String fieldName, String message) { configErrors.add(fieldName, message); } } public List<PresentationElement> getUserAuthorizations() { ArrayList<PresentationElement> list = new ArrayList<>(); Class<AdminUser> allowOnly = AdminUser.class; addPrivilegesForView(list, operationConfig, PrivilegeType.OPERATE, allowOnly, UserType.USER); addPrivilegesForView(list, viewConfig, PrivilegeType.VIEW, allowOnly, UserType.USER); addPrivilegesForView(list, adminsConfig, PrivilegeType.ADMIN, allowOnly, UserType.USER); sort(list); return list; } public List<PresentationElement> getRoleAuthorizations() { ArrayList<PresentationElement> list = new ArrayList<>(); Class<AdminRole> onlyOfType = AdminRole.class; addPrivilegesForView(list, operationConfig, PrivilegeType.OPERATE, onlyOfType, UserType.ROLE); addPrivilegesForView(list, viewConfig, PrivilegeType.VIEW, onlyOfType, UserType.ROLE); addPrivilegesForView(list, adminsConfig, PrivilegeType.ADMIN, onlyOfType, UserType.ROLE); sort(list); return list; } public String toString() { return String.format("Authorization [view: %s] [operate: %s] [admins: %s] [allowGroupAdmins: %s]", viewConfig, operationConfig, adminsConfig, allowGroupAdmins); } private void addPrivilegesForView(ArrayList<PresentationElement> list, final AdminsConfig privilegesCollection, final PrivilegeType privilegeType, final Class<? extends Admin> allowOnly, final UserType type) { for (Admin admin : privilegesCollection) { if (allowOnly.isAssignableFrom(admin.getClass())) { addPresentationPrivilege(admin, list, privilegeType, type); } } } private void addPresentationPrivilege(Admin admin, ArrayList<PresentationElement> list, PrivilegeType privilegeType, final UserType type) { PresentationElement el = null; for (PresentationElement presentationElement : list) { if (presentationElement.getName().equals(CaseInsensitiveString.str(admin.getName()))) { el = presentationElement; } } if (el == null) { el = new PresentationElement(CaseInsensitiveString.str(admin.getName()), type); if (!admin.errors().isEmpty()) { el.addError(Admin.NAME, admin.errors().on(Admin.NAME)); } list.add(el); } privilegeType.set(el); } private boolean isRoleAnAdmin(final CaseInsensitiveString roleName){ return containsRole(roleName, adminsConfig.getRoles()); } private boolean isRoleAnOperator(final CaseInsensitiveString roleName){ return containsRole(roleName, operationConfig.getRoles()); } private boolean isRoleAViewer(final CaseInsensitiveString roleName){ return containsRole(roleName, viewConfig.getRoles()); } private boolean containsRole(final CaseInsensitiveString roleName, List<AdminRole> roles){ for(AdminRole role : roles){ if (role.getName().equals(roleName)){ return true; } } return false; } }