Merge "Added /projects/name/access as REST endpoint. Implemented GET."
This commit is contained in:
@@ -2472,6 +2472,61 @@ The path to the `GerritSiteHeader.html` file.
|
||||
The path to the `GerritSiteFooter.html` file.
|
||||
|=============================
|
||||
|
||||
[[get-access]]
|
||||
=== List Access Rights for Project
|
||||
--
|
||||
'GET //projects/link:rest-api-projects.html#project-name[\{project-name\}]/access'
|
||||
--
|
||||
|
||||
Lists the access rights for a single project.
|
||||
|
||||
As result a link:#project-access-info[ProjectAccessInfo] entity is returned.
|
||||
|
||||
.Request
|
||||
----
|
||||
GET /projects/MyProject/access HTTP/1.0
|
||||
----
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
)]}'
|
||||
{
|
||||
"revision": "61157ed63e14d261b6dca40650472a9b0bd88474",
|
||||
"inherits_from": {
|
||||
"id": "All-Projects",
|
||||
"name": "All-Projects",
|
||||
"description": "Access inherited by all other projects."
|
||||
},
|
||||
"local": {
|
||||
"refs/*": {
|
||||
"permissions": {
|
||||
"read": {
|
||||
"rules": {
|
||||
"c2ce4749a32ceb82cd6adcce65b8216e12afb41c": {
|
||||
"action": "ALLOW",
|
||||
"force": false
|
||||
},
|
||||
"global:Anonymous-Users": {
|
||||
"action": "ALLOW",
|
||||
"force": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"is_owner": true,
|
||||
"owner_of": [
|
||||
"refs/*"
|
||||
],
|
||||
"can_upload": true,
|
||||
"can_add": true,
|
||||
"config_visible": true
|
||||
}
|
||||
----
|
||||
|
||||
GERRIT
|
||||
------
|
||||
|
@@ -0,0 +1,30 @@
|
||||
// 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.acceptance.rest.project;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
import com.google.gerrit.server.config.AllProjectsNameProvider;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class AccessIT extends AbstractDaemonTest {
|
||||
@Test
|
||||
public void testGetDefaultInheritance() throws Exception {
|
||||
String newProjectName = createProject("newProjectAccess").get();
|
||||
String inheritedName = gApi.projects()
|
||||
.name(newProjectName).access().inheritsFrom.name;
|
||||
assertThat(inheritedName).isEqualTo(AllProjectsNameProvider.DEFAULT);
|
||||
}
|
||||
}
|
@@ -264,6 +264,11 @@ public class PermissionRule implements Comparable<PermissionRule> {
|
||||
return rule;
|
||||
}
|
||||
|
||||
public boolean hasRange() {
|
||||
return (!(getMin() == null || getMin() == 0))
|
||||
|| (!(getMax() == null || getMax() == 0));
|
||||
}
|
||||
|
||||
public static int parseInt(String value) {
|
||||
if (value.startsWith("+")) {
|
||||
value = value.substring(1);
|
||||
|
@@ -0,0 +1,20 @@
|
||||
// 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.extensions.api.access;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class AccessSectionInfo {
|
||||
public Map<String, PermissionInfo> permissions;
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
// 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.extensions.api.access;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class PermissionInfo {
|
||||
public String label;
|
||||
public Boolean exclusive;
|
||||
public Map<String, PermissionRuleInfo> rules;
|
||||
|
||||
public PermissionInfo(String label, Boolean exclusive) {
|
||||
this.label = label;
|
||||
this.exclusive = exclusive;
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
// 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.extensions.api.access;
|
||||
|
||||
public class PermissionRuleInfo {
|
||||
public enum Action {
|
||||
ALLOW,
|
||||
DENY,
|
||||
BLOCK,
|
||||
INTERACTIVE,
|
||||
BATCH
|
||||
}
|
||||
|
||||
public Action action;
|
||||
public Boolean force;
|
||||
public Integer min;
|
||||
public Integer max;
|
||||
|
||||
public PermissionRuleInfo(Action action, Boolean force) {
|
||||
this.action = action;
|
||||
this.force = force;
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
// 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.extensions.api.access;
|
||||
|
||||
import com.google.gerrit.extensions.common.ProjectInfo;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class ProjectAccessInfo {
|
||||
public String revision;
|
||||
public ProjectInfo inheritsFrom;
|
||||
public Map<String, AccessSectionInfo> local;
|
||||
public Boolean isOwner;
|
||||
public Set<String> ownerOf;
|
||||
public Boolean canUpload;
|
||||
public Boolean canAdd;
|
||||
public Boolean configVisible;
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
// 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.extensions.api.projects;
|
||||
|
||||
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
|
||||
import com.google.gerrit.extensions.restapi.NotImplementedException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
|
||||
public interface AccessApi {
|
||||
ProjectAccessInfo get() throws RestApiException;
|
||||
|
||||
/**
|
||||
* A default implementation which allows source compatibility
|
||||
* when adding new methods to the interface.
|
||||
**/
|
||||
class NotImplemented implements AccessApi {
|
||||
@Override
|
||||
public ProjectAccessInfo get() throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,10 +14,12 @@
|
||||
|
||||
package com.google.gerrit.extensions.api.projects;
|
||||
|
||||
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
|
||||
import com.google.gerrit.extensions.common.ProjectInfo;
|
||||
import com.google.gerrit.extensions.restapi.NotImplementedException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public interface ProjectApi {
|
||||
@@ -28,6 +30,8 @@ public interface ProjectApi {
|
||||
String description() throws RestApiException;
|
||||
void description(PutDescriptionInput in) throws RestApiException;
|
||||
|
||||
ProjectAccessInfo access() throws RestApiException;
|
||||
|
||||
ListRefsRequest<BranchInfo> branches();
|
||||
ListRefsRequest<TagInfo> tags();
|
||||
|
||||
@@ -129,6 +133,11 @@ public interface ProjectApi {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectAccessInfo access() throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void description(PutDescriptionInput in)
|
||||
throws RestApiException {
|
||||
|
@@ -14,47 +14,22 @@
|
||||
|
||||
package com.google.gerrit.server.access;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
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.common.data.RefConfigSection;
|
||||
import com.google.gerrit.common.errors.NoSuchGroupException;
|
||||
import com.google.gerrit.extensions.common.ProjectInfo;
|
||||
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.account.GroupBackend;
|
||||
import com.google.gerrit.server.account.GroupControl;
|
||||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gerrit.server.project.ProjectJson;
|
||||
import com.google.gerrit.server.project.ProjectState;
|
||||
import com.google.gerrit.server.project.RefControl;
|
||||
import com.google.gerrit.server.project.GetAccess;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class ListAccess implements RestReadView<TopLevelResource> {
|
||||
|
||||
@@ -62,237 +37,22 @@ public class ListAccess implements RestReadView<TopLevelResource> {
|
||||
usage = "projects for which the access rights should be returned")
|
||||
private List<String> projects = Lists.newArrayList();
|
||||
|
||||
private final Provider<CurrentUser> self;
|
||||
private final ProjectControl.GenericFactory projectControlFactory;
|
||||
private final ProjectCache projectCache;
|
||||
private final ProjectJson projectJson;
|
||||
private final MetaDataUpdate.Server metaDataUpdateFactory;
|
||||
private final GroupControl.Factory groupControlFactory;
|
||||
private final GroupBackend groupBackend;
|
||||
private final AllProjectsName allProjectsName;
|
||||
private final GetAccess getAccess;
|
||||
|
||||
@Inject
|
||||
public ListAccess(Provider<CurrentUser> self,
|
||||
ProjectControl.GenericFactory projectControlFactory,
|
||||
ProjectCache projectCache, ProjectJson projectJson,
|
||||
MetaDataUpdate.Server metaDataUpdateFactory,
|
||||
GroupControl.Factory groupControlFactory, GroupBackend groupBackend,
|
||||
AllProjectsName allProjectsName) {
|
||||
this.self = self;
|
||||
this.projectControlFactory = projectControlFactory;
|
||||
this.projectCache = projectCache;
|
||||
this.projectJson = projectJson;
|
||||
this.metaDataUpdateFactory = metaDataUpdateFactory;
|
||||
this.groupControlFactory = groupControlFactory;
|
||||
this.groupBackend = groupBackend;
|
||||
this.allProjectsName = allProjectsName;
|
||||
public ListAccess(GetAccess getAccess) {
|
||||
this.getAccess = getAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ProjectAccessInfo> apply(TopLevelResource resource)
|
||||
throws ResourceNotFoundException, ResourceConflictException, IOException {
|
||||
Map<String, ProjectAccessInfo> access = Maps.newTreeMap();
|
||||
for (String p: projects) {
|
||||
// Load the current configuration from the repository, ensuring it's the most
|
||||
// recent version available. If it differs from what was in the project
|
||||
// state, force a cache flush now.
|
||||
//
|
||||
for (String p : projects) {
|
||||
Project.NameKey projectName = new Project.NameKey(p);
|
||||
try (MetaDataUpdate md = metaDataUpdateFactory.create(projectName)) {
|
||||
ProjectControl pc = open(projectName);
|
||||
ProjectConfig config = ProjectConfig.read(md);
|
||||
|
||||
if (config.updateGroupNames(groupBackend)) {
|
||||
md.setMessage("Update group names\n");
|
||||
config.commit(md);
|
||||
projectCache.evict(config.getProject());
|
||||
pc = open(projectName);
|
||||
} else if (config.getRevision() != null
|
||||
&& !config.getRevision().equals(
|
||||
pc.getProjectState().getConfig().getRevision())) {
|
||||
projectCache.evict(config.getProject());
|
||||
pc = open(projectName);
|
||||
}
|
||||
access.put(p, new ProjectAccessInfo(pc, config));
|
||||
} catch (ConfigInvalidException e) {
|
||||
throw new ResourceConflictException(e.getMessage());
|
||||
} catch (RepositoryNotFoundException e) {
|
||||
throw new ResourceNotFoundException(p);
|
||||
}
|
||||
access.put(p, getAccess.apply(projectName));
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
||||
private ProjectControl open(Project.NameKey projectName)
|
||||
throws ResourceNotFoundException, IOException {
|
||||
try {
|
||||
return projectControlFactory.validateFor(projectName,
|
||||
ProjectControl.OWNER | ProjectControl.VISIBLE, self.get());
|
||||
} catch (NoSuchProjectException e) {
|
||||
throw new ResourceNotFoundException(projectName.get());
|
||||
}
|
||||
}
|
||||
|
||||
public class ProjectAccessInfo {
|
||||
public String revision;
|
||||
public ProjectInfo inheritsFrom;
|
||||
public Map<String, AccessSectionInfo> local;
|
||||
public Boolean isOwner;
|
||||
public Set<String> ownerOf;
|
||||
public Boolean canUpload;
|
||||
public Boolean canAdd;
|
||||
public Boolean configVisible;
|
||||
|
||||
public ProjectAccessInfo(ProjectControl pc, ProjectConfig config) {
|
||||
final RefControl metaConfigControl =
|
||||
pc.controlForRef(RefNames.REFS_CONFIG);
|
||||
local = Maps.newHashMap();
|
||||
ownerOf = Sets.newHashSet();
|
||||
Map<AccountGroup.UUID, Boolean> visibleGroups = new HashMap<>();
|
||||
|
||||
for (AccessSection section : config.getAccessSections()) {
|
||||
String name = section.getName();
|
||||
if (AccessSection.GLOBAL_CAPABILITIES.equals(name)) {
|
||||
if (pc.isOwner()) {
|
||||
local.put(name, new AccessSectionInfo(section));
|
||||
ownerOf.add(name);
|
||||
|
||||
} else if (metaConfigControl.isVisible()) {
|
||||
local.put(section.getName(), new AccessSectionInfo(section));
|
||||
}
|
||||
|
||||
} else if (RefConfigSection.isValid(name)) {
|
||||
RefControl rc = pc.controlForRef(name);
|
||||
if (rc.isOwner()) {
|
||||
local.put(name, new AccessSectionInfo(section));
|
||||
ownerOf.add(name);
|
||||
|
||||
} else if (metaConfigControl.isVisible()) {
|
||||
local.put(name, new AccessSectionInfo(section));
|
||||
|
||||
} else if (rc.isVisible()) {
|
||||
// Filter the section to only add rules describing groups that
|
||||
// are visible to the current-user. This includes any group the
|
||||
// user is a member of, as well as groups they own or that
|
||||
// are visible to all users.
|
||||
|
||||
AccessSection dst = null;
|
||||
for (Permission srcPerm : section.getPermissions()) {
|
||||
Permission dstPerm = null;
|
||||
|
||||
for (PermissionRule srcRule : srcPerm.getRules()) {
|
||||
AccountGroup.UUID group = srcRule.getGroup().getUUID();
|
||||
if (group == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Boolean canSeeGroup = visibleGroups.get(group);
|
||||
if (canSeeGroup == null) {
|
||||
try {
|
||||
canSeeGroup = groupControlFactory.controlFor(group).isVisible();
|
||||
} catch (NoSuchGroupException e) {
|
||||
canSeeGroup = Boolean.FALSE;
|
||||
}
|
||||
visibleGroups.put(group, canSeeGroup);
|
||||
}
|
||||
|
||||
if (canSeeGroup) {
|
||||
if (dstPerm == null) {
|
||||
if (dst == null) {
|
||||
dst = new AccessSection(name);
|
||||
local.put(name, new AccessSectionInfo(dst));
|
||||
}
|
||||
dstPerm = dst.getPermission(srcPerm.getName(), true);
|
||||
}
|
||||
dstPerm.add(srcRule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ownerOf.isEmpty() && pc.isOwnerAnyRef()) {
|
||||
// Special case: If the section list is empty, this project has no current
|
||||
// access control information. Rely on what ProjectControl determines
|
||||
// is ownership, which probably means falling back to site administrators.
|
||||
ownerOf.add(AccessSection.ALL);
|
||||
}
|
||||
|
||||
|
||||
if (config.getRevision() != null) {
|
||||
revision = config.getRevision().name();
|
||||
}
|
||||
|
||||
ProjectState parent =
|
||||
Iterables.getFirst(pc.getProjectState().parents(), null);
|
||||
if (parent != null) {
|
||||
inheritsFrom = projectJson.format(parent.getProject());
|
||||
}
|
||||
|
||||
if (pc.getProject().getNameKey().equals(allProjectsName)) {
|
||||
if (pc.isOwner()) {
|
||||
ownerOf.add(AccessSection.GLOBAL_CAPABILITIES);
|
||||
}
|
||||
}
|
||||
|
||||
isOwner = toBoolean(pc.isOwner());
|
||||
canUpload = toBoolean(pc.isOwner()
|
||||
|| (metaConfigControl.isVisible() && metaConfigControl.canUpload()));
|
||||
canAdd = toBoolean(pc.canAddRefs());
|
||||
configVisible = pc.isOwner() || metaConfigControl.isVisible();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AccessSectionInfo {
|
||||
public Map<String, PermissionInfo> permissions;
|
||||
|
||||
public AccessSectionInfo(AccessSection section) {
|
||||
permissions = Maps.newHashMap();
|
||||
for (Permission p : section.getPermissions()) {
|
||||
permissions.put(p.getName(), new PermissionInfo(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class PermissionInfo {
|
||||
public String label;
|
||||
public Boolean exclusive;
|
||||
public Map<String, PermissionRuleInfo> rules;
|
||||
|
||||
public PermissionInfo(Permission permission) {
|
||||
label = permission.getLabel();
|
||||
exclusive = toBoolean(permission.getExclusiveGroup());
|
||||
rules = Maps.newHashMap();
|
||||
for (PermissionRule r : permission.getRules()) {
|
||||
rules.put(r.getGroup().getUUID().get(), new PermissionRuleInfo(r));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class PermissionRuleInfo {
|
||||
public PermissionRule.Action action;
|
||||
public Boolean force;
|
||||
public Integer min;
|
||||
public Integer max;
|
||||
|
||||
|
||||
public PermissionRuleInfo(PermissionRule rule) {
|
||||
action = rule.getAction();
|
||||
force = toBoolean(rule.getForce());
|
||||
if (hasRange(rule)) {
|
||||
min = rule.getMin();
|
||||
max = rule.getMax();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasRange(PermissionRule rule) {
|
||||
return (!(rule.getMin() == null || rule.getMin() == 0))
|
||||
|| (!(rule.getMax() == null || rule.getMax() == 0));
|
||||
}
|
||||
}
|
||||
|
||||
private static Boolean toBoolean(boolean value) {
|
||||
return value ? true : null;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,50 @@
|
||||
// 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.api.projects;
|
||||
|
||||
import com.google.gerrit.extensions.api.projects.AccessApi;
|
||||
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.server.project.GetAccess;
|
||||
import com.google.gerrit.server.project.ProjectResource;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AccessApiImpl implements AccessApi {
|
||||
interface Factory {
|
||||
AccessApiImpl create(ProjectResource project);
|
||||
}
|
||||
|
||||
private final ProjectResource project;
|
||||
private final GetAccess getAccess;
|
||||
|
||||
@Inject
|
||||
AccessApiImpl(GetAccess getAccess,
|
||||
@Assisted ProjectResource project) {
|
||||
this.project = project;
|
||||
this.getAccess = getAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectAccessInfo get() throws RestApiException {
|
||||
try {
|
||||
return getAccess.apply(project);
|
||||
} catch (IOException e) {
|
||||
throw new RestApiException("Cannot get access rights", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -26,5 +26,6 @@ public class Module extends FactoryModule {
|
||||
factory(TagApiImpl.Factory.class);
|
||||
factory(ProjectApiImpl.Factory.class);
|
||||
factory(ChildProjectApiImpl.Factory.class);
|
||||
factory(AccessApiImpl.Factory.class);
|
||||
}
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ import static com.google.gerrit.server.account.CapabilityUtils.checkRequiresCapa
|
||||
import com.google.gerrit.extensions.api.projects.BranchApi;
|
||||
import com.google.gerrit.extensions.api.projects.BranchInfo;
|
||||
import com.google.gerrit.extensions.api.projects.ChildProjectApi;
|
||||
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
|
||||
import com.google.gerrit.extensions.api.projects.ProjectApi;
|
||||
import com.google.gerrit.extensions.api.projects.ProjectInput;
|
||||
import com.google.gerrit.extensions.api.projects.PutDescriptionInput;
|
||||
@@ -70,6 +71,7 @@ public class ProjectApiImpl implements ProjectApi {
|
||||
private final String name;
|
||||
private final BranchApiImpl.Factory branchApi;
|
||||
private final TagApiImpl.Factory tagApi;
|
||||
private final AccessApiImpl.Factory accessApi;
|
||||
private final Provider<ListBranches> listBranchesProvider;
|
||||
private final Provider<ListTags> listTagsProvider;
|
||||
|
||||
@@ -85,12 +87,14 @@ public class ProjectApiImpl implements ProjectApi {
|
||||
ProjectJson projectJson,
|
||||
BranchApiImpl.Factory branchApiFactory,
|
||||
TagApiImpl.Factory tagApiFactory,
|
||||
AccessApiImpl.Factory accessApiFactory,
|
||||
Provider<ListBranches> listBranchesProvider,
|
||||
Provider<ListTags> listTagsProvider,
|
||||
@Assisted ProjectResource project) {
|
||||
this(user, createProjectFactory, projectApi, projects, getDescription,
|
||||
putDescription, childApi, children, projectJson, branchApiFactory,
|
||||
tagApiFactory, listBranchesProvider, listTagsProvider, project, null);
|
||||
tagApiFactory, accessApiFactory, listBranchesProvider, listTagsProvider,
|
||||
project, null);
|
||||
}
|
||||
|
||||
@AssistedInject
|
||||
@@ -105,12 +109,14 @@ public class ProjectApiImpl implements ProjectApi {
|
||||
ProjectJson projectJson,
|
||||
BranchApiImpl.Factory branchApiFactory,
|
||||
TagApiImpl.Factory tagApiFactory,
|
||||
AccessApiImpl.Factory accessApiFactory,
|
||||
Provider<ListBranches> listBranchesProvider,
|
||||
Provider<ListTags> listTagsProvider,
|
||||
@Assisted String name) {
|
||||
this(user, createProjectFactory, projectApi, projects, getDescription,
|
||||
putDescription, childApi, children, projectJson, branchApiFactory,
|
||||
tagApiFactory, listBranchesProvider, listTagsProvider, null, name);
|
||||
tagApiFactory, accessApiFactory, listBranchesProvider, listTagsProvider,
|
||||
null, name);
|
||||
}
|
||||
|
||||
private ProjectApiImpl(Provider<CurrentUser> user,
|
||||
@@ -124,6 +130,7 @@ public class ProjectApiImpl implements ProjectApi {
|
||||
ProjectJson projectJson,
|
||||
BranchApiImpl.Factory branchApiFactory,
|
||||
TagApiImpl.Factory tagApiFactory,
|
||||
AccessApiImpl.Factory accessApiFactory,
|
||||
Provider<ListBranches> listBranchesProvider,
|
||||
Provider<ListTags> listTagsProvider,
|
||||
ProjectResource project,
|
||||
@@ -141,6 +148,7 @@ public class ProjectApiImpl implements ProjectApi {
|
||||
this.name = name;
|
||||
this.branchApi = branchApiFactory;
|
||||
this.tagApi = tagApiFactory;
|
||||
this.accessApi = accessApiFactory;
|
||||
this.listBranchesProvider = listBranchesProvider;
|
||||
this.listTagsProvider = listTagsProvider;
|
||||
}
|
||||
@@ -181,6 +189,11 @@ public class ProjectApiImpl implements ProjectApi {
|
||||
return getDescription.apply(checkExists());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectAccessInfo access() throws RestApiException {
|
||||
return accessApi.create(checkExists()).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void description(PutDescriptionInput in)
|
||||
throws RestApiException {
|
||||
|
@@ -0,0 +1,270 @@
|
||||
// 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.project;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
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.common.data.RefConfigSection;
|
||||
import com.google.gerrit.common.errors.NoSuchGroupException;
|
||||
import com.google.gerrit.extensions.api.access.AccessSectionInfo;
|
||||
import com.google.gerrit.extensions.api.access.PermissionInfo;
|
||||
import com.google.gerrit.extensions.api.access.PermissionRuleInfo;
|
||||
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.account.GroupBackend;
|
||||
import com.google.gerrit.server.account.GroupControl;
|
||||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Singleton
|
||||
public class GetAccess implements RestReadView<ProjectResource> {
|
||||
|
||||
private static final ImmutableMap<PermissionRule.Action, PermissionRuleInfo.Action> ACTION_TYPE =
|
||||
Maps.immutableEnumMap(
|
||||
new ImmutableMap.Builder<PermissionRule.Action, PermissionRuleInfo.Action>()
|
||||
.put(PermissionRule.Action.ALLOW, PermissionRuleInfo.Action.ALLOW)
|
||||
.put(PermissionRule.Action.BATCH, PermissionRuleInfo.Action.BATCH)
|
||||
.put(PermissionRule.Action.BLOCK, PermissionRuleInfo.Action.BLOCK)
|
||||
.put(PermissionRule.Action.DENY, PermissionRuleInfo.Action.DENY)
|
||||
.put(PermissionRule.Action.INTERACTIVE,
|
||||
PermissionRuleInfo.Action.INTERACTIVE)
|
||||
.build());
|
||||
|
||||
private final Provider<CurrentUser> self;
|
||||
private final GroupControl.Factory groupControlFactory;
|
||||
private final AllProjectsName allProjectsName;
|
||||
private final ProjectJson projectJson;
|
||||
private final ProjectCache projectCache;
|
||||
private final MetaDataUpdate.Server metaDataUpdateFactory;
|
||||
private final ProjectControl.GenericFactory projectControlFactory;
|
||||
private final GroupBackend groupBackend;
|
||||
|
||||
@Inject
|
||||
public GetAccess(Provider<CurrentUser> self,
|
||||
GroupControl.Factory groupControlFactory,
|
||||
AllProjectsName allProjectsName,
|
||||
ProjectCache projectCache,
|
||||
MetaDataUpdate.Server metaDataUpdateFactory,
|
||||
ProjectJson projectJson,
|
||||
ProjectControl.GenericFactory projectControlFactory,
|
||||
GroupBackend groupBackend) {
|
||||
this.self = self;
|
||||
this.groupControlFactory = groupControlFactory;
|
||||
this.allProjectsName = allProjectsName;
|
||||
this.projectJson = projectJson;
|
||||
this.projectCache = projectCache;
|
||||
this.projectControlFactory = projectControlFactory;
|
||||
this.metaDataUpdateFactory = metaDataUpdateFactory;
|
||||
this.groupBackend = groupBackend;
|
||||
}
|
||||
|
||||
public ProjectAccessInfo apply(Project.NameKey nameKey)
|
||||
throws ResourceNotFoundException, ResourceConflictException, IOException {
|
||||
try {
|
||||
return this.apply(new ProjectResource(
|
||||
projectControlFactory.controlFor(nameKey, self.get())));
|
||||
} catch (NoSuchProjectException e) {
|
||||
throw new ResourceNotFoundException(nameKey.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectAccessInfo apply(ProjectResource rsrc)
|
||||
throws ResourceNotFoundException, ResourceConflictException, IOException {
|
||||
// Load the current configuration from the repository, ensuring it's the most
|
||||
// recent version available. If it differs from what was in the project
|
||||
// state, force a cache flush now.
|
||||
//
|
||||
Project.NameKey projectName = rsrc.getNameKey();
|
||||
ProjectAccessInfo info = new ProjectAccessInfo();
|
||||
ProjectConfig config;
|
||||
ProjectControl pc = open(projectName);
|
||||
RefControl metaConfigControl = pc.controlForRef(RefNames.REFS_CONFIG);
|
||||
try (MetaDataUpdate md = metaDataUpdateFactory.create(projectName)) {
|
||||
config = ProjectConfig.read(md);
|
||||
|
||||
if (config.updateGroupNames(groupBackend)) {
|
||||
md.setMessage("Update group names\n");
|
||||
config.commit(md);
|
||||
projectCache.evict(config.getProject());
|
||||
pc = open(projectName);
|
||||
} else if (config.getRevision() != null
|
||||
&& !config.getRevision().equals(
|
||||
pc.getProjectState().getConfig().getRevision())) {
|
||||
projectCache.evict(config.getProject());
|
||||
pc = open(projectName);
|
||||
}
|
||||
} catch (ConfigInvalidException e) {
|
||||
throw new ResourceConflictException(e.getMessage());
|
||||
} catch (RepositoryNotFoundException e) {
|
||||
throw new ResourceNotFoundException(rsrc.getName());
|
||||
}
|
||||
|
||||
info.local = new HashMap<>();
|
||||
info.ownerOf = Sets.newHashSet();
|
||||
Map<AccountGroup.UUID, Boolean> visibleGroups = new HashMap<>();
|
||||
|
||||
for (AccessSection section : config.getAccessSections()) {
|
||||
String name = section.getName();
|
||||
if (AccessSection.GLOBAL_CAPABILITIES.equals(name)) {
|
||||
if (pc.isOwner()) {
|
||||
info.local.put(name, createAccessSection(section));
|
||||
info.ownerOf.add(name);
|
||||
|
||||
} else if (metaConfigControl.isVisible()) {
|
||||
info.local.put(section.getName(), createAccessSection(section));
|
||||
}
|
||||
|
||||
} else if (RefConfigSection.isValid(name)) {
|
||||
RefControl rc = pc.controlForRef(name);
|
||||
if (rc.isOwner()) {
|
||||
info.local.put(name, createAccessSection(section));
|
||||
info.ownerOf.add(name);
|
||||
|
||||
} else if (metaConfigControl.isVisible()) {
|
||||
info.local.put(name, createAccessSection(section));
|
||||
|
||||
} else if (rc.isVisible()) {
|
||||
// Filter the section to only add rules describing groups that
|
||||
// are visible to the current-user. This includes any group the
|
||||
// user is a member of, as well as groups they own or that
|
||||
// are visible to all users.
|
||||
|
||||
AccessSection dst = null;
|
||||
for (Permission srcPerm : section.getPermissions()) {
|
||||
Permission dstPerm = null;
|
||||
|
||||
for (PermissionRule srcRule : srcPerm.getRules()) {
|
||||
AccountGroup.UUID group = srcRule.getGroup().getUUID();
|
||||
if (group == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Boolean canSeeGroup = visibleGroups.get(group);
|
||||
if (canSeeGroup == null) {
|
||||
try {
|
||||
canSeeGroup = groupControlFactory.controlFor(group)
|
||||
.isVisible();
|
||||
} catch (NoSuchGroupException e) {
|
||||
canSeeGroup = Boolean.FALSE;
|
||||
}
|
||||
visibleGroups.put(group, canSeeGroup);
|
||||
}
|
||||
|
||||
if (canSeeGroup) {
|
||||
if (dstPerm == null) {
|
||||
if (dst == null) {
|
||||
dst = new AccessSection(name);
|
||||
info.local.put(name, createAccessSection(dst));
|
||||
}
|
||||
dstPerm = dst.getPermission(srcPerm.getName(), true);
|
||||
}
|
||||
dstPerm.add(srcRule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info.ownerOf.isEmpty() && pc.isOwnerAnyRef()) {
|
||||
// Special case: If the section list is empty, this project has no current
|
||||
// access control information. Rely on what ProjectControl determines
|
||||
// is ownership, which probably means falling back to site administrators.
|
||||
info.ownerOf.add(AccessSection.ALL);
|
||||
}
|
||||
|
||||
if (config.getRevision() != null) {
|
||||
info.revision = config.getRevision().name();
|
||||
}
|
||||
|
||||
ProjectState parent =
|
||||
Iterables.getFirst(pc.getProjectState().parents(), null);
|
||||
if (parent != null) {
|
||||
info.inheritsFrom = projectJson.format(parent.getProject());
|
||||
}
|
||||
|
||||
if (pc.getProject().getNameKey().equals(allProjectsName)) {
|
||||
if (pc.isOwner()) {
|
||||
info.ownerOf.add(AccessSection.GLOBAL_CAPABILITIES);
|
||||
}
|
||||
}
|
||||
|
||||
info.isOwner = toBoolean(pc.isOwner());
|
||||
info.canUpload = toBoolean(pc.isOwner()
|
||||
|| (metaConfigControl.isVisible() && metaConfigControl.canUpload()));
|
||||
info.canAdd = toBoolean(pc.canAddRefs());
|
||||
info.configVisible = pc.isOwner() || metaConfigControl.isVisible();
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private AccessSectionInfo createAccessSection(AccessSection section) {
|
||||
AccessSectionInfo accessSectionInfo = new AccessSectionInfo();
|
||||
accessSectionInfo.permissions = new HashMap<>();
|
||||
for (Permission p : section.getPermissions()) {
|
||||
PermissionInfo pInfo = new PermissionInfo(p.getLabel(),
|
||||
p.getExclusiveGroup() ? true : null);
|
||||
pInfo.rules = new HashMap<>();
|
||||
for (PermissionRule r : p.getRules()) {
|
||||
PermissionRuleInfo info = new PermissionRuleInfo(
|
||||
ACTION_TYPE.get(r.getAction()), r.getForce());
|
||||
if (r.hasRange()) {
|
||||
info.max = r.getMax();
|
||||
info.min = r.getMin();
|
||||
}
|
||||
pInfo.rules.put(r.getGroup().getUUID().get(), info);
|
||||
}
|
||||
accessSectionInfo.permissions.put(p.getName(), pInfo);
|
||||
}
|
||||
return accessSectionInfo;
|
||||
}
|
||||
|
||||
private ProjectControl open(Project.NameKey projectName)
|
||||
throws ResourceNotFoundException, IOException {
|
||||
try {
|
||||
return projectControlFactory.validateFor(projectName,
|
||||
ProjectControl.OWNER | ProjectControl.VISIBLE, self.get());
|
||||
} catch (NoSuchProjectException e) {
|
||||
throw new ResourceNotFoundException(projectName.get());
|
||||
}
|
||||
}
|
||||
|
||||
private static Boolean toBoolean(boolean value) {
|
||||
return value ? true : null;
|
||||
}
|
||||
}
|
@@ -42,6 +42,7 @@ public class Module extends RestApiModule {
|
||||
put(PROJECT_KIND).to(PutProject.class);
|
||||
get(PROJECT_KIND).to(GetProject.class);
|
||||
get(PROJECT_KIND, "description").to(GetDescription.class);
|
||||
get(PROJECT_KIND, "access").to(GetAccess.class);
|
||||
put(PROJECT_KIND, "description").to(PutDescription.class);
|
||||
delete(PROJECT_KIND, "description").to(PutDescription.class);
|
||||
|
||||
|
Reference in New Issue
Block a user