Merge "Rename 'Push Annotated/Signed Tag' permission to 'Create Annotated/Signed Tag'"

This commit is contained in:
David Pursehouse
2016-09-09 06:18:46 +00:00
committed by Gerrit Code Review
17 changed files with 241 additions and 63 deletions

View File

@@ -660,7 +660,8 @@ of merge commits.
[[category_push_annotated]]
=== Push Annotated Tag
[[category_create_annotated]]
=== Create Annotated Tag
This category permits users to push an annotated tag object into the
project's repository. Typically this would be done with a command line
@@ -687,7 +688,7 @@ tagger email address must be verified for the current user.
To push tags created by users other than the current user (such
as tags mirrored from an upstream project), `Forge Committer Identity`
must be also granted in addition to `Push Annotated Tag`.
must be also granted in addition to `Create Annotated Tag`.
To push lightweight (non annotated) tags, grant
<<category_create,`Create Reference`>> for reference name
@@ -706,7 +707,8 @@ is only allowed by granting `Push` with `force` option on `+refs/tags/*+`.
[[category_push_signed]]
=== Push Signed Tag
[[category_create_signed]]
=== Create Signed Tag
This category permits users to push a PGP signed tag object into the
project's repository. Typically this would be done with a command
@@ -1019,7 +1021,7 @@ Suggested access rights to grant:
* <<category_push_merge,`Push merge commit`>> to 'refs/heads/*'
* <<category_forge_committer,`Forge Committer Identity`>> to 'refs/for/refs/heads/*'
* <<category_create,`Create Reference`>> to 'refs/heads/*'
* <<category_push_annotated,`Push Annotated Tag`>> to 'refs/tags/*'
* <<category_create_annotated,`Create Annotated Tag`>> to 'refs/tags/*'
[[examples_project-owner]]

View File

@@ -17,10 +17,10 @@ In particular this error occurs:
link:access-control.html#category_create['Create Reference'] access
right on `+refs/heads/*+`
4. if you push an annotated tag without
link:access-control.html#category_push_annotated['Push Annotated Tag']
link:access-control.html#category_create_annotated['Create Annotated Tag']
access right on `+refs/tags/*+`
5. if you push a signed tag without
link:access-control.html#category_push_signed['Push Signed Tag']
link:access-control.html#category_create_signed['Create Signed Tag']
access right on `+refs/tags/*+`
6. if you push a lightweight tag without the access right link:access-control.html#category_create['Create
Reference'] for the reference name `+refs/tags/*+`

View File

