Add new REST endpoint that provides the audit log of a group
Bug: Issue 1479 Change-Id: I59c51964cab1f1ab32f50db8645a09d96d7a0196 Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
parent
d7f38a5ee3
commit
fc3f832935
@ -592,6 +592,89 @@ describes the new owner group.
|
||||
}
|
||||
----
|
||||
|
||||
[[get-audit-log]]
|
||||
=== Get Audit Log
|
||||
--
|
||||
'GET /groups/link:#group-id[\{group-id\}]/log.audit'
|
||||
--
|
||||
|
||||
Gets the audit log of a Gerrit internal group.
|
||||
|
||||
.Request
|
||||
----
|
||||
GET /groups/9999c971bb4ab872aab759d8c49833ee6b9ff320/log.audit HTTP/1.0
|
||||
----
|
||||
|
||||
As response a list of link:#group-audit-event-info[GroupAuditEventInfo]
|
||||
entities is returned that describes the audit events of the group. The
|
||||
returned audit events are sorted by date in reverse order so that the
|
||||
newest audit event comes first.
|
||||
|
||||
.Response
|
||||
----
|
||||
HTTP/1.1 200 OK
|
||||
Content-Disposition: attachment
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
)]}'
|
||||
[
|
||||
{
|
||||
"member": {
|
||||
"url": "#/admin/groups/uuid-fdda826a0815859ab48d22a05a43472f0f55f89a",
|
||||
"options": {},
|
||||
"group_id": 3,
|
||||
"owner": "Administrators",
|
||||
"owner_id": "e56678641565e7f59dd5c6878f5bcbc842bf150a",
|
||||
"id": "fdda826a0815859ab48d22a05a43472f0f55f89a",
|
||||
"name": "MyGroup"
|
||||
},
|
||||
"type": "REMOVE_GROUP",
|
||||
"user": {
|
||||
"_account_id": 1000000,
|
||||
"name": "Administrator",
|
||||
"email": "admin@example.com",
|
||||
"username": "admin"
|
||||
},
|
||||
"date": "2015-07-03 09:22:26.348000000"
|
||||
},
|
||||
{
|
||||
"member": {
|
||||
"url": "#/admin/groups/uuid-fdda826a0815859ab48d22a05a43472f0f55f89a",
|
||||
"options": {},
|
||||
"group_id": 3,
|
||||
"owner": "Administrators",
|
||||
"owner_id": "e56678641565e7f59dd5c6878f5bcbc842bf150a",
|
||||
"id": "fdda826a0815859ab48d22a05a43472f0f55f89a",
|
||||
"name": "MyGroup"
|
||||
},
|
||||
"type": "ADD_GROUP",
|
||||
"user": {
|
||||
"_account_id": 1000000,
|
||||
"name": "Administrator",
|
||||
"email": "admin@example.com",
|
||||
"username": "admin"
|
||||
},
|
||||
"date": "2015-07-03 08:43:36.592000000"
|
||||
},
|
||||
{
|
||||
"member": {
|
||||
"_account_id": 1000000,
|
||||
"name": "Administrator",
|
||||
"email": "admin@example.com",
|
||||
"username": "admin"
|
||||
},
|
||||
"type": "ADD_USER",
|
||||
"user": {
|
||||
"_account_id": 1000001,
|
||||
"name": "John Doe",
|
||||
"email": "john.doe@example.com",
|
||||
"username": "jdoe"
|
||||
},
|
||||
"date": "2015-07-01 13:36:36.602000000"
|
||||
}
|
||||
]
|
||||
----
|
||||
|
||||
[[group-member-endpoints]]
|
||||
== Group Member Endpoints
|
||||
|
||||
@ -1108,6 +1191,38 @@ Group name that uniquely identifies one group.
|
||||
[[json-entities]]
|
||||
== JSON Entities
|
||||
|
||||
[[group-audit-event-info]]
|
||||
=== GroupAuditEventInfo
|
||||
The `GroupAuditEventInfo` entity contains information about an audit
|
||||
event of a group.
|
||||
|
||||
[options="header",cols="1,6"]
|
||||
|======================
|
||||
|Field Name|Description
|
||||
|`member` |
|
||||
The group member that is added/removed. If `type` is `ADD_USER` or
|
||||
`REMOVE_USER` the member is returned as detailed
|
||||
link:rest-api-accounts.html#account-info[AccountInfo] entity, if `type`
|
||||
is `ADD_GROUP` or `REMOVE_GROUP` the member is returned as
|
||||
link:#group-info[GroupInfo] entity.
|
||||
|`type` |
|
||||
The event type, can be: `ADD_USER`, `REMOVE_USER`, `ADD_GROUP` or
|
||||
`REMOVE_GROUP`.
|
||||
|
||||
`ADD_USER`: A user was added as member to the group.
|
||||
|
||||
`REMOVE_USER`: A user member was removed from the group.
|
||||
|
||||
`ADD_GROUP`: A group was included as member in the group.
|
||||
|
||||
`REMOVE_GROUP`: An included group was removed from the group.
|
||||
|`user` |
|
||||
The user that did the add/remove as detailed
|
||||
link:rest-api-accounts.html#account-info[AccountInfo] entity.
|
||||
|`date` |
|
||||
The timestamp of the event.
|
||||
|======================
|
||||
|
||||
[[group-info]]
|
||||
=== GroupInfo
|
||||
The `GroupInfo` entity contains information about a group. This can be
|
||||
|
@ -0,0 +1,74 @@
|
||||
// Copyright (C) 2015 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.common;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
public abstract class GroupAuditEventInfo {
|
||||
public enum Type {
|
||||
ADD_USER, REMOVE_USER, ADD_GROUP, REMOVE_GROUP
|
||||
}
|
||||
|
||||
public Type type;
|
||||
public AccountInfo user;
|
||||
public Timestamp date;
|
||||
|
||||
public static UserMemberAuditEventInfo createAddUserEvent(AccountInfo user,
|
||||
Timestamp date, AccountInfo member) {
|
||||
return new UserMemberAuditEventInfo(Type.ADD_USER, user, date, member);
|
||||
}
|
||||
|
||||
public static UserMemberAuditEventInfo createRemoveUserEvent(
|
||||
AccountInfo user, Timestamp date, AccountInfo member) {
|
||||
return new UserMemberAuditEventInfo(Type.REMOVE_USER, user, date, member);
|
||||
}
|
||||
|
||||
public static GroupMemberAuditEventInfo createAddGroupEvent(AccountInfo user,
|
||||
Timestamp date, GroupInfo member) {
|
||||
return new GroupMemberAuditEventInfo(Type.ADD_GROUP, user, date, member);
|
||||
}
|
||||
|
||||
public static GroupMemberAuditEventInfo createRemoveGroupEvent(
|
||||
AccountInfo user, Timestamp date, GroupInfo member) {
|
||||
return new GroupMemberAuditEventInfo(Type.REMOVE_GROUP, user, date, member);
|
||||
}
|
||||
|
||||
protected GroupAuditEventInfo(Type type, AccountInfo user,
|
||||
Timestamp date) {
|
||||
this.type = type;
|
||||
this.user = user;
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public static class UserMemberAuditEventInfo extends GroupAuditEventInfo {
|
||||
public AccountInfo member;
|
||||
|
||||
public UserMemberAuditEventInfo(Type type, AccountInfo user,
|
||||
Timestamp date, AccountInfo member) {
|
||||
super(type, user, date);
|
||||
this.member = member;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GroupMemberAuditEventInfo extends GroupAuditEventInfo {
|
||||
public GroupInfo member;
|
||||
|
||||
public GroupMemberAuditEventInfo(Type type, AccountInfo user,
|
||||
Timestamp date, GroupInfo member) {
|
||||
super(type, user, date);
|
||||
this.member = member;
|
||||
}
|
||||
}
|
||||
}
|
@ -98,4 +98,16 @@ public final class AccountGroupByIdAud {
|
||||
removedBy = deleter;
|
||||
removedOn = when;
|
||||
}
|
||||
|
||||
public Account.Id getAddedBy() {
|
||||
return addedBy;
|
||||
}
|
||||
|
||||
public Account.Id getRemovedBy() {
|
||||
return removedBy;
|
||||
}
|
||||
|
||||
public Timestamp getRemovedOn() {
|
||||
return removedOn;
|
||||
}
|
||||
}
|
||||
|
@ -103,4 +103,16 @@ public final class AccountGroupMemberAudit {
|
||||
removedBy = addedBy;
|
||||
removedOn = key.addedOn;
|
||||
}
|
||||
|
||||
public Account.Id getAddedBy() {
|
||||
return addedBy;
|
||||
}
|
||||
|
||||
public Account.Id getRemovedBy() {
|
||||
return removedBy;
|
||||
}
|
||||
|
||||
public Timestamp getRemovedOn() {
|
||||
return removedOn;
|
||||
}
|
||||
}
|
||||
|
@ -32,4 +32,8 @@ public interface AccountGroupByIdAudAccess extends
|
||||
@Query("WHERE key.groupId = ? AND key.includeUUID = ?")
|
||||
ResultSet<AccountGroupByIdAud> byGroupInclude(AccountGroup.Id groupId,
|
||||
AccountGroup.UUID incGroupUUID) throws OrmException;
|
||||
|
||||
@Query("WHERE key.groupId = ?")
|
||||
ResultSet<AccountGroupByIdAud> byGroup(AccountGroup.Id groupId)
|
||||
throws OrmException;
|
||||
}
|
||||
|
@ -33,4 +33,8 @@ public interface AccountGroupMemberAuditAccess extends
|
||||
@Query("WHERE key.groupId = ? AND key.accountId = ?")
|
||||
ResultSet<AccountGroupMemberAudit> byGroupAccount(AccountGroup.Id groupId,
|
||||
Account.Id accountId) throws OrmException;
|
||||
|
||||
@Query("WHERE key.groupId = ?")
|
||||
ResultSet<AccountGroupMemberAudit> byGroup(AccountGroup.Id groupId)
|
||||
throws OrmException;
|
||||
}
|
||||
|
@ -0,0 +1,130 @@
|
||||
// Copyright (C) 2015 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.group;
|
||||
|
||||
import com.google.gerrit.common.data.GroupDescriptions;
|
||||
import com.google.gerrit.extensions.common.AccountInfo;
|
||||
import com.google.gerrit.extensions.common.GroupAuditEventInfo;
|
||||
import com.google.gerrit.extensions.common.GroupInfo;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.extensions.restapi.Url;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.account.AccountLoader;
|
||||
import com.google.gerrit.server.account.GroupCache;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@Singleton
|
||||
public class GetAuditLog implements RestReadView<GroupResource> {
|
||||
private final Provider<ReviewDb> db;
|
||||
private final AccountLoader.Factory accountLoaderFactory;
|
||||
private final GroupCache groupCache;
|
||||
private final GroupJson groupJson;
|
||||
|
||||
@Inject
|
||||
public GetAuditLog(Provider<ReviewDb> db,
|
||||
AccountLoader.Factory accountLoaderFactory,
|
||||
GroupCache groupCache,
|
||||
GroupJson groupJson) {
|
||||
this.db = db;
|
||||
this.accountLoaderFactory = accountLoaderFactory;
|
||||
this.groupCache = groupCache;
|
||||
this.groupJson = groupJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends GroupAuditEventInfo> apply(GroupResource rsrc)
|
||||
throws AuthException, ResourceNotFoundException,
|
||||
MethodNotAllowedException, OrmException {
|
||||
if (rsrc.toAccountGroup() == null) {
|
||||
throw new MethodNotAllowedException();
|
||||
} else if (!rsrc.getControl().isOwner()) {
|
||||
throw new AuthException("Not group owner");
|
||||
}
|
||||
|
||||
AccountGroup group = db.get().accountGroups().get(
|
||||
rsrc.toAccountGroup().getId());
|
||||
if (group == null) {
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
AccountLoader accountLoader = accountLoaderFactory.create(true);
|
||||
|
||||
List<GroupAuditEventInfo> auditEvents = new ArrayList<>();
|
||||
|
||||
for (AccountGroupMemberAudit auditEvent :
|
||||
db.get().accountGroupMembersAudit().byGroup(group.getId()).toList()) {
|
||||
AccountInfo member = accountLoader.get(auditEvent.getKey().getParentKey());
|
||||
|
||||
auditEvents.add(GroupAuditEventInfo.createAddUserEvent(
|
||||
accountLoader.get(auditEvent.getAddedBy()),
|
||||
auditEvent.getKey().getAddedOn(), member));
|
||||
|
||||
if (!auditEvent.isActive()) {
|
||||
auditEvents.add(GroupAuditEventInfo.createRemoveUserEvent(
|
||||
accountLoader.get(auditEvent.getRemovedBy()),
|
||||
auditEvent.getRemovedOn(), member));
|
||||
}
|
||||
}
|
||||
|
||||
for (AccountGroupByIdAud auditEvent :
|
||||
db.get().accountGroupByIdAud().byGroup(group.getId()).toList()) {
|
||||
AccountGroup.UUID includedGroupUUID = auditEvent.getKey().getIncludeUUID();
|
||||
AccountGroup includedGroup = groupCache.get(includedGroupUUID);
|
||||
GroupInfo member;
|
||||
if (includedGroup != null) {
|
||||
member = groupJson.format(GroupDescriptions.forAccountGroup(includedGroup));
|
||||
} else {
|
||||
member = new GroupInfo();
|
||||
member.id = Url.encode(includedGroupUUID.get());
|
||||
}
|
||||
|
||||
auditEvents.add(GroupAuditEventInfo.createAddGroupEvent(
|
||||
accountLoader.get(auditEvent.getAddedBy()),
|
||||
auditEvent.getKey().getAddedOn(), member));
|
||||
|
||||
if (!auditEvent.isActive()) {
|
||||
auditEvents.add(GroupAuditEventInfo.createRemoveGroupEvent(
|
||||
accountLoader.get(auditEvent.getRemovedBy()),
|
||||
auditEvent.getRemovedOn(), member));
|
||||
}
|
||||
}
|
||||
|
||||
accountLoader.fill();
|
||||
|
||||
// sort by date in reverse order so that the newest audit event comes first
|
||||
Collections.sort(auditEvents, new Comparator<GroupAuditEventInfo>() {
|
||||
@Override
|
||||
public int compare(GroupAuditEventInfo e1, GroupAuditEventInfo e2) {
|
||||
return e2.date.compareTo(e1.date);
|
||||
}
|
||||
});
|
||||
|
||||
return auditEvents;
|
||||
}
|
||||
}
|
@ -55,6 +55,7 @@ public class Module extends RestApiModule {
|
||||
put(GROUP_KIND, "owner").to(PutOwner.class);
|
||||
get(GROUP_KIND, "options").to(GetOptions.class);
|
||||
put(GROUP_KIND, "options").to(PutOptions.class);
|
||||
get(GROUP_KIND, "log.audit").to(GetAuditLog.class);
|
||||
|
||||
child(GROUP_KIND, "members").to(MembersCollection.class);
|
||||
get(MEMBER_KIND).to(GetMember.class);
|
||||
|
Loading…
x
Reference in New Issue
Block a user