/* * Copyright 2000-2014 JetBrains s.r.o. * * 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 jetbrains.buildServer.server.rest.data; import com.intellij.openapi.diagnostic.Logger; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import jetbrains.buildServer.RootUrlHolder; import jetbrains.buildServer.ServiceLocator; import jetbrains.buildServer.buildTriggers.BuildTriggerDescriptor; import jetbrains.buildServer.groups.SUserGroup; import jetbrains.buildServer.plugins.PluginManager; import jetbrains.buildServer.plugins.bean.PluginInfo; import jetbrains.buildServer.plugins.bean.ServerPluginInfo; import jetbrains.buildServer.requirements.Requirement; import jetbrains.buildServer.server.rest.errors.AuthorizationFailedException; import jetbrains.buildServer.server.rest.errors.BadRequestException; import jetbrains.buildServer.server.rest.errors.NotFoundException; import jetbrains.buildServer.server.rest.model.Constants; import jetbrains.buildServer.server.rest.model.Util; import jetbrains.buildServer.server.rest.model.user.RoleAssignment; import jetbrains.buildServer.server.rest.util.BeanContext; import jetbrains.buildServer.server.rest.util.BeanFactory; import jetbrains.buildServer.serverSide.*; import jetbrains.buildServer.serverSide.agentPools.*; import jetbrains.buildServer.serverSide.artifacts.SArtifactDependency; import jetbrains.buildServer.serverSide.auth.*; import jetbrains.buildServer.serverSide.db.DBFunctionsProvider; import jetbrains.buildServer.serverSide.impl.VcsModificationChecker; import jetbrains.buildServer.serverSide.statistics.ValueProviderRegistry; import jetbrains.buildServer.serverSide.statistics.build.BuildDataStorage; import jetbrains.buildServer.users.SUser; import jetbrains.buildServer.users.User; import jetbrains.buildServer.users.UserModel; import jetbrains.buildServer.util.StringUtil; import jetbrains.buildServer.vcs.SVcsModification; import jetbrains.buildServer.vcs.VcsManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.context.ConfigurableApplicationContext; /** * User: Yegor Yarko * Date: 28.03.2009 */ public class DataProvider { private static final Logger LOG = Logger.getInstance(DataProvider.class.getName()); @NotNull private final SBuildServer myServer; @NotNull private final BuildHistory myBuildHistory; @NotNull private final UserModel myUserModel; @NotNull private final RolesManager myRolesManager; @NotNull private final VcsManager myVcsManager; @NotNull private final WebLinks myWebLinks; @NotNull private final ServerPluginInfo myPluginInfo; @NotNull private final ServerListener myServerListener; @NotNull private final SecurityContext mySecurityContext; @NotNull private final PluginManager myPluginManager; @NotNull private final RunningBuildsManager myRunningBuildsManager; @NotNull private final ValueProviderRegistry myValueProviderRegistry; @NotNull private final BuildDataStorage myBuildDataStorage; @NotNull private final BuildPromotionManager myPromotionManager; @NotNull private final VcsModificationChecker myVcsModificationChecker; @NotNull private final BeanFactory myBeanFactory; @NotNull private final ConfigurableApplicationContext myApplicationContext; @NotNull private final DBFunctionsProvider myDbFunctionsProvider; @NotNull private final ServiceLocator myServiceLocator; @NotNull private final RootUrlHolder myRootUrlHolder; public DataProvider(@NotNull final SBuildServer myServer, @NotNull final BuildHistory myBuildHistory, @NotNull final UserModel userModel, @NotNull final RolesManager rolesManager, @NotNull final VcsManager vcsManager, @NotNull final WebLinks webLinks, @NotNull final ServerPluginInfo pluginInfo, @NotNull final ServerListener serverListener, @NotNull final SecurityContext securityContext, @NotNull final PluginManager pluginManager, @NotNull final RunningBuildsManager runningBuildsManager, @NotNull final ValueProviderRegistry valueProviderRegistry, @NotNull final BuildDataStorage buildDataStorage, @NotNull final BuildPromotionManager promotionManager, @NotNull final VcsModificationChecker vcsModificationChecker, @NotNull final BeanFactory beanFactory, @NotNull final ConfigurableApplicationContext applicationContext, @NotNull final DBFunctionsProvider dbFunctionsProvider, @NotNull final ServiceLocator serviceLocator, @NotNull final RootUrlHolder rootUrlHolder) { this.myServer = myServer; this.myBuildHistory = myBuildHistory; this.myUserModel = userModel; myRolesManager = rolesManager; myVcsManager = vcsManager; myWebLinks = webLinks; myPluginInfo = pluginInfo; myServerListener = serverListener; mySecurityContext = securityContext; myPluginManager = pluginManager; myRunningBuildsManager = runningBuildsManager; myValueProviderRegistry = valueProviderRegistry; myBuildDataStorage = buildDataStorage; myPromotionManager = promotionManager; myVcsModificationChecker = vcsModificationChecker; myBeanFactory = beanFactory; myApplicationContext = applicationContext; myDbFunctionsProvider = dbFunctionsProvider; myServiceLocator = serviceLocator; myRootUrlHolder = rootUrlHolder; } public static String dumpQuoted(final Collection strings) { final StringBuilder result = new StringBuilder(); final Iterator it = strings.iterator(); while (it.hasNext()){ result.append("\"").append(it.next()).append("\""); if (it.hasNext()) result.append(", "); } return result.toString(); } @NotNull public SBuildServer getServer() { return myServer; } @NotNull public SProject getProjectByInternalId(@NotNull String projectInternalId){ final SProject project = myServer.getProjectManager().findProjectById(projectInternalId); if (project == null){ throw new NotFoundException("Could not find project by internal id '" + projectInternalId + "'."); } return project; } @NotNull public Role getRoleById(String roleId) { if (StringUtil.isEmpty(roleId)) { throw new BadRequestException("Cannot file role by empty id."); } Role role = myRolesManager.findRoleById(roleId); if (role == null) { throw new NotFoundException("Cannot find role by id '" + roleId + "'."); } return role; } public Collection getAllUsers() { final Collection serverUsers = myUserModel.getAllUsers().getUsers(); final Collection result = new ArrayList(serverUsers.size()); for (SUser group : serverUsers) { result.add(group); } return result; } @NotNull public static SArtifactDependency getArtifactDep(final BuildTypeSettings buildType, final String artifactDepLocator) { final int order = getArtifactDepOrderNum(buildType, artifactDepLocator); try { return buildType.getArtifactDependencies().get(order); } catch (IndexOutOfBoundsException e) { throw new NotFoundException("No artifact dependency found by locator '" + artifactDepLocator + "'. There is no dependency with order " + order + "."); } } public static int getArtifactDepOrderNum(final BuildTypeSettings buildType, final String artifactDepLocator) { final Long result; if (StringUtil.isEmpty(artifactDepLocator)) { throw new BadRequestException("Empty artifact dependency locator is not supported."); } final Locator locator = new Locator(artifactDepLocator); if (locator.isSingleValue()) { // no dimensions found, assume it's an result number result = locator.getSingleValueAsLong(); if (result == null) { throw new NotFoundException("No artifact dependency found by locator '" + artifactDepLocator + ". Locator should be result number of the dependency in the build configuration."); } if (result >= buildType.getArtifactDependencies().size()) { throw new NotFoundException("No artifact dependency found by locator '" + artifactDepLocator + "'. There is no dependency at position " + result + "."); } return result.intValue(); } throw new BadRequestException("No artifact dependency found by locator '" + artifactDepLocator + "'. Locator should be result number of the dependency in the build configuration."); } public static BuildTriggerDescriptor getTrigger(final BuildTypeSettings buildType, final String triggerLocator) { if (StringUtil.isEmpty(triggerLocator)) { throw new BadRequestException("Empty trigger locator is not supported."); } final Locator locator = new Locator(triggerLocator); if (locator.isSingleValue()) { // no dimensions found, assume it's trigger id final String triggerId = locator.getSingleValue(); if (StringUtil.isEmpty(triggerId)){ throw new BadRequestException("Trigger id cannot be empty."); } @SuppressWarnings("ConstantConditions") final BuildTriggerDescriptor foundTrigger = buildType.findTriggerById(triggerId); if (foundTrigger == null){ throw new NotFoundException("No trigger found by id '" + triggerLocator +"' in build type."); } return foundTrigger; } throw new BadRequestException( "No trigger can be found by locator '" + triggerLocator + "'. Locator should be trigger id."); } public static Requirement getAgentRequirement(final BuildTypeSettings buildType, final String agentRequirementLocator) { if (StringUtil.isEmpty(agentRequirementLocator)) { throw new BadRequestException("Empty agent requirement locator is not supported."); } final Locator locator = new Locator(agentRequirementLocator); if (locator.isSingleValue()) { // no dimensions found, assume it's requirement parameter name final String parameterName = locator.getSingleValue(); if (StringUtil.isEmpty(parameterName)){ throw new BadRequestException("Agent requirement property name cannot be empty."); } Requirement result = getAgentRequirementOrNull(buildType, parameterName); if (result == null){ throw new NotFoundException("No agent requirement for build parameter '" + parameterName +"' is found in the build type."); } return result; } throw new BadRequestException( "No agent requirement can be found by locator '" + agentRequirementLocator + "'. Locator should be property name."); } public static Requirement getAgentRequirementOrNull(final BuildTypeSettings buildType, final String parameterName) { final List requirements = buildType.getRequirements(); for (Requirement requirement : requirements) { if (parameterName.equals(requirement.getPropertyName())) { return requirement; } } return null; } @NotNull public String getBuildUrl(SBuild build) { return myWebLinks.getViewResultsUrl(build); } @NotNull public String getBuildTypeUrl(SBuildType buildType) { return myWebLinks.getConfigurationHomePageUrl(buildType); } @Nullable public static Date parseDate(@Nullable final String dateString) { if (dateString == null) { return null; } return getDate(dateString); } @NotNull public static Date getDate(@NotNull final String dateString) { try { return new SimpleDateFormat(Constants.TIME_FORMAT, Locale.ENGLISH).parse(dateString); } catch (ParseException e) { throw new BadRequestException("Could not parse date from value '" + dateString + "'. Supported format example : " + Util.formatTime(new Date()) + " :", e); } } public void deleteBuild(@NotNull final SBuild build) { if (build.isFinished()){ myBuildHistory.removeEntry((SFinishedBuild)build); //todo: (TeamCity) open API: should also add entry into audit and check permisisons (see also deleteBuild callers) }else{ throw new BadRequestException("Deleting not finished builds is not supported. Cancel the build and only then delete it."); } } @NotNull public ServerPluginInfo getPluginInfo() { return myPluginInfo; } @Nullable public Date getServerStartTime() { return myServerListener.getServerStartTime(); } public static RoleEntry getGroupRoleEntry(final SUserGroup group, final String roleId, final String scopeValue, final BeanContext context) { if (roleId == null) { throw new BadRequestException("Expected roleId is not specified"); } final RoleScope roleScope = RoleAssignment.getScope(scopeValue, context); final Collection roles = group.getRoles(); for (RoleEntry roleEntry : roles) { if (roleScope.equals(roleEntry.getScope()) && roleId.equals(roleEntry.getRole().getId())) { return roleEntry; } } throw new NotFoundException("Group " + group + " does not have role with id: '" + roleId + "' and scope '" + scopeValue + "'"); } public static RoleEntry getUserRoleEntry(final SUser user, final String roleId, final String scopeValue, final BeanContext context) { if (roleId == null) { throw new BadRequestException("Expected roleId is not specified"); } final RoleScope roleScope = RoleAssignment.getScope(scopeValue, context); final Collection roles = user.getRoles(); for (RoleEntry roleEntry : roles) { if (roleScope.equals(roleEntry.getScope()) && roleId.equals(roleEntry.getRole().getId())) { return roleEntry; } } throw new NotFoundException("User " + user + " does not have role with id: '" + roleId + "' and scope '" + scopeValue + "'"); } public String getHelpLink(@NotNull final String page, @Nullable final String anchor) { return myWebLinks.getHelp(page, anchor); } public boolean isPermissionGranted(@NotNull final Permission permission, @Nullable final String internalProjectId) { final AuthorityHolder authorityHolder = mySecurityContext.getAuthorityHolder(); if (internalProjectId == null){ return authorityHolder.isPermissionGrantedGlobally(permission); } return authorityHolder.isPermissionGrantedForProject(internalProjectId, permission); } public void checkGlobalPermission(final Permission permission) throws AuthorizationFailedException{ final AuthorityHolder authorityHolder = mySecurityContext.getAuthorityHolder(); if (!authorityHolder.isPermissionGrantedForAnyProject(permission)) { throw new AuthorizationFailedException( "User " + authorityHolder.getAssociatedUser() + " does not have global permission " + permission); } } public void checkGlobalPermissionAnyOf(final Permission[] permissions) throws AuthorizationFailedException{ final AuthorityHolder authorityHolder = mySecurityContext.getAuthorityHolder(); for (Permission permission : permissions) { if (authorityHolder.isPermissionGrantedForAnyProject(permission)) { return; } } throw new AuthorizationFailedException( "User " + authorityHolder.getAssociatedUser() + " does not have any of the permissions granted globally: " + Arrays.toString(permissions)); } public void checkProjectPermission(@NotNull final Permission permission, @Nullable final String internalProjectId) throws AuthorizationFailedException{ final AuthorityHolder authorityHolder = mySecurityContext.getAuthorityHolder(); if (internalProjectId == null){ if (authorityHolder.isPermissionGrantedGlobally(permission)){ return; } throw new AuthorizationFailedException("No permission '" + permission + " is granted globally."); } if (!authorityHolder.isPermissionGrantedForProject(internalProjectId, permission)) { throw new AuthorizationFailedException("User " + authorityHolder.getAssociatedUser() + " does not have permission " + permission + " in project with internal id: '" + internalProjectId + "'"); } } // workaround for http://youtrack.jetbrains.com/issue/TW-28306 public boolean checkCanView(final SVcsModification change) { final AuthorityHolder authorityHolder = mySecurityContext.getAuthorityHolder(); if (authorityHolder.isPermissionGrantedGlobally(Permission.VIEW_PROJECT)){ return true; } return AuthUtil.hasReadAccessTo(authorityHolder, change); } @NotNull public VcsManager getVcsManager() { return myVcsManager; } public Collection getPlugins() { final Collection detectedPlugins = myPluginManager.getDetectedPlugins(); Collection result = new ArrayList(detectedPlugins.size()); for (PluginInfo plugin : detectedPlugins) { result.add((ServerPluginInfo)plugin); } return result; } @NotNull public BuildDataStorage getBuildDataStorage() { return myBuildDataStorage; } @NotNull public ValueProviderRegistry getValueProviderRegistry() { return myValueProviderRegistry; } @Nullable public SUser getCurrentUser() { return getCurrentUser(mySecurityContext, myUserModel); } @Nullable public static SUser getCurrentUser(@NotNull final ServiceLocator serviceLocator) { return getCurrentUser(serviceLocator.getSingletonService(SecurityContext.class), serviceLocator.getSingletonService(UserModel.class)); } @Nullable private static SUser getCurrentUser(@NotNull final SecurityContext securityContext, @NotNull final UserModel userModel) { //also related API: SessionUser.getUser(request) final User associatedUser = securityContext.getAuthorityHolder().getAssociatedUser(); if (associatedUser == null){ return null; } if (SUser.class.isAssignableFrom(associatedUser.getClass())){ return (SUser)associatedUser; } return userModel.findUserAccount(null, associatedUser.getUsername()); } @NotNull public UserModel getUserModel() { return myUserModel; } @NotNull public BuildPromotionManager getPromotionManager() { return myPromotionManager; } @NotNull public BuildHistory getBuildHistory() { return myBuildHistory; } @NotNull public RunningBuildsManager getRunningBuildsManager() { return myRunningBuildsManager; } @NotNull public VcsModificationChecker getVcsModificationChecker() { return myVcsModificationChecker; } @NotNull public BeanFactory getBeanFactory() { return myBeanFactory; } // Workaround for http://youtrack.jetbrains.com/issue/TW-25260 public T getBean(Class type){ if (type.equals(DBFunctionsProvider.class)) return (T)myDbFunctionsProvider; return myApplicationContext.getBean(type); } public void addAgentToPool(@NotNull final jetbrains.buildServer.serverSide.agentPools.AgentPool agentPool, @NotNull final SBuildAgent postedAgent) { final AgentPoolManager agentPoolManager = myServiceLocator.getSingletonService(AgentPoolManager.class); final int agentPoolId = agentPool.getAgentPoolId(); try { agentPoolManager.moveAgentTypesToPool(agentPoolId, Collections.singleton(postedAgent.getId())); } catch (NoSuchAgentPoolException e) { throw new IllegalStateException("Agent pool with id \'" + agentPoolId + "' is not found."); } catch (PoolQuotaExceededException e) { throw new IllegalStateException(e.getMessage()); } catch (AgentTypeCannotBeMovedException e) { throw new IllegalStateException(e.getMessage()); } } public void setProjectPools(final SProject project, final List pools) { final AgentPoolManager poolManager = myServiceLocator.getSingletonService(AgentPoolManager.class); for (AgentPool agentPool : poolManager.getAllAgentPools()) { final int agentPoolId = agentPool.getAgentPoolId(); try { if (pools.contains(agentPool)){ poolManager.associateProjectsWithPool(agentPoolId, Collections.singleton(project.getProjectId())); }else{ poolManager.dissociateProjectsFromPool(agentPoolId, Collections.singleton(project.getProjectId())); } } catch (NoSuchAgentPoolException e) { throw new NotFoundException("No agent pool is found by id '" + agentPoolId + "'."); } } } /** * methods to cover missing URLs form TeamCity open API (TeamCity API issue) */ public String getBuildQueueUrl() { return makeUrl("queue.html"); } @NotNull private String makeUrl(@NotNull String relativePart) { String baseUrl = myRootUrlHolder.getRootUrl(); if (!baseUrl.endsWith("/")) baseUrl += "/"; return baseUrl + relativePart; } }