Merge "Support retrieving project access rights via REST"
This commit is contained in:
376
Documentation/rest-api-access.txt
Normal file
376
Documentation/rest-api-access.txt
Normal file
@@ -0,0 +1,376 @@
|
|||||||
|
Gerrit Code Review - /access/ REST API
|
||||||
|
======================================
|
||||||
|
|
||||||
|
This page describes the access rights related REST endpoints.
|
||||||
|
Please also take note of the general information on the
|
||||||
|
link:rest-api.html[REST API].
|
||||||
|
|
||||||
|
[[access-endpoints]]
|
||||||
|
Access Rights Endpoints
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
[[list-access]]
|
||||||
|
List Access Rights
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
[verse]
|
||||||
|
'GET /access/?project=link:rest-api-projects.html#project-name[\{project-name\}]'
|
||||||
|
|
||||||
|
Lists the access rights for projects. The projects for which the access
|
||||||
|
rights should be returned must be specified as `project` options. The
|
||||||
|
`project` can be specified multiple times.
|
||||||
|
|
||||||
|
As result a map is returned that maps the project name to
|
||||||
|
link:#project-access-info[ProjectAccessInfo] entities.
|
||||||
|
|
||||||
|
The entries in the map are sorted by project name.
|
||||||
|
|
||||||
|
.Request
|
||||||
|
----
|
||||||
|
GET /access/?project=MyProject&project=All-Projects HTTP/1.0
|
||||||
|
----
|
||||||
|
|
||||||
|
.Response
|
||||||
|
----
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json;charset=UTF-8
|
||||||
|
|
||||||
|
)]}'
|
||||||
|
{
|
||||||
|
"All-Projects": {
|
||||||
|
"revision": "edd453d18e08640e67a8c9a150cec998ed0ac9aa",
|
||||||
|
"local": {
|
||||||
|
"GLOBAL_CAPABILITIES": {
|
||||||
|
"permissions": {
|
||||||
|
"priority": {
|
||||||
|
"rules": {
|
||||||
|
"15bfcd8a6de1a69c50b30cedcdcc951c15703152": {
|
||||||
|
"action": "BATCH"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"streamEvents": {
|
||||||
|
"rules": {
|
||||||
|
"15bfcd8a6de1a69c50b30cedcdcc951c15703152": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"administrateServer": {
|
||||||
|
"rules": {
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"refs/meta/config": {
|
||||||
|
"permissions": {
|
||||||
|
"submit": {
|
||||||
|
"rules": {
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
},
|
||||||
|
"global:Project-Owners": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"label-Code-Review": {
|
||||||
|
"label": "Code-Review",
|
||||||
|
"rules": {
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW",
|
||||||
|
"min": -2,
|
||||||
|
"max": 2
|
||||||
|
},
|
||||||
|
"global:Project-Owners": {
|
||||||
|
"action": "ALLOW",
|
||||||
|
"min": -2,
|
||||||
|
"max": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"read": {
|
||||||
|
"exclusive": true,
|
||||||
|
"rules": {
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
},
|
||||||
|
"global:Project-Owners": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"push": {
|
||||||
|
"rules": {
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
},
|
||||||
|
"global:Project-Owners": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"refs/for/refs/*": {
|
||||||
|
"permissions": {
|
||||||
|
"pushMerge": {
|
||||||
|
"rules": {
|
||||||
|
"global:Registered-Users": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"push": {
|
||||||
|
"rules": {
|
||||||
|
"global:Registered-Users": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"refs/tags/*": {
|
||||||
|
"permissions": {
|
||||||
|
"pushSignedTag": {
|
||||||
|
"rules": {
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
},
|
||||||
|
"global:Project-Owners": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pushTag": {
|
||||||
|
"rules": {
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
},
|
||||||
|
"global:Project-Owners": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"refs/heads/*": {
|
||||||
|
"permissions": {
|
||||||
|
"forgeCommitter": {
|
||||||
|
"rules": {
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
},
|
||||||
|
"global:Project-Owners": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forgeAuthor": {
|
||||||
|
"rules": {
|
||||||
|
"global:Registered-Users": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"submit": {
|
||||||
|
"rules": {
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
},
|
||||||
|
"global:Project-Owners": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editTopicName": {
|
||||||
|
"rules": {
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW",
|
||||||
|
"force": true
|
||||||
|
},
|
||||||
|
"global:Project-Owners": {
|
||||||
|
"action": "ALLOW",
|
||||||
|
"force": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"label-Code-Review": {
|
||||||
|
"label": "Code-Review",
|
||||||
|
"rules": {
|
||||||
|
"global:Registered-Users": {
|
||||||
|
"action": "ALLOW",
|
||||||
|
"min": -1,
|
||||||
|
"max": 1
|
||||||
|
},
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW",
|
||||||
|
"min": -2,
|
||||||
|
"max": 2
|
||||||
|
},
|
||||||
|
"global:Project-Owners": {
|
||||||
|
"action": "ALLOW",
|
||||||
|
"min": -2,
|
||||||
|
"max": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"create": {
|
||||||
|
"rules": {
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
},
|
||||||
|
"global:Project-Owners": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"push": {
|
||||||
|
"rules": {
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
},
|
||||||
|
"global:Project-Owners": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"refs/*": {
|
||||||
|
"permissions": {
|
||||||
|
"read": {
|
||||||
|
"rules": {
|
||||||
|
"global:Anonymous-Users": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
},
|
||||||
|
"53a4f647a89ea57992571187d8025f830625192a": {
|
||||||
|
"action": "ALLOW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"owner_of": [
|
||||||
|
"GLOBAL_CAPABILITIES",
|
||||||
|
"refs/meta/config",
|
||||||
|
"refs/for/refs/*",
|
||||||
|
"refs/tags/*",
|
||||||
|
"refs/heads/*",
|
||||||
|
"refs/*"
|
||||||
|
],
|
||||||
|
"can_upload": true,
|
||||||
|
"can_add": true,
|
||||||
|
"config_visible": true
|
||||||
|
},
|
||||||
|
"MyProject": {
|
||||||
|
"revision": "61157ed63e14d261b6dca40650472a9b0bd88474",
|
||||||
|
"inherits_from": {
|
||||||
|
"kind": "gerritcodereview#project",
|
||||||
|
"id": "All-Projects",
|
||||||
|
"name": "All-Projects",
|
||||||
|
"description": "Access inherited by all other projects."
|
||||||
|
},
|
||||||
|
"local": {},
|
||||||
|
"owner_of": [
|
||||||
|
"refs/*"
|
||||||
|
],
|
||||||
|
"can_upload": true,
|
||||||
|
"can_add": true,
|
||||||
|
"config_visible": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
[[access-section-info]]
|
||||||
|
AccessSectionInfo
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
The `AccessSectionInfo` describes the access rights that are assigned
|
||||||
|
on a ref.
|
||||||
|
|
||||||
|
[options="header",width="50%",cols="1,^1,5"]
|
||||||
|
|==================================
|
||||||
|
|Field Name ||Description
|
||||||
|
|`permissions` ||
|
||||||
|
The permissions assigned on the ref of this access section as a map
|
||||||
|
that maps the permission names to link:#permission-info[PermissionInfo]
|
||||||
|
entities.
|
||||||
|
|==================================
|
||||||
|
|
||||||
|
[[permission-info]]
|
||||||
|
PermissionInfo
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
The `PermissionInfo` entity contains information about an assigned
|
||||||
|
permission.
|
||||||
|
|
||||||
|
[options="header",width="50%",cols="1,^1,5"]
|
||||||
|
|==================================
|
||||||
|
|Field Name ||Description
|
||||||
|
|`label` |optional|
|
||||||
|
The name of the label. Not set if it's not a label permission.
|
||||||
|
|`exclusive` |not set if `false`|
|
||||||
|
Whether this permission is assigned exclusively.
|
||||||
|
|`rules` ||
|
||||||
|
The rules assigned for this permission as a map that maps the UUIDs of
|
||||||
|
the groups for which the permission are assigned to
|
||||||
|
link:#permission-info[PermissionRuleInfo] entities.
|
||||||
|
|==================================
|
||||||
|
|
||||||
|
[[permission-rule-info]]
|
||||||
|
PermissionRuleInfo
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
The `PermissionRuleInfo` entity contains information about a permission
|
||||||
|
rule that is assigned to group.
|
||||||
|
|
||||||
|
[options="header",width="50%",cols="1,^1,5"]
|
||||||
|
|==================================
|
||||||
|
|Field Name ||Description
|
||||||
|
|`action` ||
|
||||||
|
The action of this rule. For normal permissions this can be `ALLOW`,
|
||||||
|
`DENY` or `BLOCK`. Special values for global capabilities are
|
||||||
|
`INTERACTIVE` and `BATCH`.
|
||||||
|
|`force` |not set if `false`|
|
||||||
|
Whether the force flag is set.
|
||||||
|
|`min` |
|
||||||
|
not set if range if empty (from `0` to `0`) or not set|
|
||||||
|
The min value of the permission range.
|
||||||
|
|`max` |
|
||||||
|
not set if range if empty (from `0` to `0`) or not set|
|
||||||
|
The max value of the permission range.
|
||||||
|
|==================================
|
||||||
|
|
||||||
|
[[project-access-info]]
|
||||||
|
ProjectAccessInfo
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
The `ProjectAccessInfo` entity contains information about the access
|
||||||
|
rights for a project.
|
||||||
|
|
||||||
|
[options="header",width="50%",cols="1,^1,5"]
|
||||||
|
|==================================
|
||||||
|
|Field Name ||Description
|
||||||
|
|`revision` ||
|
||||||
|
The revision of the `refs/meta/config` branch from which the access
|
||||||
|
rights were loaded.
|
||||||
|
|`inherits_from` |not set for the `All-Project` project|
|
||||||
|
The parent project from which permissions are inherited as a
|
||||||
|
link:rest-api-projects.html#project-info[ProjectInfo] entity.
|
||||||
|
|`local` ||
|
||||||
|
The local access rights of the project as a map that maps the refs to
|
||||||
|
link:#access-section-info[AccessSectionInfo] entities.
|
||||||
|
|`owner_of` ||The list of refs owned by the calling user.
|
||||||
|
|`can_upload` |not set if `false`|
|
||||||
|
Whether the calling user can upload to any ref.
|
||||||
|
|`can_add` |not set if `false`|
|
||||||
|
Whether the calling user can add any ref.
|
||||||
|
|`config_visible` |not set if `false`|
|
||||||
|
Whether the calling user can see the `refs/meta/config` branch of the
|
||||||
|
project.
|
||||||
|
|==================================
|
||||||
|
|
||||||
|
|
||||||
|
GERRIT
|
||||||
|
------
|
||||||
|
Part of link:index.html[Gerrit Code Review]
|
||||||
@@ -9,6 +9,8 @@ See also: link:dev-rest-api.html[REST API Developers' Notes].
|
|||||||
|
|
||||||
Endpoints
|
Endpoints
|
||||||
---------
|
---------
|
||||||
|
link:rest-api-access.html[/access/]::
|
||||||
|
Access Right related REST endpoints
|
||||||
link:rest-api-accounts.html[/accounts/]::
|
link:rest-api-accounts.html[/accounts/]::
|
||||||
Account related REST endpoints
|
Account related REST endpoints
|
||||||
link:rest-api-changes.html[/changes/]::
|
link:rest-api-changes.html[/changes/]::
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.google.gerrit.httpd.raw.LegacyGerritServlet;
|
|||||||
import com.google.gerrit.httpd.raw.SshInfoServlet;
|
import com.google.gerrit.httpd.raw.SshInfoServlet;
|
||||||
import com.google.gerrit.httpd.raw.StaticServlet;
|
import com.google.gerrit.httpd.raw.StaticServlet;
|
||||||
import com.google.gerrit.httpd.raw.ToolServlet;
|
import com.google.gerrit.httpd.raw.ToolServlet;
|
||||||
|
import com.google.gerrit.httpd.rpc.access.AccessRestApiServlet;
|
||||||
import com.google.gerrit.httpd.rpc.account.AccountsRestApiServlet;
|
import com.google.gerrit.httpd.rpc.account.AccountsRestApiServlet;
|
||||||
import com.google.gerrit.httpd.rpc.change.ChangesRestApiServlet;
|
import com.google.gerrit.httpd.rpc.change.ChangesRestApiServlet;
|
||||||
import com.google.gerrit.httpd.rpc.change.DeprecatedChangeQueryServlet;
|
import com.google.gerrit.httpd.rpc.change.DeprecatedChangeQueryServlet;
|
||||||
@@ -101,6 +102,7 @@ class UrlModule extends ServletModule {
|
|||||||
|
|
||||||
filter("/a/*").through(RequireIdentifiedUserFilter.class);
|
filter("/a/*").through(RequireIdentifiedUserFilter.class);
|
||||||
serveRegex("^/(?:a/)?tools/(.*)$").with(ToolServlet.class);
|
serveRegex("^/(?:a/)?tools/(.*)$").with(ToolServlet.class);
|
||||||
|
serveRegex("^/(?:a/)?access/(.*)$").with(AccessRestApiServlet.class);
|
||||||
serveRegex("^/(?:a/)?accounts/(.*)$").with(AccountsRestApiServlet.class);
|
serveRegex("^/(?:a/)?accounts/(.*)$").with(AccountsRestApiServlet.class);
|
||||||
serveRegex("^/(?:a/)?changes/(.*)$").with(ChangesRestApiServlet.class);
|
serveRegex("^/(?:a/)?changes/(.*)$").with(ChangesRestApiServlet.class);
|
||||||
serveRegex("^/(?:a/)?config/(.*)$").with(ConfigRestApiServlet.class);
|
serveRegex("^/(?:a/)?config/(.*)$").with(ConfigRestApiServlet.class);
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (C) 2013 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.httpd.rpc.access;
|
||||||
|
|
||||||
|
import com.google.gerrit.httpd.restapi.RestApiServlet;
|
||||||
|
import com.google.gerrit.server.access.AccessCollection;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class AccessRestApiServlet extends RestApiServlet {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AccessRestApiServlet(RestApiServlet.Globals globals,
|
||||||
|
Provider<AccessCollection> access) {
|
||||||
|
super(globals, access);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (C) 2013 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.access;
|
||||||
|
|
||||||
|
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||||
|
import com.google.gerrit.extensions.restapi.IdString;
|
||||||
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestCollection;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestView;
|
||||||
|
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
|
||||||
|
public class AccessCollection implements
|
||||||
|
RestCollection<TopLevelResource, AccessResource> {
|
||||||
|
private final Provider<ListAccess> list;
|
||||||
|
private final DynamicMap<RestView<AccessResource>> views;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AccessCollection(Provider<ListAccess> list,
|
||||||
|
DynamicMap<RestView<AccessResource>> views) {
|
||||||
|
this.list = list;
|
||||||
|
this.views = views;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestView<TopLevelResource> list() {
|
||||||
|
return list.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessResource parse(TopLevelResource parent, IdString id)
|
||||||
|
throws ResourceNotFoundException {
|
||||||
|
throw new ResourceNotFoundException(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicMap<RestView<AccessResource>> views() {
|
||||||
|
return views;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (C) 2013 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.access;
|
||||||
|
|
||||||
|
import com.google.gerrit.extensions.restapi.RestResource;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestView;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
|
public class AccessResource implements RestResource {
|
||||||
|
public static final TypeLiteral<RestView<AccessResource>> ACCESS_KIND =
|
||||||
|
new TypeLiteral<RestView<AccessResource>>() {};
|
||||||
|
}
|
||||||
@@ -0,0 +1,306 @@
|
|||||||
|
// Copyright (C) 2013 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.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.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.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.GitRepositoryManager;
|
||||||
|
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||||
|
import com.google.gerrit.server.git.ProjectConfig;
|
||||||
|
import com.google.gerrit.server.group.GroupJson;
|
||||||
|
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.ProjectJson.ProjectInfo;
|
||||||
|
import com.google.gerrit.server.project.ProjectState;
|
||||||
|
import com.google.gerrit.server.project.RefControl;
|
||||||
|
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> {
|
||||||
|
|
||||||
|
@Option(name = "--project", aliases = {"-p"}, metaVar = "PROJECT",
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ListAccess(Provider<CurrentUser> self,
|
||||||
|
ProjectControl.GenericFactory projectControlFactory,
|
||||||
|
ProjectCache projectCache, ProjectJson projectJson,
|
||||||
|
MetaDataUpdate.Server metaDataUpdateFactory,
|
||||||
|
GroupControl.Factory groupControlFactory, GroupBackend groupBackend,
|
||||||
|
GroupJson groupJson, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ProjectAccessInfo> apply(TopLevelResource resource)
|
||||||
|
throws ResourceNotFoundException, ResourceConflictException, IOException {
|
||||||
|
Map<String, ProjectAccessInfo> access = Maps.newTreeMap();
|
||||||
|
for (String p: projects) {
|
||||||
|
Project.NameKey projectName = new Project.NameKey(p);
|
||||||
|
ProjectControl pc = open(projectName);
|
||||||
|
ProjectConfig config;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
MetaDataUpdate md = metaDataUpdateFactory.create(projectName);
|
||||||
|
try {
|
||||||
|
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());
|
||||||
|
} finally {
|
||||||
|
md.close();
|
||||||
|
}
|
||||||
|
} catch (RepositoryNotFoundException e) {
|
||||||
|
throw new ResourceNotFoundException(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
access.put(p, new ProjectAccessInfo(pc, config));
|
||||||
|
}
|
||||||
|
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 Set<String> ownerOf;
|
||||||
|
public Boolean canUpload;
|
||||||
|
public Boolean canAdd;
|
||||||
|
public Boolean configVisible;
|
||||||
|
|
||||||
|
public ProjectAccessInfo(ProjectControl pc, ProjectConfig config) {
|
||||||
|
final RefControl metaConfigControl =
|
||||||
|
pc.controlForRef(GitRepositoryManager.REF_CONFIG);
|
||||||
|
local = Maps.newHashMap();
|
||||||
|
ownerOf = Sets.newHashSet();
|
||||||
|
Map<AccountGroup.UUID, Boolean> visibleGroups =
|
||||||
|
new HashMap<AccountGroup.UUID, Boolean>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canUpload = toBoolean(pc.isOwner()
|
||||||
|
|| (metaConfigControl.isVisible() && metaConfigControl.canUpload()));
|
||||||
|
canAdd = toBoolean(pc.canAddRefs());
|
||||||
|
configVisible = pc.isOwner() || metaConfigControl.isVisible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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 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 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,29 @@
|
|||||||
|
// Copyright (C) 2013 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.access;
|
||||||
|
|
||||||
|
import static com.google.gerrit.server.access.AccessResource.ACCESS_KIND;
|
||||||
|
|
||||||
|
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||||
|
import com.google.gerrit.extensions.restapi.RestApiModule;
|
||||||
|
|
||||||
|
public class Module extends RestApiModule {
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(AccessCollection.class);
|
||||||
|
|
||||||
|
DynamicMap.mapOf(binder(), ACCESS_KIND);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -238,6 +238,7 @@ public class GerritGlobalModule extends FactoryModule {
|
|||||||
bind(AccountControl.Factory.class);
|
bind(AccountControl.Factory.class);
|
||||||
|
|
||||||
install(new AuditModule());
|
install(new AuditModule());
|
||||||
|
install(new com.google.gerrit.server.access.Module());
|
||||||
install(new com.google.gerrit.server.account.Module());
|
install(new com.google.gerrit.server.account.Module());
|
||||||
install(new com.google.gerrit.server.change.Module());
|
install(new com.google.gerrit.server.change.Module());
|
||||||
install(new com.google.gerrit.server.config.Module());
|
install(new com.google.gerrit.server.config.Module());
|
||||||
|
|||||||
@@ -70,6 +70,18 @@ public class ProjectControl {
|
|||||||
}
|
}
|
||||||
return p.controlFor(user);
|
return p.controlFor(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ProjectControl validateFor(Project.NameKey nameKey, int need,
|
||||||
|
CurrentUser user) throws NoSuchProjectException, IOException {
|
||||||
|
final ProjectControl c = controlFor(nameKey, user);
|
||||||
|
if ((need & VISIBLE) == VISIBLE && c.isVisible()) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if ((need & OWNER) == OWNER && c.isOwner()) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
throw new NoSuchProjectException(nameKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Factory {
|
public static class Factory {
|
||||||
|
|||||||
Reference in New Issue
Block a user