@@ -132,7 +132,7 @@ The entries in the map are sorted by project name.
},
"refs/tags/*": {
"permissions": {
"pushSignedTag": {
"createSignedTag": {
"rules": {
"53a4f647a89ea57992571187d8025f830625192a": {
"action": "ALLOW"
@@ -142,7 +142,7 @@ The entries in the map are sorted by project name.
}
}
},
"pushTag": {
"createTag": {
"rules": {
"53a4f647a89ea57992571187d8025f830625192a": {
"action": "ALLOW"

View File

@@ -403,11 +403,11 @@ To grant this access, configure
link:access-control.html#category_push_direct['Push'] with the
'Force' option ticked.
To push annotated tags, the `Push Annotated Tag` project right must
To push annotated tags, the `Create Annotated Tag` project right must
be granted to one (or more) of the user's groups. There is only
one level of access in this category.
Project owners may wish to grant themselves `Push Annotated Tag`
Project owners may wish to grant themselves `Create Annotated Tag`
only at times when a new release is being prepared, and otherwise
grant nothing at all. This ensures that accidental pushes don't
make undesired changes to the public repository.

View File

@@ -42,7 +42,7 @@ import org.junit.Test;
public class PushTagIT extends AbstractDaemonTest {
enum TagType {
LIGHTWEIGHT(Permission.CREATE),
ANNOTATED(Permission.PUSH_TAG);
ANNOTATED(Permission.CREATE_TAG);
final String createPermission;

View File

@@ -248,7 +248,7 @@ public class TagsIT extends AbstractDaemonTest {
@Test
public void createAnnotatedTagNotAllowed() throws Exception {
block(Permission.PUSH_TAG, REGISTERED_USERS, R_TAGS + "*");
block(Permission.CREATE_TAG, REGISTERED_USERS, R_TAGS + "*");
TagInput input = new TagInput();
input.ref = "test";
input.message = "annotation";
@@ -339,8 +339,8 @@ public class TagsIT extends AbstractDaemonTest {
private void grantTagPermissions() throws Exception {
grant(Permission.CREATE, project, R_TAGS + "*");
grant(Permission.PUSH_TAG, project, R_TAGS + "*");
grant(Permission.PUSH_SIGNED_TAG, project, R_TAGS + "*");
grant(Permission.CREATE_TAG, project, R_TAGS + "*");
grant(Permission.CREATE_SIGNED_TAG, project, R_TAGS + "*");
}
private ListRefsRequest<TagInfo> getTags() throws Exception {

View File

@@ -25,6 +25,8 @@ public class Permission implements Comparable<Permission> {
public static final String ADD_PATCH_SET = "addPatchSet";
public static final String CREATE = "create";
public static final String DELETE = "delete";
public static final String CREATE_TAG = "createTag";
public static final String CREATE_SIGNED_TAG = "createSignedTag";
public static final String DELETE_DRAFTS = "deleteDrafts";
public static final String EDIT_HASHTAGS = "editHashtags";
public static final String EDIT_TOPIC_NAME = "editTopicName";
@@ -37,8 +39,6 @@ public class Permission implements Comparable<Permission> {
public static final String PUBLISH_DRAFTS = "publishDrafts";
public static final String PUSH = "push";
public static final String PUSH_MERGE = "pushMerge";
public static final String PUSH_TAG = "pushTag";
public static final String PUSH_SIGNED_TAG = "pushSignedTag";
public static final String READ = "read";
public static final String REBASE = "rebase";
public static final String REMOVE_REVIEWER = "removeReviewer";
@@ -57,14 +57,14 @@ public class Permission implements Comparable<Permission> {
NAMES_LC.add(ABANDON.toLowerCase());
NAMES_LC.add(ADD_PATCH_SET.toLowerCase());
NAMES_LC.add(CREATE.toLowerCase());
NAMES_LC.add(CREATE_TAG.toLowerCase());
NAMES_LC.add(CREATE_SIGNED_TAG.toLowerCase());
NAMES_LC.add(DELETE.toLowerCase());
NAMES_LC.add(FORGE_AUTHOR.toLowerCase());
NAMES_LC.add(FORGE_COMMITTER.toLowerCase());
NAMES_LC.add(FORGE_SERVER.toLowerCase());
NAMES_LC.add(PUSH.toLowerCase());
NAMES_LC.add(PUSH_MERGE.toLowerCase());
NAMES_LC.add(PUSH_TAG.toLowerCase());
NAMES_LC.add(PUSH_SIGNED_TAG.toLowerCase());
NAMES_LC.add(LABEL.toLowerCase());
NAMES_LC.add(LABEL_AS.toLowerCase());
NAMES_LC.add(REBASE.toLowerCase());

View File

@@ -123,6 +123,8 @@ permissionNames = \
abandon, \
addPatchSet, \
create, \
createTag, \
createSignedTag, \
delete, \
deleteDrafts, \
editHashtags, \
@@ -134,8 +136,6 @@ permissionNames = \
publishDrafts, \
push, \
pushMerge, \
pushTag, \
pushSignedTag, \
read, \
rebase, \
removeReviewer, \
@@ -146,6 +146,8 @@ permissionNames = \
abandon = Abandon
addPatchSet = Add Patch Set
create = Create Reference
createTag = Create Annotated Tag
createSignedTag = Create Signed Tag
delete = Delete Reference
deleteDrafts = Delete Drafts
editHashtags = Edit Hashtags
@@ -157,8 +159,6 @@ owner = Owner
publishDrafts = Publish Drafts
push = Push
pushMerge = Push Merge Commit
pushTag = Push Annotated Tag
pushSignedTag = Push Signed Tag
read = Read
rebase = Rebase
removeReviewer = Remove Reviewer

View File

@@ -91,7 +91,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private static final String PROJECT = "project";
private static final String KEY_DESCRIPTION = "description";
private static final String ACCESS = "access";
public static final String ACCESS = "access";
private static final String KEY_INHERIT_FROM = "inheritFrom";
private static final String KEY_GROUP_PERMISSIONS = "exclusiveGroupPermissions";
@@ -155,6 +155,9 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private static final Set<String> LABEL_FUNCTIONS = ImmutableSet.of(
"MaxWithBlock", "AnyWithBlock", "MaxNoBlock", "NoBlock", "NoOp", "PatchSetLock");
private static final String LEGACY_PERMISSION_PUSH_TAG = "pushTag";
private static final String LEGACY_PERMISSION_PUSH_SIGNED_TAG = "pushSignedTag";
private static final String PLUGIN = "plugin";
private static final SubmitType defaultSubmitAction =
@@ -180,6 +183,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
private Map<String, Config> pluginConfigs;
private boolean checkReceivedObjects;
private Set<String> sectionsWithUnknownPermissions;
private boolean hasLegacyPermissions;
public static ProjectConfig read(MetaDataUpdate update) throws IOException,
ConfigInvalidException {
@@ -627,6 +631,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
for (String varName : rc.getStringList(ACCESS, refName, KEY_GROUP_PERMISSIONS)) {
for (String n : varName.split("[, \t]{1,}")) {
n = convertLegacyPermission(n);
if (isPermission(n)) {
as.getPermission(n, true).setExclusiveGroup(true);
}
@@ -634,10 +639,11 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
for (String varName : rc.getNames(ACCESS, refName)) {
if (isPermission(varName)) {
Permission perm = as.getPermission(varName, true);
String convertedName = convertLegacyPermission(varName);
if (isPermission(convertedName)) {
Permission perm = as.getPermission(convertedName, true);
loadPermissionRules(rc, ACCESS, refName, varName, groupsByName,
perm, Permission.hasRange(varName));
perm, Permission.hasRange(convertedName));
} else {
sectionsWithUnknownPermissions.add(as.getName());
}
@@ -1147,7 +1153,8 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
}
for (String varName : rc.getNames(ACCESS, refName)) {
if (isPermission(varName) && !have.contains(varName.toLowerCase())) {
if (isPermission(convertLegacyPermission(varName))
&& !have.contains(varName.toLowerCase())) {
rc.unset(ACCESS, refName, varName);
}
}
@@ -1282,4 +1289,21 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
Collections.sort(r);
return r;
}
public boolean hasLegacyPermissions() {
return hasLegacyPermissions;
}
private String convertLegacyPermission(String permissionName) {
switch(permissionName) {
case LEGACY_PERMISSION_PUSH_TAG:
hasLegacyPermissions = true;
return Permission.CREATE_TAG;
case LEGACY_PERMISSION_PUSH_SIGNED_TAG:
hasLegacyPermissions = true;
return Permission.CREATE_SIGNED_TAG;
default:
return permissionName;
}
}
}

View File

@@ -116,7 +116,7 @@ public class CreateTag implements RestModifyView<ProjectResource, TagInput> {
if (isSigned) {
throw new MethodNotAllowedException(
"Cannot create signed tag \"" + ref + "\"");
} else if (isAnnotated && !refControl.canPerform(Permission.PUSH_TAG)) {
} else if (isAnnotated && !refControl.canPerform(Permission.CREATE_TAG)) {
throw new AuthException("Cannot create annotated tag \"" + ref + "\"");
} else if (!refControl.canPerform(Permission.CREATE)) {
throw new AuthException("Cannot create tag \"" + ref + "\"");

View File

@@ -329,7 +329,7 @@ public class ProjectControl {
/** @return true if the user can upload to at least one reference */
public Capable canPushToAtLeastOneRef() {
if (!canPerformOnAnyRef(Permission.PUSH) &&
! canPerformOnAnyRef(Permission.PUSH_TAG)) {
!canPerformOnAnyRef(Permission.CREATE_TAG)) {
String pName = state.getProject().getName();
return new Capable("Upload denied for project '" + pName + "'");
}

View File

@@ -317,9 +317,9 @@ public class RefControl {
// than if it doesn't have a PGP signature.
//
if (tag.getFullMessage().contains("-----BEGIN PGP SIGNATURE-----\n")) {
return canPerform(Permission.PUSH_SIGNED_TAG);
return canPerform(Permission.CREATE_SIGNED_TAG);
}
return canPerform(Permission.PUSH_TAG);
return canPerform(Permission.CREATE_TAG);
} else {
return false;
}

View File

@@ -166,8 +166,8 @@ public class AllProjectsCreator {
grant(config, heads, Permission.EDIT_TOPIC_NAME, true, admin, owners);
grant(config, tags, Permission.CREATE, admin, owners);
grant(config, tags, Permission.PUSH_TAG, admin, owners);
grant(config, tags, Permission.PUSH_SIGNED_TAG, admin, owners);
grant(config, tags, Permission.CREATE_TAG, admin, owners);
grant(config, tags, Permission.CREATE_SIGNED_TAG, admin, owners);
grant(config, magic, Permission.PUSH, registered);
grant(config, magic, Permission.PUSH_MERGE, registered);

View File

@@ -0,0 +1,109 @@
// Copyright (C) 2016 The Android Open Source Project
//
// 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 com.google.gerrit.server.schema;
import static com.google.gerrit.server.git.ProjectConfig.ACCESS;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.git.VersionedMetaData;
import com.google.gwtorm.server.OrmException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class ProjectConfigSchemaUpdate extends VersionedMetaData {
private final MetaDataUpdate update;
private Config config;
private boolean updated;
public static ProjectConfigSchemaUpdate read(MetaDataUpdate update)
throws IOException, ConfigInvalidException {
ProjectConfigSchemaUpdate r = new ProjectConfigSchemaUpdate(update);
r.load(update);
return r;
}
private ProjectConfigSchemaUpdate(MetaDataUpdate update) {
this.update = update;
}
@Override
protected String getRefName() {
return RefNames.REFS_CONFIG;
}
@Override
protected void onLoad() throws IOException, ConfigInvalidException {
config = readConfig(ProjectConfig.PROJECT_CONFIG);
}
public void removeForceFromPermission(String name) {
for (String subsection : config.getSubsections(ACCESS)) {
Set<String> names = config.getNames(ACCESS, subsection);
if (names.contains(name)) {
List<String> values =
Arrays.asList(config.getStringList(ACCESS, subsection, name));
values = Lists.transform(values, new Function<String, String>() {
@Override
public String apply(String ruleString) {
PermissionRule rule = PermissionRule.fromString(ruleString, false);
if (rule.getForce()) {
rule.setForce(false);
updated = true;
}
return rule.asString(false);
}
});
config.setStringList(ACCESS, subsection, name, values);
}
}
}
@Override
protected boolean onSave(CommitBuilder commit)
throws IOException, ConfigInvalidException {
saveConfig(ProjectConfig.PROJECT_CONFIG, config);
return true;
}
public void save(PersonIdent personIdent, String commitMessage)
throws OrmException {
if (!updated) {
return;
}
update.getCommitBuilder().setAuthor(personIdent);
update.getCommitBuilder().setCommitter(personIdent);
update.setMessage(commitMessage);
try {
commit(update);
} catch (IOException e) {
throw new OrmException(e);
}
}
}

View File

@@ -33,7 +33,7 @@ import java.util.List;
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
public static final Class<Schema_130> C = Schema_130.class;
public static final Class<Schema_131> C = Schema_131.class;
public static int getBinaryVersion() {
return guessVersion(C);

View File

@@ -14,16 +14,12 @@
package com.google.gerrit.server.schema;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -59,30 +55,9 @@ public class Schema_130 extends SchemaVersion {
try (Repository git = repoManager.openRepository(projectName);
MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED,
projectName, git)) {
ProjectConfig config = ProjectConfig.read(md);
boolean update = false;
for (AccessSection accessSection : config.getAccessSections()) {
Permission pushTagPermission =
accessSection.getPermission(Permission.PUSH_TAG);
if (pushTagPermission == null) {
continue;
}
for (PermissionRule rule : pushTagPermission.getRules()) {
if (rule.getForce()) {
rule.setForce(false);
update = true;
}
}
}
if (!update) {
continue;
}
md.getCommitBuilder().setAuthor(serverUser);
md.getCommitBuilder().setCommitter(serverUser);
md.setMessage(COMMIT_MSG);
config.commit(md);
ProjectConfigSchemaUpdate cfg = ProjectConfigSchemaUpdate.read(md);
cfg.removeForceFromPermission("pushTag");
cfg.save(serverUser, COMMIT_MSG);
} catch (ConfigInvalidException | IOException ex) {
throw new OrmException(ex);
}

View File

@@ -0,0 +1,68 @@
// Copyright (C) 2016 The Android Open Source Project
//
// 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 com.google.gerrit.server.schema;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import java.io.IOException;
public class Schema_131 extends SchemaVersion {
private static final String COMMIT_MSG =
"Rename 'Push Annotated/Signed Tag' permission to 'Create Annotated/Signed Tag'";
private final GitRepositoryManager repoManager;
private final PersonIdent serverUser;
@Inject
Schema_131(Provider<Schema_130> prior,
GitRepositoryManager repoManager,
@GerritPersonIdent PersonIdent serverUser) {
super(prior);
this.repoManager = repoManager;
this.serverUser = serverUser;
}
@Override
protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
for (Project.NameKey projectName : repoManager.list()) {
try (Repository git = repoManager.openRepository(projectName);
MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED,
projectName, git)) {
ProjectConfig config = ProjectConfig.read(md);
if (config.hasLegacyPermissions()) {
md.getCommitBuilder().setAuthor(serverUser);
md.getCommitBuilder().setCommitter(serverUser);
md.setMessage(COMMIT_MSG);
config.commit(md);
}
} catch (ConfigInvalidException | IOException ex) {
throw new OrmException(ex);
}
}
}
}