Line data Source code
1 : // Copyright (C) 2016 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.base.Strings; 18 : import com.google.common.collect.Iterables; 19 : import com.google.gerrit.entities.AccessSection; 20 : import com.google.gerrit.entities.Project; 21 : import com.google.gerrit.exceptions.InvalidNameException; 22 : import com.google.gerrit.extensions.api.access.ProjectAccessInfo; 23 : import com.google.gerrit.extensions.api.access.ProjectAccessInput; 24 : import com.google.gerrit.extensions.restapi.BadRequestException; 25 : import com.google.gerrit.extensions.restapi.ResourceConflictException; 26 : import com.google.gerrit.extensions.restapi.Response; 27 : import com.google.gerrit.extensions.restapi.RestModifyView; 28 : import com.google.gerrit.server.CreateGroupPermissionSyncer; 29 : import com.google.gerrit.server.IdentifiedUser; 30 : import com.google.gerrit.server.account.GroupBackend; 31 : import com.google.gerrit.server.git.meta.MetaDataUpdate; 32 : import com.google.gerrit.server.permissions.GlobalPermission; 33 : import com.google.gerrit.server.permissions.PermissionBackend; 34 : import com.google.gerrit.server.permissions.RefPermission; 35 : import com.google.gerrit.server.project.ProjectCache; 36 : import com.google.gerrit.server.project.ProjectConfig; 37 : import com.google.gerrit.server.project.ProjectResource; 38 : import com.google.inject.Inject; 39 : import com.google.inject.Provider; 40 : import com.google.inject.Singleton; 41 : import java.util.List; 42 : import org.eclipse.jgit.errors.ConfigInvalidException; 43 : 44 : @Singleton 45 : public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessInput> { 46 : protected final GroupBackend groupBackend; 47 : private final PermissionBackend permissionBackend; 48 : private final Provider<MetaDataUpdate.User> metaDataUpdateFactory; 49 : private final GetAccess getAccess; 50 : private final ProjectCache projectCache; 51 : private final Provider<IdentifiedUser> identifiedUser; 52 : private final SetAccessUtil accessUtil; 53 : private final CreateGroupPermissionSyncer createGroupPermissionSyncer; 54 : private final ProjectConfig.Factory projectConfigFactory; 55 : 56 : @Inject 57 : private SetAccess( 58 : GroupBackend groupBackend, 59 : PermissionBackend permissionBackend, 60 : Provider<MetaDataUpdate.User> metaDataUpdateFactory, 61 : ProjectCache projectCache, 62 : GetAccess getAccess, 63 : Provider<IdentifiedUser> identifiedUser, 64 : SetAccessUtil accessUtil, 65 : CreateGroupPermissionSyncer createGroupPermissionSyncer, 66 146 : ProjectConfig.Factory projectConfigFactory) { 67 146 : this.groupBackend = groupBackend; 68 146 : this.permissionBackend = permissionBackend; 69 146 : this.metaDataUpdateFactory = metaDataUpdateFactory; 70 146 : this.getAccess = getAccess; 71 146 : this.projectCache = projectCache; 72 146 : this.identifiedUser = identifiedUser; 73 146 : this.accessUtil = accessUtil; 74 146 : this.createGroupPermissionSyncer = createGroupPermissionSyncer; 75 146 : this.projectConfigFactory = projectConfigFactory; 76 146 : } 77 : 78 : @Override 79 : public Response<ProjectAccessInfo> apply(ProjectResource rsrc, ProjectAccessInput input) 80 : throws Exception { 81 8 : MetaDataUpdate.User metaDataUpdateUser = metaDataUpdateFactory.get(); 82 : 83 : ProjectConfig config; 84 : 85 8 : List<AccessSection> removals = 86 8 : accessUtil.getAccessSections(input.remove, /* rejectNonResolvableGroups= */ false); 87 8 : List<AccessSection> additions = 88 8 : accessUtil.getAccessSections(input.add, /* rejectNonResolvableGroups= */ true); 89 8 : try (MetaDataUpdate md = metaDataUpdateUser.create(rsrc.getNameKey())) { 90 8 : config = projectConfigFactory.read(md); 91 : 92 : // Check that the user has the right permissions. 93 8 : boolean checkedAdmin = false; 94 8 : for (AccessSection section : Iterables.concat(additions, removals)) { 95 7 : boolean isGlobalCapabilities = AccessSection.GLOBAL_CAPABILITIES.equals(section.getName()); 96 7 : if (isGlobalCapabilities) { 97 3 : if (!checkedAdmin) { 98 3 : permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER); 99 3 : checkedAdmin = true; 100 : } 101 : } else { 102 6 : permissionBackend 103 6 : .currentUser() 104 6 : .project(rsrc.getNameKey()) 105 6 : .ref(section.getName()) 106 6 : .check(RefPermission.WRITE_CONFIG); 107 : } 108 7 : } 109 : 110 8 : accessUtil.validateChanges(config, removals, additions); 111 8 : accessUtil.applyChanges(config, removals, additions); 112 : 113 8 : accessUtil.setParentName( 114 8 : identifiedUser.get(), 115 : config, 116 8 : rsrc.getNameKey(), 117 8 : input.parent == null ? null : Project.nameKey(input.parent), 118 : !checkedAdmin); 119 : 120 8 : if (!Strings.isNullOrEmpty(input.message)) { 121 0 : if (!input.message.endsWith("\n")) { 122 0 : input.message += "\n"; 123 : } 124 0 : md.setMessage(input.message); 125 : } else { 126 8 : md.setMessage("Modify access rules\n"); 127 : } 128 : 129 8 : config.commit(md); 130 8 : projectCache.evictAndReindex(config.getProject()); 131 8 : createGroupPermissionSyncer.syncIfNeeded(); 132 1 : } catch (InvalidNameException e) { 133 1 : throw new BadRequestException(e.toString()); 134 0 : } catch (ConfigInvalidException e) { 135 0 : throw new ResourceConflictException(rsrc.getName(), e); 136 8 : } 137 : 138 8 : return Response.ok(getAccess.apply(rsrc.getNameKey())); 139 : } 140 : }