Line data Source code
1 : // Copyright (C) 2018 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 static com.google.gerrit.entities.RefNames.REFS_HEADS; 18 : 19 : import com.google.common.base.Strings; 20 : import com.google.common.collect.ImmutableList; 21 : import com.google.gerrit.entities.Account; 22 : import com.google.gerrit.entities.BranchNameKey; 23 : import com.google.gerrit.extensions.api.config.AccessCheckInfo; 24 : import com.google.gerrit.extensions.api.config.AccessCheckInput; 25 : import com.google.gerrit.extensions.restapi.AuthException; 26 : import com.google.gerrit.extensions.restapi.BadRequestException; 27 : import com.google.gerrit.extensions.restapi.Response; 28 : import com.google.gerrit.extensions.restapi.RestApiException; 29 : import com.google.gerrit.extensions.restapi.RestReadView; 30 : import com.google.gerrit.server.account.AccountResolver; 31 : import com.google.gerrit.server.git.GitRepositoryManager; 32 : import com.google.gerrit.server.logging.TraceContext; 33 : import com.google.gerrit.server.permissions.DefaultPermissionMappings; 34 : import com.google.gerrit.server.permissions.GlobalPermission; 35 : import com.google.gerrit.server.permissions.PermissionBackend; 36 : import com.google.gerrit.server.permissions.PermissionBackendException; 37 : import com.google.gerrit.server.permissions.ProjectPermission; 38 : import com.google.gerrit.server.permissions.RefPermission; 39 : import com.google.gerrit.server.project.ProjectResource; 40 : import com.google.inject.Inject; 41 : import java.io.IOException; 42 : import java.util.Optional; 43 : import javax.servlet.http.HttpServletResponse; 44 : import org.eclipse.jgit.errors.ConfigInvalidException; 45 : import org.eclipse.jgit.lib.Repository; 46 : import org.kohsuke.args4j.Option; 47 : 48 : public class CheckAccess implements RestReadView<ProjectResource> { 49 : private final AccountResolver accountResolver; 50 : private final PermissionBackend permissionBackend; 51 : private final GitRepositoryManager gitRepositoryManager; 52 : 53 : @Inject 54 : CheckAccess( 55 : AccountResolver resolver, 56 : PermissionBackend permissionBackend, 57 143 : GitRepositoryManager gitRepositoryManager) { 58 143 : this.accountResolver = resolver; 59 143 : this.permissionBackend = permissionBackend; 60 143 : this.gitRepositoryManager = gitRepositoryManager; 61 143 : } 62 : 63 : @Option(name = "--ref", usage = "ref name to check permission for") 64 : String refName; 65 : 66 : @Option(name = "--account", usage = "account to check acccess for") 67 : String account; 68 : 69 : @Option(name = "--perm", usage = "permission to check; default: read of any ref.") 70 : String permission; 71 : 72 : public Response<AccessCheckInfo> apply(ProjectResource rsrc, AccessCheckInput input) 73 : throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException { 74 2 : permissionBackend.user(rsrc.getUser()).check(GlobalPermission.VIEW_ACCESS); 75 : 76 2 : rsrc.getProjectState().checkStatePermitsRead(); 77 : 78 2 : if (input == null) { 79 0 : throw new BadRequestException("input is required"); 80 : } 81 2 : if (Strings.isNullOrEmpty(input.account)) { 82 2 : throw new BadRequestException("input requires 'account'"); 83 : } 84 : 85 1 : try (TraceContext traceContext = TraceContext.open()) { 86 1 : traceContext.enableAclLogging(); 87 : 88 1 : Account.Id match = accountResolver.resolve(input.account).asUnique().account().id(); 89 : 90 : try { 91 1 : permissionBackend 92 1 : .absentUser(match) 93 1 : .project(rsrc.getNameKey()) 94 1 : .check(ProjectPermission.ACCESS); 95 1 : } catch (AuthException e) { 96 1 : return Response.ok( 97 1 : createInfo( 98 : HttpServletResponse.SC_FORBIDDEN, 99 1 : String.format("user %s cannot see project %s", match, rsrc.getName()))); 100 1 : } 101 : RefPermission refPerm; 102 1 : if (!Strings.isNullOrEmpty(input.permission)) { 103 1 : if (Strings.isNullOrEmpty(input.ref)) { 104 1 : throw new BadRequestException("must set 'ref' when specifying 'permission'"); 105 : } 106 1 : Optional<RefPermission> rp = DefaultPermissionMappings.refPermission(input.permission); 107 1 : if (!rp.isPresent()) { 108 1 : throw new BadRequestException( 109 1 : String.format("'%s' is not recognized as ref permission", input.permission)); 110 : } 111 : 112 1 : refPerm = rp.get(); 113 1 : } else { 114 1 : refPerm = RefPermission.READ; 115 : } 116 : 117 1 : String message = null; 118 1 : if (!Strings.isNullOrEmpty(input.ref)) { 119 : try { 120 1 : permissionBackend 121 1 : .absentUser(match) 122 1 : .ref(BranchNameKey.create(rsrc.getNameKey(), input.ref)) 123 1 : .check(refPerm); 124 1 : } catch (AuthException e) { 125 1 : return Response.ok( 126 1 : createInfo( 127 : HttpServletResponse.SC_FORBIDDEN, 128 1 : String.format( 129 : "user %s lacks permission %s for %s in project %s", 130 1 : match, input.permission, input.ref, rsrc.getName()))); 131 1 : } 132 : } else { 133 : // We say access is okay if there are no refs, but this warrants a warning, 134 : // as access denied looks the same as no branches to the user. 135 1 : try (Repository repo = gitRepositoryManager.openRepository(rsrc.getNameKey())) { 136 1 : if (repo.getRefDatabase().getRefsByPrefix(REFS_HEADS).isEmpty()) { 137 1 : message = "access is OK, but repository has no branches under refs/heads/"; 138 : } 139 : } 140 : } 141 1 : return Response.ok(createInfo(HttpServletResponse.SC_OK, message)); 142 1 : } 143 : } 144 : 145 : private AccessCheckInfo createInfo(int statusCode, String message) { 146 1 : AccessCheckInfo info = new AccessCheckInfo(); 147 1 : info.status = statusCode; 148 1 : info.message = message; 149 1 : info.debugLogs = TraceContext.getAclLogRecords(); 150 1 : if (info.debugLogs.isEmpty()) { 151 1 : info.debugLogs = 152 1 : ImmutableList.of("Found no rules that apply, so defaulting to no permission"); 153 : } 154 1 : return info; 155 : } 156 : 157 : @Override 158 : public Response<AccessCheckInfo> apply(ProjectResource rsrc) 159 : throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException { 160 : 161 2 : AccessCheckInput input = new AccessCheckInput(); 162 2 : input.ref = refName; 163 2 : input.account = account; 164 2 : input.permission = permission; 165 : 166 1 : return apply(rsrc, input); 167 : } 168 : }