/*
* Copyright (c) 2013 Oracle Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Winston Prakash
*/
package org.eclipse.hudson.security.team;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import hudson.BulkChange;
import hudson.Util;
import hudson.XmlFile;
import hudson.model.AllView;
import hudson.model.Computer;
import hudson.model.Failure;
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.Job;
import hudson.model.Saveable;
import hudson.model.TopLevelItem;
import hudson.model.View;
import hudson.model.listeners.SaveableListener;
import hudson.security.ACL;
import hudson.security.AccessControlled;
import hudson.security.AuthorizationStrategy;
import hudson.security.Permission;
import hudson.security.SecurityRealm;
import hudson.util.FormValidation;
import hudson.util.XStream2;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.hudson.security.HudsonSecurityEntitiesHolder;
import org.eclipse.hudson.security.HudsonSecurityManager;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
/**
* Manager that manages the teams and their persistence
*
* @since 3.1.0
* @author Winston Prakash
*/
public final class TeamManager implements Saveable, AccessControlled {
public static final String TEAM_SEPARATOR = ".";
private final List<String> sysAdmins = new CopyOnWriteArrayList();
private final List<Team> teams = new CopyOnWriteArrayList<Team>();
private transient final XStream xstream = new XStream2();
private final transient Logger logger = LoggerFactory.getLogger(TeamManager.class);
private final transient File hudsonHomeDir;
private final transient File teamsFolder;
private transient final String teamsConfigFileName = "teams.xml";
private transient PublicTeam publicTeam;
private transient final String TEAMS_FOLDER_NAME = "teams";
public TeamManager(File homeDir) {
hudsonHomeDir = homeDir;
teamsFolder = new File(hudsonHomeDir, TEAMS_FOLDER_NAME);
if (!teamsFolder.exists()) {
teamsFolder.mkdirs();
}
initializeXstream();
load();
ensurePublicTeam();
ensureCustomFolders();
}
@Override
public ACL getACL() {
AuthorizationStrategy authorizationStrategy = HudsonSecurityEntitiesHolder.getHudsonSecurityManager().getAuthorizationStrategy();
if (authorizationStrategy instanceof TeamBasedAuthorizationStrategy) {
TeamBasedAuthorizationStrategy teamBasedAuthorizationStrategy = (TeamBasedAuthorizationStrategy) authorizationStrategy;
return teamBasedAuthorizationStrategy.getACL(this);
}
// Team will not be used if Team Based Authorization Strategy is not used
return new ACL() {
@Override
public boolean hasPermission(Authentication a, Permission permission) {
return false;
}
};
}
@Override
public boolean hasPermission(Permission permission) {
return getACL().hasPermission(permission);
}
@Override
public void checkPermission(Permission permission) throws AccessDeniedException {
getACL().checkPermission(permission);
}
public boolean isTeamManagementEnabled() {
HudsonSecurityManager hudsonSecurityManager = HudsonSecurityEntitiesHolder.getHudsonSecurityManager();
if (hudsonSecurityManager != null) {
AuthorizationStrategy authorizationStrategy = hudsonSecurityManager.getAuthorizationStrategy();
if (authorizationStrategy instanceof TeamBasedAuthorizationStrategy) {
return true;
}
}
return false;
}
public void addSysAdmin(String adminName) throws IOException {
if (!sysAdmins.contains(adminName)) {
sysAdmins.add(adminName);
save();
}
}
public void removeSysAdmin(String adminName) throws IOException {
if (sysAdmins.contains(adminName)) {
sysAdmins.remove(adminName);
save();
}
}
public List<String> getSysAdmins() {
return sysAdmins;
}
boolean isSysAdmin(String userName) {
if (getTeamAwareSecurityRealm() != null) {
return getTeamAwareSecurityRealm().isCurrentUserSysAdmin();
} else {
for (String admin : sysAdmins) {
if (userName.equalsIgnoreCase(admin)) {
return true;
}
}
return false;
}
}
public boolean isCurrentUserSysAdmin() {
String user = getCurrentUser();
logger.debug("Checking if principal {} is a System Admin", user);
if (isSysAdmin(user)) {
return true;
} else {
for (GrantedAuthority ga : getCurrentUserRoles()) {
logger.debug("Checking if the principal's role {} is a System Admin Role", ga.toString());
if (isSysAdmin(ga.getAuthority())) {
return true;
}
}
}
return false;
}
public boolean isCurrentUserTeamAdmin(String teamName) {
if (isCurrentUserSysAdmin()) {
return true;
}
String user = getCurrentUser();
try {
Team team = findTeam(teamName);
if (team.isAdmin(user)) {
return true;
} else {
// Check if any of the group the user is a memmber has given
// Team Admin Role
for (GrantedAuthority ga : getCurrentUserRoles()) {
logger.debug("Checking if the principal's role {} is a Team Admin Role", ga.toString());
if (team.isAdmin(ga.getAuthority())) {
return true;
}
}
}
} catch (TeamNotFoundException ex) {
logger.info(ex.getLocalizedMessage(), ex);
}
return false;
}
// Used in Jelly
public boolean isCurrentUserAnyTeamAdmin(){
for (Team team : teams) {
if (isCurrentUserTeamAdmin(team.getName())){
return true;
}
}
return false;
}
//Used by TeamManager Jelly to display team details in master-details fashion
public Map<String, Team> getTeams() {
Map<String, Team> teamMap = new HashMap<String, Team>();
for (Team team : teams) {
teamMap.put(team.getName(), team);
}
return teamMap;
}
public List<String> getTeamNames() {
List<String> teamList = new ArrayList<String>();
for (Team team : teams) {
teamList.add(team.getName());
}
return teamList;
}
public Team createTeam(String teamName) throws IOException, TeamAlreadyExistsException {
return createTeam(teamName, teamName, null);
}
public Team createTeam(String teamName, String description, String customFolder) throws IOException, TeamAlreadyExistsException {
try {
Hudson.checkGoodTeamName(teamName);
if (teamName.trim().length() > Hudson.TEAM_NAME_LIMIT) {
throw new Failure("Team name cannot exceed " + Hudson.TEAM_NAME_LIMIT + " characters.");
}
} catch (Failure ex) {
throw new IOException(ex.getMessage());
}
return internalCreateTeam(teamName, description, customFolder);
}
private Team internalCreateTeam(String teamName, String description, String customFolder) throws IOException, TeamAlreadyExistsException {
for (Team team : teams) {
if (teamName.equals(team.getName())) {
throw new TeamAlreadyExistsException(teamName);
}
}
Team newTeam = new Team(teamName, description, customFolder, this);
addTeam(newTeam);
return newTeam;
}
private void addTeam(Team team) throws IOException {
teams.add(team);
save();
}
public void deleteTeam(String teamName) throws TeamNotFoundException, IOException {
deleteTeam(teamName, false);
}
public void deleteTeam(String teamName, boolean deleteJobs) throws TeamNotFoundException, IOException {
Team team = findTeam(teamName);
if (Team.PUBLIC_TEAM_NAME.equals(team.getName())) {
throw new IOException("Cannot delete public team");
}
for (TeamJob job : team.getJobs()) {
TopLevelItem item = Hudson.getInstance().getItem(job.getId());
if (item != null && (item instanceof Job)) {
if (deleteJobs) {
try {
item.delete();
} catch (InterruptedException e) {
throw new IOException("Delete team " + team.getName() + " was interrupted");
}
} else {
// Make deleted team jobs public
moveJob((Job) item, team, publicTeam, null);
}
}
}
teams.remove(team);
save();
File teamFolder = team.getTeamFolder(teamsFolder);
if (teamFolder.exists() && teamFolder.isDirectory()) {
Util.deleteContentsRecursive(teamFolder);
Util.deleteFile(teamFolder);
}
}
public Team findTeam(String teamName) throws TeamNotFoundException {
for (Team team : teams) {
if (teamName.equals(team.getName())) {
return team;
}
}
throw new TeamNotFoundException(teamName);
}
public void removeTeam(String teamName) throws IOException, TeamNotFoundException {
Team team = findTeam(teamName);
teams.remove(team);
save();
}
public HttpResponse doCreateTeam(@QueryParameter String teamName, @QueryParameter String description, @QueryParameter String customFolder) throws IOException {
if (!isCurrentUserSysAdmin()) {
return new TeamUtils.ErrorHttpResponse("No permission to create team.");
}
if ((teamName == null) || "".equals(teamName.trim())) {
return new TeamUtils.ErrorHttpResponse("Team name required.");
}
if ((customFolder != null) && !"".equals(customFolder.trim())) {
File folder = new File(customFolder.trim());
if (!folder.exists() && !folder.mkdirs()) {
return new TeamUtils.ErrorHttpResponse("Could not create custom team folder - " + customFolder);
}
}
try {
Hudson.checkGoodName(teamName);
if (teamName.trim().length() > Hudson.TEAM_NAME_LIMIT) {
throw new Failure("Team name cannot exceed " + Hudson.TEAM_NAME_LIMIT + " characters.");
}
} catch (Failure ex) {
return new TeamUtils.ErrorHttpResponse(ex.getMessage());
}
try {
internalCreateTeam(teamName, description, customFolder);
return HttpResponses.ok();
} catch (TeamAlreadyExistsException ex) {
return new TeamUtils.ErrorHttpResponse(ex.getLocalizedMessage());
}
}
public HttpResponse doDeleteTeam(@QueryParameter String teamName) throws IOException {
if (!isCurrentUserSysAdmin()) {
return new TeamUtils.ErrorHttpResponse("No permission to delete team.");
}
if ((teamName == null) || "".equals(teamName.trim())) {
return new TeamUtils.ErrorHttpResponse("Team name required.");
}
try {
deleteTeam(teamName);
return HttpResponses.ok();
} catch (TeamNotFoundException ex) {
return new TeamUtils.ErrorHttpResponse(ex.getLocalizedMessage());
}
}
public HttpResponse doAddTeamMember(@QueryParameter String teamName,
@QueryParameter String teamMemberSid,
@QueryParameter boolean isTeamAdmin,
@QueryParameter boolean canCreate,
@QueryParameter boolean canDelete,
@QueryParameter boolean canConfigure,
@QueryParameter boolean canBuild,
@QueryParameter boolean canCreateNode,
@QueryParameter boolean canDeleteNode,
@QueryParameter boolean canConfigureNode,
@QueryParameter boolean canCreateView,
@QueryParameter boolean canDeleteView,
@QueryParameter boolean canConfigureView
) throws IOException {
if (!isCurrentUserTeamAdmin(teamName)) {
return new TeamUtils.ErrorHttpResponse("No permission to add team member.");
}
if ((teamName == null) || "".equals(teamName.trim())) {
return new TeamUtils.ErrorHttpResponse("Team name required.");
}
if ((teamMemberSid == null) || "".equals(teamMemberSid.trim())) {
return new TeamUtils.ErrorHttpResponse("Team member name required.");
}
Team team;
try {
team = findTeam(teamName);
if (team.findMember(teamMemberSid) == null) {
team.addMember(teamMemberSid, isTeamAdmin, canCreate, canDelete, canConfigure, canBuild,
canCreateNode, canDeleteNode, canConfigureNode, canCreateView, canDeleteView, canConfigureView);
return FormValidation.respond(FormValidation.Kind.OK, TeamUtils.getIcon(teamMemberSid));
} else {
return new TeamUtils.ErrorHttpResponse(teamMemberSid + " is already a team member.");
}
} catch (TeamNotFoundException ex) {
return new TeamUtils.ErrorHttpResponse(teamName + " is not a valid team.");
}
}
public HttpResponse doUpdateTeamMember(@QueryParameter String teamName,
@QueryParameter String teamMemberSid,
@QueryParameter boolean isTeamAdmin,
@QueryParameter boolean canCreate,
@QueryParameter boolean canDelete,
@QueryParameter boolean canConfigure,
@QueryParameter boolean canBuild,
@QueryParameter boolean canCreateNode,
@QueryParameter boolean canDeleteNode,
@QueryParameter boolean canConfigureNode,
@QueryParameter boolean canCreateView,
@QueryParameter boolean canDeleteView,
@QueryParameter boolean canConfigureView) throws IOException {
if (!isCurrentUserTeamAdmin(teamName)) {
return new TeamUtils.ErrorHttpResponse("No permission to add team member.");
}
if ((teamName == null) || "".equals(teamName.trim())) {
return new TeamUtils.ErrorHttpResponse("Team name required.");
}
if ((teamMemberSid == null) || "".equals(teamMemberSid.trim())) {
return new TeamUtils.ErrorHttpResponse("Team member name required.");
}
Team team;
try {
team = findTeam(teamName);
TeamMember currentMember = team.findMember(teamMemberSid);
if (currentMember != null) {
team.updateMember(teamMemberSid, isTeamAdmin, canCreate, canDelete, canConfigure, canBuild,
canCreateNode, canDeleteNode, canConfigureNode, canCreateView, canDeleteView, canConfigureView);
return FormValidation.respond(FormValidation.Kind.OK, TeamUtils.getIcon(teamMemberSid));
} else {
return new TeamUtils.ErrorHttpResponse(teamMemberSid + " is not a team member.");
}
} catch (TeamNotFoundException ex) {
return new TeamUtils.ErrorHttpResponse(teamName + " is not a valid team.");
}
}
public HttpResponse doRemoveTeamMember(@QueryParameter String teamName,
@QueryParameter String teamMemberSid) throws IOException {
if (!isCurrentUserTeamAdmin(teamName)) {
return new TeamUtils.ErrorHttpResponse("No permission to remove team member");
}
if ((teamName == null) || "".equals(teamName.trim())) {
return new TeamUtils.ErrorHttpResponse("Team name required.");
}
if ((teamMemberSid == null) || "".equals(teamMemberSid.trim())) {
return new TeamUtils.ErrorHttpResponse("Team member name required.");
}
Team team;
try {
team = findTeam(teamName);
} catch (TeamNotFoundException ex) {
return new TeamUtils.ErrorHttpResponse(teamName + " is not a valid team.");
}
TeamMember currentMember = team.findMember(teamMemberSid);
if (currentMember != null) {
team.removeMember(teamMemberSid);
return HttpResponses.ok();
} else {
return new TeamUtils.ErrorHttpResponse(teamMemberSid + " is not a team member.");
}
}
public HttpResponse doMoveJob(@QueryParameter String jobName, @QueryParameter String teamName) throws IOException {
Team jobTeam = findJobOwnerTeam(jobName);
if (!isCurrentUserTeamAdmin(jobTeam.getName())) {
return new TeamUtils.ErrorHttpResponse("No permission to move job from team - " + jobTeam.getName());
}
if (!isCurrentUserTeamAdmin(teamName)) {
return new TeamUtils.ErrorHttpResponse("No permission to move job to team - " + teamName);
}
if ((teamName == null) || "".equals(teamName.trim())) {
return new TeamUtils.ErrorHttpResponse("Team name required.");
}
if ((jobName == null) || "".equals(jobName.trim())) {
return new TeamUtils.ErrorHttpResponse("Job name required.");
}
Team newTeam;
try {
newTeam = findTeam(teamName);
} catch (TeamNotFoundException ex) {
return new TeamUtils.ErrorHttpResponse(teamName + " is not a valid team.");
}
Team oldTeam = findJobOwnerTeam(jobName);
if (oldTeam == null) {
return new TeamUtils.ErrorHttpResponse(jobName + " does not belong to any team.");
}
if (oldTeam == newTeam) {
return new TeamUtils.ErrorHttpResponse(jobName + " is already in team " + oldTeam.getName());
}
Item item = Hudson.getInstance().getItem(jobName);
Job job;
if (item instanceof Job<?, ?>) {
job = (Job) item;
if (job.isBuilding()) {
return new TeamUtils.ErrorHttpResponse(job.getName() + " is building.");
}
try {
String newJobName = moveJob(job, oldTeam, newTeam, null);
return HttpResponses.plainText(newJobName);
} catch (IOException ex) {
return new TeamUtils.ErrorHttpResponse("Faile to move the job " + jobName
+ " to the team " + teamName + ". " + ex.getLocalizedMessage());
}
} else {
return new TeamUtils.ErrorHttpResponse(jobName + " not a valid job Id.");
}
}
public HttpResponse doMoveView(@QueryParameter String viewName, @QueryParameter String teamName) throws IOException {
Team viewTeam = findViewOwnerTeam(viewName);
if (!isCurrentUserTeamAdmin(viewTeam.getName())) {
return new TeamUtils.ErrorHttpResponse("No permission to move view from team - " + viewTeam.getName());
}
if (!isCurrentUserTeamAdmin(teamName)) {
return new TeamUtils.ErrorHttpResponse("No permission to move view to team - " + teamName);
}
if ((teamName == null) || "".equals(teamName.trim())) {
return new TeamUtils.ErrorHttpResponse("Team name required.");
}
if ((viewName == null) || "".equals(viewName.trim())) {
return new TeamUtils.ErrorHttpResponse("View name required.");
}
Team newTeam;
try {
newTeam = findTeam(teamName);
} catch (TeamNotFoundException ex) {
return new TeamUtils.ErrorHttpResponse(teamName + " is not a valid team.");
}
Team oldTeam = findViewOwnerTeam(viewName);
if (oldTeam == null) {
return new TeamUtils.ErrorHttpResponse(viewName + " does not belong to any team.");
}
if (oldTeam == newTeam) {
return new TeamUtils.ErrorHttpResponse(viewName + " is already in team " + oldTeam.getName());
}
moveView(oldTeam, newTeam, viewName);
return HttpResponses.ok();
}
public void moveView(Team oldTeam, Team newTeam, String viewName) throws IOException {
if (!oldTeam.removeView(viewName)) {
throw new IOException("View "+viewName+" is not a member of team "+oldTeam.getName());
}
newTeam.addView(new TeamView(viewName));
}
public HttpResponse doMoveNode(@QueryParameter String nodeName, @QueryParameter String teamName) throws IOException {
Team nodeTeam = findNodeOwnerTeam(nodeName);
if (!isCurrentUserTeamAdmin(nodeTeam.getName())) {
return new TeamUtils.ErrorHttpResponse("No permission to move node from team - " + nodeTeam.getName());
}
if (!isCurrentUserTeamAdmin(teamName)) {
return new TeamUtils.ErrorHttpResponse("No permission to move node to team - " + teamName);
}
if ((teamName == null) || "".equals(teamName.trim())) {
return new TeamUtils.ErrorHttpResponse("Team name required.");
}
if ((nodeName == null) || "".equals(nodeName.trim())) {
return new TeamUtils.ErrorHttpResponse("Node name required.");
}
Team newTeam;
try {
newTeam = findTeam(teamName);
} catch (TeamNotFoundException ex) {
return new TeamUtils.ErrorHttpResponse(teamName + " is not a valid team.");
}
Team oldTeam = findNodeOwnerTeam(nodeName);
if (oldTeam == null) {
return new TeamUtils.ErrorHttpResponse(nodeName + " does not belong to any team.");
}
if (oldTeam == newTeam) {
return new TeamUtils.ErrorHttpResponse(nodeName + " is already in team " + oldTeam.getName());
}
moveNode(oldTeam, newTeam, nodeName);
return HttpResponses.ok();
}
public void moveNode(Team oldTeam, Team newTeam, String nodeName) throws IOException {
if (!oldTeam.removeNode(nodeName)) {
throw new IOException("Node "+nodeName+" is not a member of team "+oldTeam.getName());
}
newTeam.addNode(new TeamNode(nodeName));
}
public HttpResponse doSetJobVisibility(@QueryParameter String jobName, @QueryParameter String teamNames, @QueryParameter boolean canViewConfig) throws IOException {
Team jobTeam = findJobOwnerTeam(jobName);
if (!isCurrentUserTeamAdmin(jobTeam.getName())) {
return new TeamUtils.ErrorHttpResponse("No permission to set job visibility. Job name - " + jobName + " Team Name - " + jobTeam.getName());
}
if ((jobName == null) || "".equals(jobName.trim())) {
return new TeamUtils.ErrorHttpResponse("Job id required.");
}
Team ownerTeam = findJobOwnerTeam(jobName);
if (ownerTeam == null) {
return new TeamUtils.ErrorHttpResponse(jobName + " does not belong to any team.");
} else {
TeamJob job = ownerTeam.findJob(jobName);
job.removeAllVisibilities();
for (String teamName : teamNames.split(":")) {
job.addVisibility(teamName);
}
job.setAllowConfigView(canViewConfig);
save();
}
return HttpResponses.ok();
}
public HttpResponse doSetViewVisibility(@QueryParameter String viewName, @QueryParameter String teamNames) throws IOException {
Team viewTeam = findViewOwnerTeam(viewName);
if (!isCurrentUserTeamAdmin(viewTeam.getName())) {
return new TeamUtils.ErrorHttpResponse("No permission to set view visibility. View name - " + viewName + " Team Name - " + viewTeam.getName());
}
if ((viewName == null) || "".equals(viewName.trim())) {
return new TeamUtils.ErrorHttpResponse("View name required.");
}
Team ownerTeam = findViewOwnerTeam(viewName);
if (ownerTeam == null) {
return new TeamUtils.ErrorHttpResponse(viewName + " does not belong to any team.");
} else {
TeamView view = ownerTeam.findView(viewName);
view.removeAllVisibilities();
for (String teamName : teamNames.split(":")) {
view.addVisibility(teamName);
}
save();
}
return HttpResponses.ok();
}
public HttpResponse doSetPrimaryView(@QueryParameter String viewName, @QueryParameter String teamName) throws IOException, TeamNotFoundException {
Team viewTeam = findViewOwnerTeam(viewName);
if (!isCurrentUserTeamAdmin(teamName)) {
return new TeamUtils.ErrorHttpResponse("No permission to set primary view. View name - " + viewName + " Team Name - " + viewTeam.getName());
}
if ((viewName == null) || "".equals(viewName.trim())) {
return new TeamUtils.ErrorHttpResponse("View name required.");
}
Team viewVisibleTeam = findTeam(teamName);
viewVisibleTeam.setPrimaryView(viewName);
return HttpResponses.ok();
}
public void addViewVisibility(TeamView view, String teamName) {
view.addVisibility(teamName);
}
public HttpResponse doSetNodeVisibility(@QueryParameter String nodeName, @QueryParameter String teamNames) throws IOException {
Team nodeTeam = findNodeOwnerTeam(nodeName);
if (!isCurrentUserTeamAdmin(nodeTeam.getName())) {
return new TeamUtils.ErrorHttpResponse("No permission to set node visibility. Node name - " + nodeName + " Team Name - " + nodeTeam.getName());
}
if ((nodeName == null) || "".equals(nodeName.trim())) {
return new TeamUtils.ErrorHttpResponse("Node name required.");
}
Team ownerTeam = findNodeOwnerTeam(nodeName);
if (ownerTeam == null) {
return new TeamUtils.ErrorHttpResponse(nodeName + " does not belong to any team.");
} else {
TeamNode node = ownerTeam.findNode(nodeName);
node.removeAllVisibilities();
for (String teamName : teamNames.split(":")) {
node.addVisibility(teamName);
}
save();
}
return HttpResponses.ok();
}
public HttpResponse doSetNodeEnabled(@QueryParameter String nodeName, @QueryParameter String teamName, @QueryParameter boolean enabled) throws IOException, TeamNotFoundException {
if (!isCurrentUserTeamAdmin(teamName)) {
return new TeamUtils.ErrorHttpResponse("No permission to enable node. Node name - " + nodeName + " Team Name - " + teamName);
}
if ((nodeName == null) || "".equals(nodeName.trim())) {
return new TeamUtils.ErrorHttpResponse("Node name required.");
}
Team nodeVisibleTeam = findTeam(teamName);
setNodeEnabled(nodeName, nodeVisibleTeam, enabled);
return HttpResponses.ok();
}
public void setNodeEnabled(String nodeName, Team team, boolean enabled) throws IOException {
if (!enabled) {
team.removeFromEnabledVisibleNodes(nodeName);
} else {
team.addToEnabledVisibleNodes(nodeName);
}
}
public void addNodeVisibility(TeamNode node, String teamName) {
node.addVisibility(teamName);
}
public HttpResponse doCheckSid(@QueryParameter String sid) throws IOException {
return FormValidation.respond(FormValidation.Kind.OK, TeamUtils.getIcon(sid));
}
/**
* Copy jobs from old team to new team. Supplying the original name helps,
* when job is created for one team and then moved to another team (Ex.
* Create Job in a team). When the job is created in first team it may take
* a unique name different from the supplied original name.
*
* @param job
* @param oldTeam
* @param newTeam
* @param originalName - original name with which the moved job should be
* created.
* @throws IOException
*/
private String moveJob(Job job, Team oldTeam, Team newTeam, String originalName) throws IOException {
try {
String oldJobName = job.getName();
String unqualifiedJobName = originalName;
if ((originalName == null) || "".equals(originalName.trim())) {
unqualifiedJobName = getUnqualifiedJobName(oldTeam, job.getName());
}
unqualifiedJobName = unqualifiedJobName.trim();
// Deal with public job name created with team disabled corner case.
if (isPublicTeam(oldTeam)) {
// Job name might be too long or contain team separator
if (unqualifiedJobName.length() > Hudson.JOB_NAME_LIMIT_TEAM) {
unqualifiedJobName = unqualifiedJobName.substring(0, Hudson.JOB_NAME_LIMIT_TEAM);
}
unqualifiedJobName = unqualifiedJobName.replace(TEAM_SEPARATOR, "_");
}
String qualifiedNewJobName = getTeamQualifiedJobName(newTeam, unqualifiedJobName);
// Add the new job, rename before removing the old job
// ensures team manager will find correct locations.
newTeam.addJob(new TeamJob(qualifiedNewJobName));
job.renameTo(qualifiedNewJobName);
oldTeam.removeJob(oldJobName);
Hudson.getInstance().replaceItem(job.getName(), qualifiedNewJobName);
return qualifiedNewJobName;
} catch (Exception exc) {
throw new IOException(exc);
}
}
/**
* Before a job is created in a team, it must be added to the team so the
* correct location will be found.
*
* @param unqualifiedJobName job name with no team qualification
* @param team team the job is to be created in
* @return qualified job name to be used to create
* @throws IOException
*/
public String addJob(String unqualifiedJobName, Team team) throws IOException {
String qualifiedNewJobName = getTeamQualifiedJobName(team, unqualifiedJobName);
team.addJob(new TeamJob(qualifiedNewJobName));
return qualifiedNewJobName;
}
/**
* Strip team qualification from job name.
*
* @param team must not be null
* @param jobName qualified job name
* @return unqualified job name
*/
public String getUnqualifiedJobName(Team team, String jobName) {
if (!Team.PUBLIC_TEAM_NAME.equals(team.getName()) && jobName.startsWith(team.getName() + TEAM_SEPARATOR)) {
return jobName.substring(team.getName().length() + 1);
}
return jobName;
}
/**
* Get the name of the current user admin teams as JSON
*
* @return HttpResponse with JSON as content type
*/
public HttpResponse doGetTeamsJson() {
return new HttpResponse() {
@Override
public void generateResponse(StaplerRequest sr, StaplerResponse rsp, Object o) throws IOException, ServletException {
writeJson(rsp, (List<String>) getCurrentUserAdminTeams());
}
};
}
/**
* Get names of all teams in TeamManager as JSON
*
* @return HttpResponse with JSON as content type
*/
public HttpResponse doGetAllTeamsJson() {
return new HttpResponse() {
@Override
public void generateResponse(StaplerRequest sr, StaplerResponse rsp, Object o) throws IOException, ServletException {
writeJson(rsp, getTeamNames());
}
};
}
/**
* Get the name of the views visible to the team as JSON
*
* @param teamName
* @return HttpResponse with JSON as content type
*/
public HttpResponse doGetViewsJson(@QueryParameter final String teamName) {
return new HttpResponse() {
@Override
public void generateResponse(StaplerRequest sr, StaplerResponse rsp, Object o) throws IOException, ServletException {
try {
Team team = findTeam(teamName);
writeJson(rsp, (List<String>) team.getAllViewNames());
} catch (TeamNotFoundException ex) {
}
}
};
}
private void writeJson(StaplerResponse rsp, List<String> data) throws IOException {
rsp.setStatus(HttpServletResponse.SC_OK);
rsp.setContentType("application/json");
PrintWriter w = new PrintWriter(rsp.getWriter());
w.println("{");
for (int i = 0; i < data.size(); i++) {
w.print("\"" + data.get(i) + "\":\"" + data.get(i) + "\"");
if (i < data.size() - 1) {
w.println(",");
}
}
w.println("}");
w.close();
}
/* For Unit Test */
void addUser(String teamName, String userName) throws TeamNotFoundException, IOException {
Team team = findTeam(teamName);
team.addMember(userName, false, false, false, false, false, false, false, false, false, false, false);
save();
}
/**
* Return true if current user has access to team. Team management must be
* enabled.
*
* @param teamName
* @return
*/
public boolean isCurrentUserHasAccessToTeam(String teamName) {
if (isTeamManagementEnabled()) {
try {
if (isCurrentUserSysAdmin()) {
return true;
}
Team team = findTeam(teamName);
if (getTeamAwareSecurityRealm() != null) {
if (team.equals(getTeamAwareSecurityRealm().GetCurrentUserTeam())) {
return true;
}
}
for (Team userTeam : findCurrentUserTeams()) {
if (userTeam == team) {
return true;
}
}
} catch (TeamNotFoundException ex) {
// no access
}
}
return false;
}
private String getCurrentUser() {
Authentication authentication = HudsonSecurityManager.getAuthentication();
return authentication.getName();
}
private Collection<? extends GrantedAuthority> getCurrentUserRoles() {
Authentication authentication = HudsonSecurityManager.getAuthentication();
return authentication.getAuthorities();
}
/**
* Check if current user is not sys admin and is admin of exactly one team.
*
* @return
*/
public boolean isCurrentUserAdminInSingleTeam() {
return getCurrentUserAdminTeams().size() == 1;
}
/**
* Check if current user is admin in more than one team
*/
public boolean isCurrentUserAdminInMultipleTeams() {
return getCurrentUserAdminTeams().size() > 1;
}
/**
* Get the team in the case where current user is admin of only one team.
*/
public String getCurrentUserAdminTeam() {
List<String> teams = (List<String>) getCurrentUserAdminTeams();
if (teams.size() == 1) {
return teams.get(0);
}
throw new IllegalStateException("Current user is admin of " + teams.size() + " teams");
}
/**
* Get all the teams current user is admin of. Sys admin is considered to be
* admin of all teams.
*/
public Collection<String> getCurrentUserAdminTeams() {
List<String> list = new ArrayList<String>();
for (Team team : teams) {
if (isCurrentUserTeamAdmin(team.getName())){
list.add(team.getName());
}
}
Collections.sort(list);
return list;
}
//Used by teamManager.jelly.
public Collection<String> getCurrentUserAdminJobs() {
Hudson hudson = Hudson.getInstance();
List<String> jobNames = new ArrayList<String>();
for (Team team : teams) {
boolean teamAdmin = isCurrentUserTeamAdmin(team.getName());
if (teamAdmin) {
for (TeamJob teamJob : team.getJobs()) {
TopLevelItem item = hudson.getItem(teamJob.getId());
if (item != null && (item instanceof Job)) {
jobNames.add(item.getName());
}
}
}
}
Collections.sort(jobNames);
return jobNames;
}
//Used by teamManager.jelly.
public Collection<String> getCurrentUserAdminViews() throws IOException {
List<String> viewNames = new ArrayList<String>();
for (Team team : teams) {
boolean teamAdmin = isCurrentUserTeamAdmin(team.getName());
if (teamAdmin) {
for (TeamView teamView : team.getViews()) {
viewNames.add(teamView.getId());
}
}
}
Collections.sort(viewNames);
return viewNames;
}
//Used by teamManager.jelly.
public Collection<String> getCurrentUserAdminNodes() throws IOException {
List<String> nodeNames = new ArrayList<String>();
for (Team team : teams) {
boolean teamAdmin = isCurrentUserTeamAdmin(team.getName());
if (teamAdmin) {
for (TeamNode teamNode : team.getNodes()) {
nodeNames.add(teamNode.getId());
}
}
}
Collections.sort(nodeNames);
return nodeNames;
}
/**
* Check if current user is in more than one team.
*/
public boolean isCurrentUserInMultipleTeams() {
return getCurrentUserTeams().size() > 1;
}
/**
* Get all the teams current user is a member of. Sys admin is considered to
* be a member of all teams.
*/
public Collection<String> getCurrentUserTeams() {
List<String> list = new ArrayList<String>();
boolean admin = isCurrentUserSysAdmin();
String user = getCurrentUser();
for (Team team : teams) {
if (admin || team.isMember(user)) {
list.add(team.getName());
} else {
// Check if any of the group the user is a memmber, is also a member of the team
for (GrantedAuthority ga : getCurrentUserRoles()) {
logger.debug("Checking if the principal's role {} is a Team Admin Role", ga.toString());
if (team.isMember(ga.getAuthority())) {
list.add(team.getName());
}
}
}
}
return list;
}
// Used in hudson.model.view.newJob.jelly
public Collection<String> getCurrentUserTeamsWithCreatePermission() {
List<Team> teamsWithPermission;
if (isCurrentUserSysAdmin()) {
teamsWithPermission = teams;
} else {
teamsWithPermission = getCurrentUserTeamsWithPermission(Item.CREATE);
}
List<String> teamNames = new ArrayList<String>();
for (Team team : teamsWithPermission) {
teamNames.add(team.getName());
}
return teamNames;
}
public List<Team> findUserTeams(String userName) {
List<Team> userTeams = new ArrayList<Team>();
//Check if we have to use TeamAwareSecurityRealm
if (getTeamAwareSecurityRealm() != null) {
userTeams.add(getTeamAwareSecurityRealm().GetCurrentUserTeam());
return userTeams;
}
for (Team team : teams) {
if (team.isMember(userName)) {
userTeams.add(team);
}
}
return userTeams;
}
public Team findUserTeamForItem(String userName) {
List<Team> userTeams = findUserTeams(userName);
if (userTeams.isEmpty()) {
return publicTeam;
}
return userTeams.get(0);
}
public Team findUserTeamForJob(String userName) {
return findUserTeamForItem(userName);
}
public Team findUserTeamForView(String userName) {
return findUserTeamForItem(userName);
}
public Team findUserTeamForNode(String userName) {
return findUserTeamForItem(userName);
}
private TeamAwareSecurityRealm getTeamAwareSecurityRealm() {
HudsonSecurityManager hudsonSecurityManager = HudsonSecurityEntitiesHolder.getHudsonSecurityManager();
if (hudsonSecurityManager != null) {
SecurityRealm securityRealm = hudsonSecurityManager.getSecurityRealm();
if ((securityRealm != null) && securityRealm instanceof TeamAwareSecurityRealm) {
return (TeamAwareSecurityRealm) securityRealm;
}
}
return null;
}
// this could be private
public List<Team> findCurrentUserTeams() {
//Check if we have to use TeamAwareSecurityRealm
if (getTeamAwareSecurityRealm() != null) {
return Arrays.asList(getTeamAwareSecurityRealm().GetCurrentUserTeam());
}
Authentication authentication = HudsonSecurityManager.getAuthentication();
List<Team> userTeams = findUserTeams(authentication.getName());
Collection<? extends GrantedAuthority> gas = authentication.getAuthorities();
if (gas == null) {
throw new IllegalStateException("authentication.getAuthorities() returned null array");
}
for (GrantedAuthority ga : gas) {
String grantedAuthority = ga.getAuthority();
userTeams.addAll(findUserTeams(grantedAuthority));
}
return userTeams;
}
public Team findJobOwnerTeam(String jobName) {
for (Team team : teams) {
if (team.isJobOwner(jobName)) {
return team;
}
}
return null;
}
public TeamJob findJob(String jobName) {
Team jobTeam = findJobOwnerTeam(jobName);
if (jobTeam != null) {
return jobTeam.findJob(jobName);
}
return null;
}
public void addJob(Team team, String jobName) throws IOException, TeamNotFoundException {
if (team != null) {
team.addJob(new TeamJob(jobName));
} else {
findTeam(Team.PUBLIC_TEAM_NAME).addJob(new TeamJob(jobName));
}
save();
}
public void renameJob(Team team, String oldJobName, String newJobName) throws IOException {
if (team != null) {
team.renameJob(oldJobName, newJobName);
save();
}
}
public void removeJob(Team team, String jobName) throws IOException {
if (team != null) {
team.removeJob(jobName);
save();
}
}
public void removeJob(String jobName) throws IOException {
removeJob(findJobOwnerTeam(jobName), jobName);
}
public void addJobToUserTeam(String userName, String jobName) throws IOException, TeamNotFoundException {
// Fix bug in hudson.model.listeners.ItemListenerTest - no team found for user
addJob(findUserTeamForJob(userName), jobName);
}
public void addJobToCurrentUserTeam(String jobName) throws IOException, TeamNotFoundException {
addJobToUserTeam(getCurrentUser(), jobName);
}
void removeJobFromUserTeam(String userName, String jobName) throws IOException {
// Used only in tests
removeJob(findUserTeams(userName).get(0), jobName);
}
void renameJobInUserTeam(String userName, String oldJobName, String newJobName) throws IOException {
// Used only in tests
renameJob(findUserTeams(userName).get(0), oldJobName, newJobName);
}
public Team findNodeOwnerTeam(String nodeName) {
for (Team team : teams) {
if (team.isNodeOwner(nodeName)) {
return team;
}
}
return null;
}
public void addNode(Team team, String nodeName) throws IOException, TeamNotFoundException {
if (team != null) {
team.addNode(new TeamNode(nodeName));
} else {
findTeam(Team.PUBLIC_TEAM_NAME).addNode(new TeamNode(nodeName));
}
save();
}
public void renameNode(Team team, String oldNodeName, String newNodeName) throws IOException {
if (team != null) {
team.renameNode(oldNodeName, newNodeName);
save();
}
}
public void removeNode(Team team, String nodeName) throws IOException {
if (team != null) {
team.removeNode(nodeName);
save();
}
}
public TeamNode findNode(String nodeName) {
Team nodeTeam = findNodeOwnerTeam(nodeName);
if (nodeTeam != null) {
return nodeTeam.findNode(nodeName);
}
return null;
}
public void removeNode(String nodeName) throws IOException {
removeNode(findNodeOwnerTeam(nodeName), nodeName);
}
public void addNodeToUserTeam(String userName, String nodeName) throws IOException, TeamNotFoundException {
// Fix bug in hudson.model.listeners.ItemListenerTest - no team found for user
addNode(findUserTeamForNode(userName), nodeName);
}
public void addNodeToCurrentUserTeam(String nodeName) throws IOException, TeamNotFoundException {
addNodeToUserTeam(getCurrentUser(), nodeName);
}
void removeNodeFromUserTeam(String userName, String nodeName) throws IOException {
// Used only in tests
removeNode(findUserTeams(userName).get(0), nodeName);
}
void renameNodeInUserTeam(String userName, String oldNodeName, String newNodeName) throws IOException {
// Used only in tests
renameNode(findUserTeams(userName).get(0), oldNodeName, newNodeName);
}
public Team findViewOwnerTeam(String viewName) {
for (Team team : teams) {
if (team.isViewOwner(viewName)) {
return team;
}
}
return null;
}
public TeamView findView(String viewName) {
Team viewTeam = findViewOwnerTeam(viewName);
if (viewTeam != null) {
return viewTeam.findView(viewName);
}
return null;
}
public void addView(Team team, String viewName) throws IOException, TeamNotFoundException {
if (team != null) {
team.addView(new TeamView(viewName));
} else {
findTeam(Team.PUBLIC_TEAM_NAME).addView(new TeamView(viewName));
}
save();
}
public void renameView(Team team, String oldViewName, String newViewName) throws IOException {
if (team != null) {
team.renameView(oldViewName, newViewName);
save();
}
}
public void removeView(Team team, String viewName) throws IOException {
if (team != null) {
team.removeView(viewName);
save();
}
}
public void removeView(String viewName) throws IOException {
removeView(findViewOwnerTeam(viewName), viewName);
}
public void addViewToUserTeam(String userName, String viewName) throws IOException, TeamNotFoundException {
// Fix bug in hudson.model.listeners.ItemListenerTest - no team found for user
addView(findUserTeamForView(userName), viewName);
}
public void addViewToCurrentUserTeam(String viewName) throws IOException, TeamNotFoundException {
addViewToUserTeam(getCurrentUser(), viewName);
}
void removeViewFromUserTeam(String userName, String viewName) throws IOException {
// Used only in tests
removeView(findUserTeams(userName).get(0), viewName);
}
void renameViewInUserTeam(String userName, String oldViewName, String newViewName) throws IOException {
// Used only in tests
renameView(findUserTeams(userName).get(0), oldViewName, newViewName);
}
/**
* Save the settings to the configuration file.
* @throws java.io.IOException
*/
@Override
public synchronized void save() throws IOException {
if (useBulkSaveFlag && BulkChange.contains(this)) {
return;
}
getConfigFile().write(this);
if (useBulkSaveFlag) {
SaveableListener.fireOnChange(this, getConfigFile());
}
}
/**
* Get the current user team qualified Id for the job name If the user is
* member of multiple teams use the first team
*
* @param jobName
* @return String, Qualified Job ID
*/
public String getTeamQualifiedJobName(String jobName) {
List<Team> currentUserTeamsWithPermission = getCurrentUserTeamsWithPermission(Item.CREATE);
if (!currentUserTeamsWithPermission.isEmpty()) {
return getTeamQualifiedJobName(currentUserTeamsWithPermission.get(0), jobName);
}
return jobName;
}
/**
* Called to check duplicate job name before create. For this purpose, we
* want a job name that is not necessarily unique.
*
* @param jobName requested job name
* @return qualified name to check
*/
public String getRawTeamQualifiedJobName(String jobName) {
List<Team> currentUserTeamsWithPermission = getCurrentUserTeamsWithPermission(Item.CREATE);
if (!currentUserTeamsWithPermission.isEmpty()) {
return getRawTeamQualifiedJobName(currentUserTeamsWithPermission.get(0), jobName);
}
return jobName;
}
/**
* Called to check duplicate job name before create. For this purpose, we
* want a job name that is not necessarily unique.
*
* @param team requested team
* @param jobName requested job name
* @return qualified name to check
*/
public String getRawTeamQualifiedJobName(Team team, String jobName) {
if (isPublicTeam(team)) {
return jobName;
}
return team.getName() + TEAM_SEPARATOR + jobName;
}
/**
*
* @return the implicit team for the current user
* @throws TeamNotFoundException
*/
public Team findCurrentUserTeamForNewJob() throws TeamNotFoundException {
// This will only find explicit team members with create permission
List<Team> currentUserTeamsWithPermission = getCurrentUserTeamsWithPermission(Item.CREATE);
if (!currentUserTeamsWithPermission.isEmpty()) {
return currentUserTeamsWithPermission.get(0);
}
if (isCurrentUserSysAdmin()) {
return publicTeam;
}
throw new TeamNotFoundException("User does not have Job create permission in any team");
}
public Team findCurrentUserTeamForNewView() throws TeamNotFoundException {
// This will only find explicit team members with create permission
List<Team> currentUserTeamsWithPermission = getCurrentUserTeamsWithPermission(View.CREATE);
if (!currentUserTeamsWithPermission.isEmpty()) {
return currentUserTeamsWithPermission.get(0);
}
if (isCurrentUserSysAdmin()) {
return publicTeam;
}
throw new TeamNotFoundException("User does not have View create permission in any team");
}
public Team findCurrentUserTeamForNewNode() throws TeamNotFoundException {
// This will only find explicit team members with create permission
List<Team> currentUserTeamsWithPermission = getCurrentUserTeamsWithPermission(Computer.CREATE);
if (!currentUserTeamsWithPermission.isEmpty()) {
return currentUserTeamsWithPermission.get(0);
}
if (isCurrentUserSysAdmin()) {
return publicTeam;
}
throw new TeamNotFoundException("User does not have Node create permission in any team");
}
public String getCurrentUserPrimaryView() {
List<Team> currentUserTeams = findCurrentUserTeams();
if (!currentUserTeams.isEmpty()) {
Team team = currentUserTeams.get(0);
return team.getPrimaryView();
}
return null;
}
/**
* Get the current user team qualified Id for the job name
*
* @param team
* @param jobName
* @return String, Team qualified Job ID
*/
public String getTeamQualifiedJobName(Team team, String jobName) {
String teamName = team.getName();
StringBuilder sb = Team.PUBLIC_TEAM_NAME.equals(teamName)
? new StringBuilder(jobName)
: new StringBuilder(teamName + TEAM_SEPARATOR + jobName);
// Make sure the name is unique
Hudson h = Hudson.getInstance();
int postfix = 2;
int baseLength = sb.length();
while (true) {
String qualifiedName = sb.toString();
if (findJobOwnerTeam(qualifiedName) == null) {
break;
}
sb.setLength(baseLength);
sb.append("_" + postfix++);
}
return sb.toString();
}
/**
* Check that jobName is properly qualified for the team. The check fails
* if:
* <pre>
* - The team is public but the name is qualified (contains a '.')
* - The team is not public, but the name is not qualified
* by team name
* </pre>
*
* @param team must not be null
* @param jobName
* @return true if check succeeds
*/
public boolean isQualifiedJobName(Team team, String jobName) {
if (isPublicTeam(team)) {
return jobName.indexOf('.') < 0;
}
return jobName.startsWith(team.getName() + TEAM_SEPARATOR);
}
/**
* Get the part of job name that is unique within the team. That is, given
* <team-name>.<job-part> return job part if the job is already in
* <team-name> or <team-name> is the current user team and team is not the
* public team. Otherwise, return jobName.
*
* @param jobName
* @return
*/
public String getUnqualifiedJobName(String jobName) {
Team team = findJobOwnerTeam(jobName);
if (team == null) {
if (!findCurrentUserTeams().isEmpty()) {
team = findCurrentUserTeams().get(0);
}
}
if (team != null) {
return getUnqualifiedJobName(team, jobName);
}
return jobName;
}
/**
* The Folder where all the jobs of the team to which this jobName belongs
* to are stored.
*
* <p>
* This method should be called to determine the jobs folder whether or not
* team management is enabled, as team manager alone knows where team jobs
* are.
*
* @param jobName
* @return File, team jobs folder
*/
public File getRootFolderForJob(String jobName) {
Team team = findJobOwnerTeam(jobName);
// May be just created job, get the job folder from the first
// team the current user or user role has create permission
if ((team == null) && isTeamManagementEnabled()) {
if (getTeamAwareSecurityRealm() != null) {
team = getTeamAwareSecurityRealm().GetCurrentUserTeam();
} else {
List<Team> currentUserTeamsWithPermission = getCurrentUserTeamsWithPermission(Item.CREATE);
if (!currentUserTeamsWithPermission.isEmpty()) {
team = currentUserTeamsWithPermission.get(0);
}
}
}
if (team != null) {
if (isPublicTeam(team)) {
return new File(team.getJobsFolder(hudsonHomeDir), jobName);
} else {
return new File(team.getJobsFolder(teamsFolder), jobName);
}
} else {
// May be just created by sys admin who does not belong to any team
return new File(publicTeam.getJobsFolder(hudsonHomeDir), jobName);
}
}
/**
* Get the root folders of all the jobs known to this Team manager
*
* @return
*/
public File[] getJobsRootFolders() {
List<File> jobsRootFolders = new ArrayList<File>();
for (Team team : teams) {
if (isPublicTeam(team)) {
jobsRootFolders.addAll(team.getJobsRootFolders(hudsonHomeDir));
} else {
jobsRootFolders.addAll(team.getJobsRootFolders(teamsFolder));
}
}
return jobsRootFolders.toArray(new File[jobsRootFolders.size()]);
}
List<Team> getCurrentUserTeamsWithPermission(Permission permission) {
//Check if we have to use TeamAwareSecurityRealm
if (getTeamAwareSecurityRealm() != null) {
return Arrays.asList(getTeamAwareSecurityRealm().GetCurrentUserTeam());
}
List<Team> userTeamsWithPermission = new ArrayList<Team>();
Authentication authentication = HudsonSecurityManager.getAuthentication();
List<Team> userTeams = findCurrentUserTeams();
for (Team userTeam : userTeams) {
TeamMember member = userTeam.findMember(authentication.getName());
if ((member != null) && member.hasPermission(permission)) {
userTeamsWithPermission.add(userTeam);
}
}
for (GrantedAuthority ga : authentication.getAuthorities()) {
for (Team userTeam : userTeams) {
TeamMember member = userTeam.findMember(ga.getAuthority());
if ((member != null) && member.hasPermission(permission)) {
userTeamsWithPermission.add(userTeam);
}
}
}
return userTeamsWithPermission;
}
public static final String ADMIN = "Admin";
/**
* All team permissions in sorted order
*/
public static String[] ALL_TEAM_PERMISSIONS = new String[]{
ADMIN, // not a real permission, but needed to distinguish admins
Item.BUILD.getName(),
Item.CONFIGURE.getName(),
Item.CREATE.getName(),
Item.DELETE.getName(),
Item.EXTENDED_READ.getName(),
Item.READ.getName(),
Item.WIPEOUT.getName(),
Item.WORKSPACE.getName(),};
// Used in org.cli.ListTeamsCommand
public List<String> getCurrentUserVisibleTeams() {
List<String> teams = (List<String>) getCurrentUserTeams();
if (!teams.contains(Team.PUBLIC_TEAM_NAME)) {
teams.add(Team.PUBLIC_TEAM_NAME);
}
return teams;
}
// Used by ListTeamsCommand
public boolean isUserHasAccessToTeam(String user, String team) {
if (Team.PUBLIC_TEAM_NAME.equals(team)) {
return true;
}
List<Team> userTeams = findUserTeams(user);
for (Team userTeam : userTeams) {
if (team.equals(userTeam.getName())) {
return true;
}
}
return false;
}
// Used by ListTeamsCommand
public Collection<String> getCurrentUserAdminUsers() {
Set<String> adminUsers = new TreeSet<String>();
Collection<String> adminTeams = getCurrentUserAdminTeams();
for (String teamName : adminTeams) {
try {
Team team = findTeam(teamName);
List<TeamMember> members = team.getMembers();
for (TeamMember member : members) {
adminUsers.add(member.getName());
}
} catch (TeamNotFoundException ex) {
; // shouldn't happen
}
}
return adminUsers;
}
// Used by ListTeamsCommand
public String[] getUserTeamPermissions(String user, String teamName) throws TeamNotFoundException {
Team team = findTeam(teamName);
if (isSysAdmin(user)) {
return ALL_TEAM_PERMISSIONS;
}
TeamMember member = team.findMember(user);
if (member != null) {
List<String> memberPermissions = member.getPermissions();
if (team.isAdmin(user)) {
// Add the pseudo-permission
memberPermissions.add(0, ADMIN);
}
String[] permissions = memberPermissions.toArray(new String[memberPermissions.size()]);
Arrays.sort(permissions);
return permissions;
} else if (Team.PUBLIC_TEAM_NAME.equals(teamName)) {
// Even anonymous can read
return new String[]{Item.READ.getName()};
}
return new String[0];
}
// Used in org.cli.ListTeamsCommand?
public String[] getCurrentUserTeamPermissions(String teamName) throws TeamNotFoundException {
return getUserTeamPermissions(getCurrentUser(), teamName);
}
public boolean canNodeExecuteJob(String nodeName, String jobName) {
Team nodeTeam = findNodeOwnerTeam(nodeName);
Team jobTeam = findJobOwnerTeam(jobName);
if ((nodeTeam != null) && (jobTeam != null)) {
//If the node belongs to public then all teams can build
//their job, unless the team admin disables it
if (Team.PUBLIC_TEAM_NAME.equals(nodeTeam.getName()) && jobTeam.isVisibleNodeEnabled(nodeName)) {
return true;
}
if (nodeTeam == jobTeam) {
return true;
} else {
TeamNode teamNode = nodeTeam.findNode(nodeName);
if (teamNode != null) {
return teamNode.isVisible(jobTeam.getName()) && jobTeam.isVisibleNodeEnabled(nodeName);
}
}
}
return false;
}
public static class TeamNotFoundException extends Exception {
public TeamNotFoundException(String teamName) {
super("Team " + teamName + " does not exist.");
}
}
public static class TeamAlreadyExistsException extends Exception {
public TeamAlreadyExistsException(String teamName) {
super("Team " + teamName + " already exists.");
}
}
void setUseBulkSaveFlag(boolean flag) {
useBulkSaveFlag = flag;
}
Team getPublicTeam() {
return publicTeam;
}
/**
* The file where the teams settings are saved.
*/
private XmlFile getConfigFile() {
return new XmlFile(xstream, new File(teamsFolder, teamsConfigFileName));
}
// This is purely fo unit test. Since Hudson is not fully loaded during
// test BulkChange saving mode is not available
private transient boolean useBulkSaveFlag = true;
/**
* Load the settings from the configuration file
*/
private void load() {
XmlFile config = getConfigFile();
try {
if (config.exists()) {
config.unmarshal(this);
}
} catch (IOException e) {
logger.error("Failed to load " + config, e);
}
}
private void ensureCustomFolders() {
// NB: It would be best to clean up jobs at any of the logger calls below
// but we're in the TeamManager constructor so it can't be called from
// Team to save teams.xml. These end cases should be rare.
for (Team team : teams) {
String customFolderName = team.getCustomFolderName();
if (customFolderName != null && customFolderName.trim().length() > 0) {
File jobsDir = team.getJobsFolder(teamsFolder);
// In 3.1.0 there was no child jobs folder
if (!jobsDir.exists()) {
List<TeamJob> jobs = team.getJobs();
if (!jobs.isEmpty()) {
// move the jobs to jobs folder
if (jobsDir.mkdirs()) {
for (TeamJob job : jobs) {
File teamDir = team.getTeamFolder(teamsFolder);
File oldJobDir = new File(teamDir, job.getId());
if (oldJobDir.exists() && oldJobDir.isDirectory()) {
File newJobDir = new File(jobsDir, job.getId());
try {
Util.moveDirectory(oldJobDir, newJobDir);
} catch (InterruptedException e) {
logger.error("Failed to move " + oldJobDir.getAbsolutePath());
}
} else {
logger.error("Job folder not found " + oldJobDir.getAbsolutePath());
}
}
} else {
logger.error("Can't create " + jobsDir.getAbsolutePath());
}
}
}
}
}
}
private void ensurePublicTeam() {
publicTeam = new PublicTeam(this);
try {
Team team = findTeam(PublicTeam.PUBLIC_TEAM_NAME);
teams.remove(team);
} catch (TeamNotFoundException ex) {
// It's ok, we are going to remove it any way
}
try {
publicTeam.loadExistingJobs(hudsonHomeDir);
Hudson hudson = Hudson.getInstance();
//Null during initial setup
if (hudson != null) {
for (View view : hudson.getAllViews()) {
TeamView teamView = new TeamView(view.getViewName());
if (view instanceof AllView) {
teamView.setMoveAllowed(false);
publicTeam.addView(teamView);
} else if (findViewOwnerTeam(view.getViewName()) == null) {
publicTeam.addView(teamView);
}
}
for (Computer node : hudson.getAllComputers()) {
TeamNode teamNode = new TeamNode(node.getName());
if (node instanceof Hudson.MasterComputer) {
//Master node does not have a name!!
teamNode.setId("Master");
teamNode.setMoveAllowed(false);
publicTeam.addNode(teamNode);
} else if (findNodeOwnerTeam(node.getName()) == null) {
publicTeam.addNode(teamNode);
}
}
}
} catch (IOException ex) {
logger.error("Failed to load existing jobs", ex);
}
teams.add(publicTeam);
}
boolean isPublicTeam(Team team) {
return Team.PUBLIC_TEAM_NAME.equals(team.getName());
}
private void initializeXstream() {
xstream.alias("teamManager", TeamManager.class);
xstream.alias("team", Team.class);
xstream.alias("publicTeam", PublicTeam.class);
}
public static class ConverterImpl implements Converter {
@Override
public boolean canConvert(Class type) {
return type == TeamManager.class;
}
@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
TeamManager teamManager = (TeamManager) source;
for (String sid : teamManager.sysAdmins) {
writer.startNode("sysAdmin");
writer.setValue(sid);
writer.endNode();
}
for (Team team : teamManager.teams) {
writer.startNode("team");
context.convertAnother(team);
writer.endNode();
}
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext uc) {
TeamManager teamManager = (TeamManager) uc.currentObject();
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("sysAdmin".equals(reader.getNodeName())) {
teamManager.sysAdmins.add(reader.getValue());
} else if ("team".equals(reader.getNodeName())) {
Team team = (Team) uc.convertAnother(teamManager, Team.class);
teamManager.teams.add(team);
}
reader.moveUp();
}
return teamManager;
}
}
}