Line data Source code
1 : // Copyright (C) 2012 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.restapi.project; 16 : 17 : import com.google.common.collect.ListMultimap; 18 : import com.google.common.flogger.FluentLogger; 19 : import com.google.gerrit.common.Nullable; 20 : import com.google.gerrit.entities.Project; 21 : import com.google.gerrit.extensions.registration.DynamicMap; 22 : import com.google.gerrit.extensions.restapi.AuthException; 23 : import com.google.gerrit.extensions.restapi.BadRequestException; 24 : import com.google.gerrit.extensions.restapi.IdString; 25 : import com.google.gerrit.extensions.restapi.NeedsParams; 26 : import com.google.gerrit.extensions.restapi.ResourceConflictException; 27 : import com.google.gerrit.extensions.restapi.ResourceNotFoundException; 28 : import com.google.gerrit.extensions.restapi.RestApiException; 29 : import com.google.gerrit.extensions.restapi.RestCollection; 30 : import com.google.gerrit.extensions.restapi.RestView; 31 : import com.google.gerrit.extensions.restapi.TopLevelResource; 32 : import com.google.gerrit.extensions.restapi.UnprocessableEntityException; 33 : import com.google.gerrit.json.OutputFormat; 34 : import com.google.gerrit.server.CurrentUser; 35 : import com.google.gerrit.server.ProjectUtil; 36 : import com.google.gerrit.server.permissions.PermissionBackend; 37 : import com.google.gerrit.server.permissions.PermissionBackendException; 38 : import com.google.gerrit.server.permissions.ProjectPermission; 39 : import com.google.gerrit.server.project.ProjectCache; 40 : import com.google.gerrit.server.project.ProjectResource; 41 : import com.google.gerrit.server.project.ProjectState; 42 : import com.google.inject.Inject; 43 : import com.google.inject.Provider; 44 : import java.io.IOException; 45 : import java.util.Optional; 46 : 47 : public class ProjectsCollection 48 : implements RestCollection<TopLevelResource, ProjectResource>, NeedsParams { 49 149 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 50 : 51 : private final DynamicMap<RestView<ProjectResource>> views; 52 : private final Provider<ListProjects> list; 53 : private final Provider<QueryProjects> queryProjects; 54 : private final ProjectCache projectCache; 55 : private final PermissionBackend permissionBackend; 56 : private final Provider<CurrentUser> user; 57 : 58 : private boolean hasQuery; 59 : 60 : @Inject 61 : public ProjectsCollection( 62 : DynamicMap<RestView<ProjectResource>> views, 63 : Provider<ListProjects> list, 64 : Provider<QueryProjects> queryProjects, 65 : ProjectCache projectCache, 66 : PermissionBackend permissionBackend, 67 149 : Provider<CurrentUser> user) { 68 149 : this.views = views; 69 149 : this.list = list; 70 149 : this.queryProjects = queryProjects; 71 149 : this.projectCache = projectCache; 72 149 : this.permissionBackend = permissionBackend; 73 149 : this.user = user; 74 149 : } 75 : 76 : @Override 77 : public void setParams(ListMultimap<String, String> params) throws BadRequestException { 78 : // The --query option is defined in QueryProjects 79 3 : this.hasQuery = params.containsKey("query"); 80 3 : } 81 : 82 : @Override 83 : public RestView<TopLevelResource> list() { 84 3 : if (hasQuery) { 85 0 : return queryProjects.get(); 86 : } 87 3 : return list.get().setFormat(OutputFormat.JSON); 88 : } 89 : 90 : @Override 91 : public ProjectResource parse(TopLevelResource parent, IdString id) 92 : throws RestApiException, IOException, PermissionBackendException { 93 15 : ProjectResource rsrc = _parse(id.get(), true); 94 15 : if (rsrc == null) { 95 6 : throw new ResourceNotFoundException(id); 96 : } 97 15 : return rsrc; 98 : } 99 : 100 : /** 101 : * Parses a project ID from a request body and returns the project. 102 : * 103 : * @param id ID of the project, can be a project name 104 : * @return the project 105 : * @throws RestApiException thrown if the project ID cannot be resolved or if the project is not 106 : * visible to the calling user 107 : * @throws IOException thrown when there is an error. 108 : */ 109 : public ProjectResource parse(String id) 110 : throws RestApiException, IOException, PermissionBackendException { 111 144 : return parse(id, true); 112 : } 113 : 114 : /** 115 : * Parses a project ID from a request body and returns the project. 116 : * 117 : * @param id ID of the project, can be a project name 118 : * @param checkAccess if true, check the project is accessible by the current user 119 : * @return the project 120 : * @throws RestApiException thrown if the project ID cannot be resolved or if the project is not 121 : * visible to the calling user and checkVisibility is true. 122 : * @throws IOException thrown when there is an error. 123 : */ 124 : public ProjectResource parse(String id, boolean checkAccess) 125 : throws RestApiException, IOException, PermissionBackendException { 126 144 : ProjectResource rsrc = _parse(id, checkAccess); 127 144 : if (rsrc == null) { 128 143 : throw new UnprocessableEntityException(String.format("Project Not Found: %s", id)); 129 : } 130 144 : return rsrc; 131 : } 132 : 133 : @Nullable 134 : private ProjectResource _parse(String id, boolean checkAccess) 135 : throws PermissionBackendException, ResourceConflictException { 136 144 : id = ProjectUtil.sanitizeProjectName(id); 137 : 138 144 : Project.NameKey nameKey = Project.nameKey(id); 139 144 : Optional<ProjectState> state = projectCache.get(nameKey); 140 144 : if (!state.isPresent()) { 141 143 : return null; 142 : } 143 : 144 144 : logger.atFine().log("Project %s has state %s", nameKey, state.get().getProject().getState()); 145 : 146 144 : if (checkAccess) { 147 : // Hidden projects(permitsRead = false) should only be accessible by the project owners. 148 : // WRITE_CONFIG is checked here because it's only allowed to project owners (ACCESS may also 149 : // be allowed for other users). Allowing project owners to access here will help them to view 150 : // and update the config of hidden projects easily. 151 144 : if (state.get().statePermitsRead()) { 152 : try { 153 144 : permissionBackend.currentUser().project(nameKey).check(ProjectPermission.ACCESS); 154 4 : } catch (AuthException e) { 155 4 : return null; 156 144 : } 157 : } else { 158 : try { 159 2 : permissionBackend.currentUser().project(nameKey).check(ProjectPermission.WRITE_CONFIG); 160 0 : } catch (AuthException e) { 161 0 : state.get().checkStatePermitsRead(); 162 2 : } 163 : } 164 : } 165 144 : return new ProjectResource(state.get(), user.get()); 166 : } 167 : 168 : @Override 169 : public DynamicMap<RestView<ProjectResource>> views() { 170 14 : return views; 171 : } 172 : }