From 454b2c9178346aece48e311353ca8e3d9810ed9d Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Mon, 27 Jun 2022 23:18:26 +0100 Subject: [PATCH 1/6] WIP --- .../rest/controller/ClusteringController.java | 17 ++-- .../rest/controller/GroupController.java | 6 ++ .../plugin/rest/entity/GroupEntity.java | 16 ++++ .../plugin/rest/utils/LoggingUtils.java | 88 +++++++++++++++++++ 4 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/controller/ClusteringController.java b/src/java/org/jivesoftware/openfire/plugin/rest/controller/ClusteringController.java index 75a9ef062..0566b65c6 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/controller/ClusteringController.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/controller/ClusteringController.java @@ -18,12 +18,10 @@ import org.jivesoftware.openfire.cluster.ClusterManager; import org.jivesoftware.openfire.cluster.ClusterNodeInfo; import org.jivesoftware.openfire.cluster.NodeID; -import org.jivesoftware.openfire.plugin.rest.RESTServicePlugin; import org.jivesoftware.openfire.plugin.rest.entity.ClusterNodeEntities; import org.jivesoftware.openfire.plugin.rest.entity.ClusterNodeEntity; -import org.jivesoftware.util.JiveGlobals; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.jivesoftware.openfire.plugin.rest.utils.LoggingUtils; +import org.jivesoftware.openfire.plugin.rest.utils.LoggingUtils.AuditEvent; import java.nio.charset.StandardCharsets; import java.util.Collection; @@ -31,8 +29,6 @@ import java.util.stream.Collectors; public class ClusteringController { - private static final Logger LOG = LoggerFactory.getLogger(ClusteringController.class); - private static ClusteringController INSTANCE = null; /** @@ -56,13 +52,8 @@ public static void setInstance(final ClusteringController instance) { ClusteringController.INSTANCE = instance; } - public static void log(String logMessage) { - if (JiveGlobals.getBooleanProperty(RESTServicePlugin.SERVICE_LOGGING_ENABLED, false)) { - LOG.info(logMessage); - } - } - public String getClusterStatus() { + LoggingUtils.auditEvent(AuditEvent.CLUSTERING_GET_STATUS.toString()); if (ClusterManager.isClusteringEnabled()) { if (ClusterManager.isClusteringStarted()) { if (ClusterManager.isSeniorClusterMember()) { @@ -83,11 +74,13 @@ public String getClusterStatus() { } public Optional getNodeEntity(String nodeId) { + LoggingUtils.auditEvent(AuditEvent.CLUSTERING_GET_NODE.toString(), nodeId); final Optional nodeInfo = ClusterManager.getNodeInfo(NodeID.getInstance(nodeId.getBytes(StandardCharsets.UTF_8))); return nodeInfo.map(ClusterNodeEntity::from); } public ClusterNodeEntities getNodeEntities() { + LoggingUtils.auditEvent(AuditEvent.CLUSTERING_GET_NODES.toString()); final Collection nodesInfo = ClusterManager.getNodesInfo(); return new ClusterNodeEntities(nodesInfo.stream().map(ClusterNodeEntity::from).collect(Collectors.toList())); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/controller/GroupController.java b/src/java/org/jivesoftware/openfire/plugin/rest/controller/GroupController.java index 4a36d36b6..4c50b5703 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/controller/GroupController.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/controller/GroupController.java @@ -28,6 +28,7 @@ import org.jivesoftware.openfire.plugin.rest.entity.GroupEntity; import org.jivesoftware.openfire.plugin.rest.exceptions.ExceptionType; import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; +import org.jivesoftware.openfire.plugin.rest.utils.LoggingUtils; import org.jivesoftware.openfire.plugin.rest.utils.MUCRoomUtils; import org.xmpp.packet.JID; @@ -55,6 +56,7 @@ public static GroupController getInstance() { * the service exception */ public List getGroups() throws ServiceException { + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_LIST.toString()); Collection groups = GroupManager.getInstance().getGroups(); List groupEntities = new ArrayList<>(); for (Group group : groups) { @@ -75,6 +77,7 @@ public List getGroups() throws ServiceException { * the service exception */ public GroupEntity getGroup(String groupName) throws ServiceException { + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_GET_BY_NAME.toString(), groupName); Group group; try { group = GroupManager.getInstance().getGroup(groupName); @@ -101,6 +104,7 @@ public GroupEntity getGroup(String groupName) throws ServiceException { * the service exception */ public Group createGroup(GroupEntity groupEntity) throws ServiceException { + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_CREATE.toString(), groupEntity); Group group; if (groupEntity != null && !groupEntity.getName().isEmpty()) { try { @@ -163,6 +167,7 @@ public Group createGroup(GroupEntity groupEntity) throws ServiceException { * @throws ServiceException the service exception */ public Group updateGroup(String groupName, GroupEntity groupEntity) throws ServiceException { + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_UPDATE_BY_NAME.toString(), groupName, groupEntity); Group group; if (groupEntity != null && !groupEntity.getName().isEmpty()) { if (groupName.equals(groupEntity.getName())) { @@ -251,6 +256,7 @@ public Group updateGroup(String groupName, GroupEntity groupEntity) throws Servi * the service exception */ public void deleteGroup(String groupName) throws ServiceException { + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_DELETE.toString(), groupName); try { Group group = GroupManager.getInstance().getGroup(groupName); GroupManager.getInstance().deleteGroup(group); diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java index 277e3448f..90b50d669 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java @@ -17,8 +17,10 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; +import org.jivesoftware.util.StringUtils; import java.util.List; import java.util.Optional; @@ -172,4 +174,18 @@ public void setMembers(List members) { */ public void setShared(Boolean shared) { this.shared = shared; } + + @Override + public String toString() { + + return MoreObjects.toStringHelper(this) + .add("name", this.name) + .add("description", this.description) + .add( "admins", this.admins) + .add("members", this.members) + .add("shared", this.shared) + .toString(); + + } + } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java b/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java new file mode 100644 index 000000000..f2963741f --- /dev/null +++ b/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2022 Ignite Realtime Foundation. All rights reserved. + * + * 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 org.jivesoftware.openfire.plugin.rest.utils; + +import org.jivesoftware.openfire.plugin.rest.RESTServicePlugin; +import org.jivesoftware.util.JiveGlobals; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; + +public class LoggingUtils { + private static final Logger AUDIT_LOG = LoggerFactory.getLogger("RestAPI-Plugin-Audit"); + + public enum AuditEvent { + CLUSTERING_GET_STATUS("GetClusterStatus", "Get current status of clustering"), + CLUSTERING_GET_NODES("GetClusterNodes", "Get info on all cluster nodes"), + CLUSTERING_GET_NODE("GetClusterNode", "Get info on a specific cluster node by ID"), + + GROUPS_LIST("GetGroups", "List all groups"), + GROUPS_GET_BY_NAME("GetGroupByName", "Fetch a group given the name"), + GROUPS_CREATE("CreateGroup", "Create a group given the definition"), + GROUPS_UPDATE_BY_NAME("UpdateGroup", "Update the named group with the given definition"), + GROUPS_DELETE("DeleteGroup", "Delete group with the given name"), + ; + + private final String name; + private final String description; + AuditEvent(final String name, final String description){ + this.name = name; + this.description = description; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + @Override + public String toString() { + return getName(); + } + } + + public static void auditEvent(String event){ + auditEvent(event, ""); + } + + public static void auditEvent(String event, Object... parameters){ + if (JiveGlobals.getBooleanProperty(RESTServicePlugin.SERVICE_LOGGING_ENABLED, false)) { + String[] parsedParameters = parseParameters(parameters); + String logMessage = "Event: " + event; + logMessage += " - "; + logMessage += "Parameters: + " + Arrays.toString(parsedParameters); + AUDIT_LOG.info(logMessage); + }; + } + + private static String[] parseParameters(Object[] parameters) { + ArrayList parsed = new ArrayList<>(); + for (Object obj: parameters) { + if(obj == null){ + parsed.add("null"); + } else { + parsed.add(obj.toString()); + } + } + return parsed.toArray(new String[0]); + } +} From 56e4a4c6fecdbda2d0eead3b4e19970b314e9013 Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Tue, 28 Jun 2022 13:11:23 +0100 Subject: [PATCH 2/6] WIP --- .../openfire/plugin/rest/AuthFilter.java | 1 + .../rest/controller/ClusteringController.java | 6 +- .../rest/controller/GroupController.java | 10 ++-- .../controller/JustMarriedController.java | 8 +++ .../plugin/rest/utils/LoggingUtils.java | 59 ++++++++----------- 5 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java b/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java index ce447d5c4..b6bd5597e 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java @@ -106,6 +106,7 @@ public void filter(ContainerRequestContext containerRequest) throws IOException String auth = containerRequest.getHeaderString("authorization"); if (auth == null) { + LOG.warn("REST API request with no Authorization header rejected"); throw new WebApplicationException(Status.UNAUTHORIZED); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/controller/ClusteringController.java b/src/java/org/jivesoftware/openfire/plugin/rest/controller/ClusteringController.java index 0566b65c6..442691976 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/controller/ClusteringController.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/controller/ClusteringController.java @@ -53,7 +53,7 @@ public static void setInstance(final ClusteringController instance) { } public String getClusterStatus() { - LoggingUtils.auditEvent(AuditEvent.CLUSTERING_GET_STATUS.toString()); + LoggingUtils.auditEvent(AuditEvent.CLUSTERING_GET_STATUS); if (ClusterManager.isClusteringEnabled()) { if (ClusterManager.isClusteringStarted()) { if (ClusterManager.isSeniorClusterMember()) { @@ -74,13 +74,13 @@ public String getClusterStatus() { } public Optional getNodeEntity(String nodeId) { - LoggingUtils.auditEvent(AuditEvent.CLUSTERING_GET_NODE.toString(), nodeId); + LoggingUtils.auditEvent(AuditEvent.CLUSTERING_GET_NODE, nodeId); final Optional nodeInfo = ClusterManager.getNodeInfo(NodeID.getInstance(nodeId.getBytes(StandardCharsets.UTF_8))); return nodeInfo.map(ClusterNodeEntity::from); } public ClusterNodeEntities getNodeEntities() { - LoggingUtils.auditEvent(AuditEvent.CLUSTERING_GET_NODES.toString()); + LoggingUtils.auditEvent(AuditEvent.CLUSTERING_GET_NODES); final Collection nodesInfo = ClusterManager.getNodesInfo(); return new ClusterNodeEntities(nodesInfo.stream().map(ClusterNodeEntity::from).collect(Collectors.toList())); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/controller/GroupController.java b/src/java/org/jivesoftware/openfire/plugin/rest/controller/GroupController.java index 4c50b5703..0eda7404f 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/controller/GroupController.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/controller/GroupController.java @@ -56,7 +56,7 @@ public static GroupController getInstance() { * the service exception */ public List getGroups() throws ServiceException { - LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_LIST.toString()); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_LIST); Collection groups = GroupManager.getInstance().getGroups(); List groupEntities = new ArrayList<>(); for (Group group : groups) { @@ -77,7 +77,7 @@ public List getGroups() throws ServiceException { * the service exception */ public GroupEntity getGroup(String groupName) throws ServiceException { - LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_GET_BY_NAME.toString(), groupName); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_GET_BY_NAME, groupName); Group group; try { group = GroupManager.getInstance().getGroup(groupName); @@ -104,7 +104,7 @@ public GroupEntity getGroup(String groupName) throws ServiceException { * the service exception */ public Group createGroup(GroupEntity groupEntity) throws ServiceException { - LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_CREATE.toString(), groupEntity); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_CREATE, groupEntity); Group group; if (groupEntity != null && !groupEntity.getName().isEmpty()) { try { @@ -167,7 +167,7 @@ public Group createGroup(GroupEntity groupEntity) throws ServiceException { * @throws ServiceException the service exception */ public Group updateGroup(String groupName, GroupEntity groupEntity) throws ServiceException { - LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_UPDATE_BY_NAME.toString(), groupName, groupEntity); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_UPDATE_BY_NAME, groupName, groupEntity); Group group; if (groupEntity != null && !groupEntity.getName().isEmpty()) { if (groupName.equals(groupEntity.getName())) { @@ -256,7 +256,7 @@ public Group updateGroup(String groupName, GroupEntity groupEntity) throws Servi * the service exception */ public void deleteGroup(String groupName) throws ServiceException { - LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_DELETE.toString(), groupName); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.GROUPS_DELETE, groupName); try { Group group = GroupManager.getInstance().getGroup(groupName); GroupManager.getInstance().deleteGroup(group); diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/controller/JustMarriedController.java b/src/java/org/jivesoftware/openfire/plugin/rest/controller/JustMarriedController.java index 2953de4ff..8df764024 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/controller/JustMarriedController.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/controller/JustMarriedController.java @@ -30,6 +30,7 @@ import org.jivesoftware.openfire.lockout.LockOutManager; import org.jivesoftware.openfire.plugin.rest.exceptions.ExceptionType; import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; +import org.jivesoftware.openfire.plugin.rest.utils.LoggingUtils; import org.jivesoftware.openfire.roster.Roster; import org.jivesoftware.openfire.roster.RosterItem; import org.jivesoftware.openfire.session.ClientSession; @@ -66,6 +67,13 @@ public static boolean changeName(String currentUserName, String newUserName, boo String newEmail, String newRealName) throws ServiceException { UserManager userManager = UserManager.getInstance(); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.USER_CHANGE_NAME, + "currentUserName", currentUserName, + "newUserName", newUserName, + "deleteOldUser", deleteOldUser, + "newEmail", newEmail, + "newRealName", newRealName); + try { User currentUser = userManager.getUser(currentUserName); // Old user found, create new one diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java b/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java index f2963741f..518ec59e9 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java @@ -28,61 +28,50 @@ public class LoggingUtils { private static final Logger AUDIT_LOG = LoggerFactory.getLogger("RestAPI-Plugin-Audit"); public enum AuditEvent { - CLUSTERING_GET_STATUS("GetClusterStatus", "Get current status of clustering"), - CLUSTERING_GET_NODES("GetClusterNodes", "Get info on all cluster nodes"), - CLUSTERING_GET_NODE("GetClusterNode", "Get info on a specific cluster node by ID"), + //Clustering + CLUSTERING_GET_STATUS, + CLUSTERING_GET_NODES, + CLUSTERING_GET_NODE, - GROUPS_LIST("GetGroups", "List all groups"), - GROUPS_GET_BY_NAME("GetGroupByName", "Fetch a group given the name"), - GROUPS_CREATE("CreateGroup", "Create a group given the definition"), - GROUPS_UPDATE_BY_NAME("UpdateGroup", "Update the named group with the given definition"), - GROUPS_DELETE("DeleteGroup", "Delete group with the given name"), - ; - - private final String name; - private final String description; - AuditEvent(final String name, final String description){ - this.name = name; - this.description = description; - } - - public String getName() { - return name; - } + //Groups + GROUPS_LIST, + GROUPS_GET_BY_NAME, + GROUPS_CREATE, + GROUPS_UPDATE_BY_NAME, + GROUPS_DELETE, - public String getDescription() { - return description; - } - - @Override - public String toString() { - return getName(); - } + //JustMarried + USER_CHANGE_NAME + ; } - public static void auditEvent(String event){ + public static void auditEvent(AuditEvent event){ auditEvent(event, ""); } - public static void auditEvent(String event, Object... parameters){ + public static void auditEvent(AuditEvent event, Object... parameters){ if (JiveGlobals.getBooleanProperty(RESTServicePlugin.SERVICE_LOGGING_ENABLED, false)) { - String[] parsedParameters = parseParameters(parameters); + String parameterString = parseParameters(parameters); String logMessage = "Event: " + event; logMessage += " - "; - logMessage += "Parameters: + " + Arrays.toString(parsedParameters); + logMessage += "Parameters: + " + parameterString; AUDIT_LOG.info(logMessage); }; } - private static String[] parseParameters(Object[] parameters) { + private static String parseParameters(Object[] parameters) { ArrayList parsed = new ArrayList<>(); for (Object obj: parameters) { if(obj == null){ parsed.add("null"); } else { - parsed.add(obj.toString()); + try { + parsed.add(obj.toString()); + } catch (Exception e) { + parsed.add("unparseable"); + } } } - return parsed.toArray(new String[0]); + return parsed.toString(); } } From 62833f2a76c3b2614cb708a9bff4ab59bbd51575 Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Tue, 28 Jun 2022 22:12:32 +0100 Subject: [PATCH 3/6] WIP --- .../rest/controller/MUCRoomController.java | 44 ++++++++++++++--- .../rest/controller/MessageController.java | 2 + .../rest/controller/MsgArchiveController.java | 2 + .../plugin/rest/entity/GroupEntity.java | 2 +- .../rest/entity/MUCInvitationEntity.java | 4 ++ .../plugin/rest/entity/MUCRoomEntity.java | 29 +++++++++++ .../plugin/rest/entity/MessageEntity.java | 5 ++ .../plugin/rest/utils/LoggingUtils.java | 49 ++++++++++++++++++- 8 files changed, 128 insertions(+), 9 deletions(-) diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java index 57b07d850..49dd5c318 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java @@ -26,6 +26,7 @@ import org.jivesoftware.openfire.plugin.rest.entity.*; import org.jivesoftware.openfire.plugin.rest.exceptions.ExceptionType; import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; +import org.jivesoftware.openfire.plugin.rest.utils.LoggingUtils; import org.jivesoftware.openfire.plugin.rest.utils.MUCRoomUtils; import org.jivesoftware.openfire.plugin.rest.utils.UserUtils; import org.jivesoftware.util.AlreadyExistsException; @@ -154,6 +155,11 @@ protected static MUCRoom getRoom(@Nonnull final String serviceName, @Nonnull fin public MUCRoomEntities getChatRooms(String serviceName, String channelType, String roomSearch, boolean expand) throws ServiceException { log("Get the chat rooms"); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_LIST_ROOMS, + "serviceName", serviceName, + "channelType", channelType, + "roomSearch", roomSearch, + "expand", expand); final MultiUserChatService service = MUCServiceController.getService(serviceName); Collection roomsInfo = service.getAllRoomSearchInfo(); @@ -196,7 +202,10 @@ public MUCRoomEntities getChatRooms(String serviceName, String channelType, Stri * the service exception */ public MUCRoomEntity getChatRoom(String roomName, String serviceName, boolean expand) throws ServiceException { - log("Get the chat room: " + roomName); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_GET_ROOM, + "roomName", roomName, + "serviceName", serviceName, + "expand", expand); final MUCRoom chatRoom = getRoom(serviceName, roomName); return convertToMUCRoomEntity(chatRoom, expand); } @@ -212,7 +221,9 @@ public MUCRoomEntity getChatRoom(String roomName, String serviceName, boolean ex * the service exception */ public void deleteChatRoom(String roomName, String serviceName) throws ServiceException { - log("Delete the chat room: " + roomName); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_DELETE_ROOM, + "roomName", roomName, + "serviceName", serviceName); final MUCRoom chatRoom = getRoom(serviceName, roomName); chatRoom.destroyRoom(null, null); } @@ -227,6 +238,9 @@ public void deleteChatRoom(String roomName, String serviceName) throws ServiceEx */ public void createChatRoom(String serviceName, MUCRoomEntity mucRoomEntity, boolean sendInvitations) throws ServiceException { log("Create a chat room: " + mucRoomEntity.getRoomName()); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_CREATE_ROOM, + "serviceName", serviceName, + "mucRoomEntity", mucRoomEntity); try { createRoom(mucRoomEntity, serviceName, sendInvitations); } catch (NotAllowedException | ForbiddenException e) { @@ -296,7 +310,10 @@ public RoomCreationResultEntities createMultipleChatRooms(String serviceName, MU */ public void updateChatRoom(String roomName, String serviceName, MUCRoomEntity mucRoomEntity, boolean sendInvitations) throws ServiceException { - log("Update a chat room: " + mucRoomEntity.getRoomName()); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_UPDATE_ROOM, + "roomName", roomName, + "serviceName", serviceName, + "mucRoomEntity", mucRoomEntity); try { // If the room name is different throw exception if (!JID.nodeprep(roomName).equals(mucRoomEntity.getRoomName())) { @@ -493,7 +510,9 @@ private boolean equalToAffiliations(MUCRoom room, MUCRoomEntity mucRoomEntity) { */ public ParticipantEntities getRoomParticipants(String roomName, String serviceName) throws ServiceException { - log("Get room participants for room: " + roomName); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_GET_PARTICIPANT_LIST, + "roomName", roomName, + "serviceName", serviceName); ParticipantEntities participantEntities = new ParticipantEntities(); List participants = new ArrayList<>(); @@ -523,7 +542,9 @@ public ParticipantEntities getRoomParticipants(String roomName, String serviceNa */ public OccupantEntities getRoomOccupants(String roomName, String serviceName) throws ServiceException { - log("Get room occupants for room: " + roomName); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_GET_OCCUPANT_LIST, + "roomName", roomName, + "serviceName", serviceName); OccupantEntities occupantEntities = new OccupantEntities(); List occupants = new ArrayList<>(); @@ -553,7 +574,9 @@ public OccupantEntities getRoomOccupants(String roomName, String serviceName) th * @return the room chat history */ public MUCRoomMessageEntities getRoomHistory(String roomName, String serviceName) throws ServiceException { - log("Get room history for room: " + roomName); + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_GET_ROOM_HISTORY, + "roomName", roomName, + "serviceName", serviceName); MUCRoomMessageEntities mucRoomMessageEntities = new MUCRoomMessageEntities(); List listMessages = new ArrayList<>(); @@ -602,6 +625,11 @@ public MUCRoomMessageEntities getRoomHistory(String roomName, String serviceName */ public void inviteUsersAndOrGroups(String serviceName, String roomName, MUCInvitationsEntity mucInvitationsEntity) throws ServiceException { + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_INVITE_USER, + "roomName", roomName, + "serviceName", serviceName, + "jid", jid, + "invitation" , mucInvitationEntity); MUCRoom room = getRoom(serviceName, roomName); // First determine where to send all the invitations @@ -987,6 +1015,7 @@ private static Collection setRoles(MUCRoom room, MUCRoomEntity mucRoomEntit */ public Collection getByAffiliation(@Nonnull final String serviceName, @Nonnull final String roomName, @Nonnull final MUCRole.Affiliation affiliation) throws ServiceException { + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_LIST_AFFILIATED_USERS_FOR_AFFILIATION, serviceName, roomName, affiliation); final MUCRoom room = getRoom(serviceName, roomName); switch (affiliation) { case admin: @@ -1025,6 +1054,7 @@ public Collection getByAffiliation(@Nonnull final String serviceName, @Nonn */ public void replaceAffiliatedUsers(@Nonnull final String serviceName, @Nonnull final String roomName, @Nonnull final MUCRole.Affiliation affiliation, boolean sendInvitations, @Nonnull final String... jids) throws ServiceException { + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_REPLACE_AFFILIATED_USERS_FOR_AFFILIATION, serviceName, roomName, affiliation, jids); final Collection replacements = new HashSet<>(); // Input validation. @@ -1110,6 +1140,7 @@ public void replaceAffiliatedUsers(@Nonnull final String serviceName, @Nonnull f */ public void addAffiliatedUsers(@Nonnull final String serviceName, @Nonnull final String roomName, @Nonnull final MUCRole.Affiliation affiliation, boolean sendInvitations, @Nonnull final String... jids) throws ServiceException { + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_ADD_AFFILIATED_USERS_FOR_AFFILIATION, serviceName, roomName, affiliation, jids); final Collection additions = new HashSet<>(); // Input validation. @@ -1183,6 +1214,7 @@ public void addAffiliatedUsers(@Nonnull final String serviceName, @Nonnull final * the service exception */ public void deleteAffiliation(String serviceName, String roomName, MUCRole.Affiliation affiliation, String jid) throws ServiceException { + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_REMOVE_AFFILIATED_USER_OR_GROUP_FOR_AFFILIATION, serviceName, roomName, jid); MUCRoom room = getRoom(serviceName, roomName); try { JID userJid = UserUtils.checkAndGetJID(jid); diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MessageController.java b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MessageController.java index 25e16fbb4..305ba597d 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MessageController.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MessageController.java @@ -22,6 +22,7 @@ import org.jivesoftware.openfire.plugin.rest.entity.MessageEntity; import org.jivesoftware.openfire.plugin.rest.exceptions.ExceptionType; import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException; +import org.jivesoftware.openfire.plugin.rest.utils.LoggingUtils; /** * The Class MessageController. @@ -48,6 +49,7 @@ public static MessageController getInstance() { * the service exception */ public void sendBroadcastMessage(MessageEntity messageEntity) throws ServiceException { + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MESSAGE_BROADCAST, messageEntity); if (messageEntity.getBody() != null && !messageEntity.getBody().isEmpty()) { SessionManager.getInstance().sendServerMessage(null, messageEntity.getBody()); } else { diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MsgArchiveController.java b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MsgArchiveController.java index 41536ee11..6bab0ee41 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MsgArchiveController.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MsgArchiveController.java @@ -22,6 +22,7 @@ import java.sql.SQLException; import org.jivesoftware.database.DbConnectionManager; +import org.jivesoftware.openfire.plugin.rest.utils.LoggingUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xmpp.packet.JID; @@ -64,6 +65,7 @@ private MsgArchiveController() { * @return the total number of user unread messages. */ public int getUnReadMessagesCount(JID jid) { + LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MESSAGE_ARCHIVE_UNREAD_COUNT, jid); int messageCount = 0; Connection con = null; PreparedStatement pstmt = null; diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java index 90b50d669..df4a52a89 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java @@ -17,9 +17,9 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.MoreObjects; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; +import org.glassfish.jersey.internal.guava.MoreObjects; import org.jivesoftware.util.StringUtils; import java.util.List; diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCInvitationEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCInvitationEntity.java index 907daa31c..84de75129 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCInvitationEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCInvitationEntity.java @@ -38,4 +38,8 @@ public String getReason() { public void setReason(String reason) { this.reason = reason; } + @Override + public String toString() { + return "MUCInvitationEntity [reason=" + reason + "]"; + } } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntity.java index e846ac042..1e90f3a4d 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntity.java @@ -18,6 +18,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; +import org.glassfish.jersey.internal.guava.MoreObjects; import org.xmpp.packet.JID; import java.util.Date; @@ -363,4 +364,32 @@ public void setAdminGroups(List adminGroups) { this.adminGroups = adminGroups; } + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("roomName", roomName) + .add("description", description) + .add("persistent", persistent) + .add("publicRoom", publicRoom) + .add("registrationEnabled", registrationEnabled) + .add("canAnyoneDiscoverJID", canAnyoneDiscoverJID) + .add("canOccupantsChangeSubject", canOccupantsChangeSubject) + .add("canOccupantsInvite", canOccupantsInvite) + .add("canChangeNickname", canChangeNickname) + .add("logEnabled", logEnabled) + .add("loginRestrictedToNickname", loginRestrictedToNickname) + .add("membersOnly", membersOnly) + .add("moderated", moderated) + .add("broadcastPresenceRoles", broadcastPresenceRoles) + .add("owners", owners) + .add("ownerGroups", ownerGroups) + .add("members", members) + .add("memberGroups", memberGroups) + .add("outcasts", outcasts) + .add("outcastGroups", outcastGroups) + .add("admins", admins) + .add("adminGroups", adminGroups) + .toString(); + } + } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MessageEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MessageEntity.java index 3458cc1af..1acbc6ac2 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MessageEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MessageEntity.java @@ -53,4 +53,9 @@ public String getBody() { public void setBody(String body) { this.body = body; } + + @Override + public String toString() { + return "MessageEntity [body=" + body + "]"; + } } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java b/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java index 518ec59e9..4c876d9d4 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java @@ -22,7 +22,6 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.Arrays; public class LoggingUtils { private static final Logger AUDIT_LOG = LoggerFactory.getLogger("RestAPI-Plugin-Audit"); @@ -41,7 +40,34 @@ public enum AuditEvent { GROUPS_DELETE, //JustMarried - USER_CHANGE_NAME + USER_CHANGE_NAME, + + //Messages + MESSAGE_BROADCAST, + + //Message Archive + MESSAGE_ARCHIVE_UNREAD_COUNT, + + //MUC + // - MUC Affiliations + MUC_LIST_AFFILIATED_USERS_FOR_AFFILIATION, + MUC_REPLACE_AFFILIATED_USERS_FOR_AFFILIATION, + MUC_REMOVE_AFFILIATED_USER_OR_GROUP_FOR_AFFILIATION, + MUC_ADD_AFFILIATED_USERS_FOR_AFFILIATION, + MUC_ADD_AFFILIATED_USER_OR_GROUP_AS_ADMIN, + MUC_ADD_AFFILIATED_USER_OR_GROUP_AS_MEMBER, + MUC_ADD_AFFILIATED_USER_OR_GROUP_AS_OUTCAST, + MUC_ADD_AFFILIATED_USER_OR_GROUP_AS_OWNER, + // - MUC Rooms + MUC_LIST_ROOMS, + MUC_GET_ROOM, + MUC_DELETE_ROOM, + MUC_CREATE_ROOM, + MUC_UPDATE_ROOM, + MUC_GET_PARTICIPANT_LIST, + MUC_GET_OCCUPANT_LIST, + MUC_GET_ROOM_HISTORY, + MUC_INVITE_USER, ; } @@ -55,11 +81,14 @@ public static void auditEvent(AuditEvent event, Object... parameters){ String logMessage = "Event: " + event; logMessage += " - "; logMessage += "Parameters: + " + parameterString; + logMessage += " - "; + logMessage += "Caller: " + getCaller(); AUDIT_LOG.info(logMessage); }; } private static String parseParameters(Object[] parameters) { + //TODO: Does this belong here? ArrayList parsed = new ArrayList<>(); for (Object obj: parameters) { if(obj == null){ @@ -74,4 +103,20 @@ private static String parseParameters(Object[] parameters) { } return parsed.toString(); } + + /* + * Returns the name and method of the calling class. + */ + private static String getCaller() { + try { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + for (StackTraceElement element : stackTrace) { + if(element.getClassName().equals(LoggingUtils.class.getName())){ + continue; + } + return element.getClassName() + "." + element.getMethodName(); + } + } catch (Exception ignored) {} + return "unknown"; + } } From de2600c0d98e5dfe50556566927e7f27daff6a8f Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Sat, 2 Jul 2022 17:30:33 +0100 Subject: [PATCH 4/6] WIP: Simple fixes following review --- .../openfire/plugin/rest/AuthFilter.java | 28 ++++++---- .../plugin/rest/entity/GroupEntity.java | 19 +++---- .../plugin/rest/entity/MUCRoomEntity.java | 51 +++++++++---------- .../plugin/rest/utils/LoggingUtils.java | 6 ++- 4 files changed, 54 insertions(+), 50 deletions(-) diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java b/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java index b6bd5597e..b767d0cf2 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java @@ -86,16 +86,7 @@ public void filter(ContainerRequestContext containerRequest) throws IOException if (!plugin.getAllowedIPs().isEmpty()) { // Get client's IP address - String ipAddress = httpRequest.getHeader("x-forwarded-for"); - if (ipAddress == null) { - ipAddress = httpRequest.getHeader("X_FORWARDED_FOR"); - if (ipAddress == null) { - ipAddress = httpRequest.getHeader("X-Forward-For"); - if (ipAddress == null) { - ipAddress = httpRequest.getRemoteAddr(); - } - } - } + String ipAddress = getClientIPAddressForRequest(httpRequest); if (!plugin.getAllowedIPs().contains(ipAddress)) { LOG.warn("REST API rejected service for IP address: " + ipAddress); throw new WebApplicationException(Status.UNAUTHORIZED); @@ -106,7 +97,8 @@ public void filter(ContainerRequestContext containerRequest) throws IOException String auth = containerRequest.getHeaderString("authorization"); if (auth == null) { - LOG.warn("REST API request with no Authorization header rejected"); + LOG.warn("REST API request with no Authorization header rejected. [Request IP: {}, Request URI: {}]", + getClientIPAddressForRequest(httpRequest), containerRequest.getUriInfo().getRequestUri()); throw new WebApplicationException(Status.UNAUTHORIZED); } @@ -153,4 +145,18 @@ private boolean isStatusEndpoint(String path){ path.equals("/plugins/restapi/v1/system/readiness") || path.startsWith("/plugins/restapi/v1/system/readiness/"); } + + private String getClientIPAddressForRequest(HttpServletRequest request) { + String ipAddress = request.getHeader("x-forwarded-for"); + if (ipAddress == null) { + ipAddress = request.getHeader("X_FORWARDED_FOR"); + if (ipAddress == null) { + ipAddress = request.getHeader("X-Forward-For"); + if (ipAddress == null) { + ipAddress = request.getRemoteAddr(); + } + } + } + return ipAddress; + } } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java index df4a52a89..b54b70981 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java @@ -19,11 +19,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; -import org.glassfish.jersey.internal.guava.MoreObjects; -import org.jivesoftware.util.StringUtils; - import java.util.List; -import java.util.Optional; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; @@ -178,14 +174,13 @@ public void setMembers(List members) { @Override public String toString() { - return MoreObjects.toStringHelper(this) - .add("name", this.name) - .add("description", this.description) - .add( "admins", this.admins) - .add("members", this.members) - .add("shared", this.shared) - .toString(); - + return "GroupEntity [" + + "name='" + name + + ", description='" + description + + ", admins=" + admins + + ", members=" + members + + ", shared=" + shared + + "]"; } } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntity.java index 1e90f3a4d..86e95a170 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntity.java @@ -18,7 +18,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; -import org.glassfish.jersey.internal.guava.MoreObjects; import org.xmpp.packet.JID; import java.util.Date; @@ -366,30 +365,30 @@ public void setAdminGroups(List adminGroups) { @Override public String toString() { - return MoreObjects.toStringHelper(this) - .add("roomName", roomName) - .add("description", description) - .add("persistent", persistent) - .add("publicRoom", publicRoom) - .add("registrationEnabled", registrationEnabled) - .add("canAnyoneDiscoverJID", canAnyoneDiscoverJID) - .add("canOccupantsChangeSubject", canOccupantsChangeSubject) - .add("canOccupantsInvite", canOccupantsInvite) - .add("canChangeNickname", canChangeNickname) - .add("logEnabled", logEnabled) - .add("loginRestrictedToNickname", loginRestrictedToNickname) - .add("membersOnly", membersOnly) - .add("moderated", moderated) - .add("broadcastPresenceRoles", broadcastPresenceRoles) - .add("owners", owners) - .add("ownerGroups", ownerGroups) - .add("members", members) - .add("memberGroups", memberGroups) - .add("outcasts", outcasts) - .add("outcastGroups", outcastGroups) - .add("admins", admins) - .add("adminGroups", adminGroups) - .toString(); + return "MUCRoomEntity [" + + "roomName=" + roomName + + ", naturalName=" + naturalName + + ", description=" + description + + ", persistent=" + persistent + + ", publicRoom=" + publicRoom + + ", canAnyoneDiscoverJID=" + canAnyoneDiscoverJID + + ", canOccupantsChangeSubject=" + canOccupantsChangeSubject + + ", canOccupantsInvite=" + canOccupantsInvite + + ", canChangeNickname=" + canChangeNickname + + ", logEnabled=" + logEnabled + + ", loginRestrictedToNickname=" + loginRestrictedToNickname + + ", membersOnly=" + membersOnly + + ", moderated=" + moderated + + ", registrationEnabled=" + registrationEnabled + + ", broadcastPresenceRoles=" + broadcastPresenceRoles + + ", owners=" + owners + + ", ownerGroups=" + ownerGroups + + ", members=" + members + + ", memberGroups=" + memberGroups + + ", outcasts=" + outcasts + + ", outcastGroups=" + outcastGroups + + ", admins=" + admins + + ", adminGroups=" + adminGroups + + "]"; } - } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java b/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java index 4c876d9d4..aa789caf0 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/utils/LoggingUtils.java @@ -16,6 +16,7 @@ package org.jivesoftware.openfire.plugin.rest.utils; +import org.eclipse.jetty.util.log.Log; import org.jivesoftware.openfire.plugin.rest.RESTServicePlugin; import org.jivesoftware.util.JiveGlobals; import org.slf4j.Logger; @@ -25,6 +26,7 @@ public class LoggingUtils { private static final Logger AUDIT_LOG = LoggerFactory.getLogger("RestAPI-Plugin-Audit"); + private static final Logger LOG = LoggerFactory.getLogger(LoggingUtils.class); public enum AuditEvent { //Clustering @@ -116,7 +118,9 @@ private static String getCaller() { } return element.getClassName() + "." + element.getMethodName(); } - } catch (Exception ignored) {} + } catch (Exception e) { + LOG.error("Unable to get caller of the logger. This should be impossible.", e); + } return "unknown"; } } From 35046e434b265585dbaefbc008e4697c8a1a492f Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Fri, 18 Nov 2022 21:30:29 +0000 Subject: [PATCH 5/6] Rebase fix --- .../openfire/plugin/rest/controller/MUCRoomController.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java index 49dd5c318..0e3d97aca 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MUCRoomController.java @@ -628,8 +628,7 @@ public void inviteUsersAndOrGroups(String serviceName, String roomName, MUCInvit LoggingUtils.auditEvent(LoggingUtils.AuditEvent.MUC_INVITE_USER, "roomName", roomName, "serviceName", serviceName, - "jid", jid, - "invitation" , mucInvitationEntity); + "invitation" , mucInvitationsEntity); MUCRoom room = getRoom(serviceName, roomName); // First determine where to send all the invitations From fd2f68f21e89b333bce8a90bf5bc88b001aab426 Mon Sep 17 00:00:00 2001 From: Dan Caseley Date: Fri, 18 Nov 2022 22:54:07 +0000 Subject: [PATCH 6/6] Improve logging in AuthFilter --- .../openfire/plugin/rest/AuthFilter.java | 117 +++++++++++++----- 1 file changed, 85 insertions(+), 32 deletions(-) diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java b/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java index b767d0cf2..a4197b416 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java @@ -25,6 +25,7 @@ import javax.ws.rs.container.PreMatching; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.SecurityContext; import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.admin.AdminManager; @@ -32,10 +33,12 @@ import org.jivesoftware.openfire.auth.ConnectionException; import org.jivesoftware.openfire.auth.InternalUnauthenticatedException; import org.jivesoftware.openfire.auth.UnauthorizedException; +import org.jivesoftware.util.JiveGlobals; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.security.Principal; /** * The Class AuthFilter. @@ -55,32 +58,17 @@ public class AuthFilter implements ContainerRequestFilter { private RESTServicePlugin plugin = (RESTServicePlugin) XMPPServer.getInstance().getPluginManager() .getPlugin("restapi"); - @Override - public void filter(ContainerRequestContext containerRequest) throws IOException { - if (containerRequest.getUriInfo().getRequestUri().getPath().equals("/plugins/restapi/v1/openapi.yaml")) { - LOG.debug("Authentication was bypassed for openapi.yaml file (documentation)"); - return; - } + public static final String SHARED_SECRET_AUTHENTICATION_SCHEME = "SharedSecret"; - if (isStatusEndpoint(containerRequest.getUriInfo().getRequestUri().getPath())) { - LOG.debug("Authentication was bypassed for a status endpoint"); - return; - } + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { if (!plugin.isEnabled()) { LOG.debug("REST API Plugin is not enabled"); throw new WebApplicationException(Status.FORBIDDEN); } - - // Let the preflight request through the authentication - if ("OPTIONS".equals(containerRequest.getMethod())) { - LOG.debug("Authentication was bypassed because of OPTIONS request"); - return; - } - - // To be backwards compatible to userservice 1.* - if (containerRequest.getUriInfo().getRequestUri().getPath().contains("restapi/v1/userservice")) { - LOG.info("Deprecated 'userservice' endpoint was used. Please switch to the new endpoints"); + + if (!authRequired(requestContext)){ return; } @@ -94,11 +82,11 @@ public void filter(ContainerRequestContext containerRequest) throws IOException } // Get the authentication passed in HTTP headers parameters - String auth = containerRequest.getHeaderString("authorization"); + String auth = requestContext.getHeaderString("authorization"); if (auth == null) { LOG.warn("REST API request with no Authorization header rejected. [Request IP: {}, Request URI: {}]", - getClientIPAddressForRequest(httpRequest), containerRequest.getUriInfo().getRequestUri()); + getClientIPAddressForRequest(httpRequest), requestContext.getUriInfo().getRequestUri().getPath()); throw new WebApplicationException(Status.UNAUTHORIZED); } @@ -108,37 +96,102 @@ public void filter(ContainerRequestContext containerRequest) throws IOException // If username or password fail if (usernameAndPassword == null || usernameAndPassword.length != 2) { - LOG.warn("Username or password is not set"); - throw new WebApplicationException(Status.UNAUTHORIZED); + LOG.warn("Basic authentication failed. Username or password is not set. [Request IP: {}, Request URI: {}]", + getClientIPAddressForRequest(httpRequest), requestContext.getUriInfo().getRequestUri().getPath()); + throw new WebApplicationException("Username or password is not set", Status.UNAUTHORIZED); } boolean userAdmin = AdminManager.getInstance().isUserAdmin(usernameAndPassword[0], true); if (!userAdmin) { LOG.warn("Provided User is not an admin"); - throw new WebApplicationException(Status.UNAUTHORIZED); + throw new WebApplicationException("User is not authorised", Status.UNAUTHORIZED); } try { AuthFactory.authenticate(usernameAndPassword[0], usernameAndPassword[1]); + setSecurityForContext(requestContext, usernameAndPassword[0], SecurityContext.BASIC_AUTH); + if (JiveGlobals.getBooleanProperty(RESTServicePlugin.SERVICE_LOGGING_ENABLED, false)) { + LOG.info("Authentication - successfully authenticated user. [Request IP: {}, Request URI: {}, Username: {}]", + getClientIPAddressForRequest(httpRequest), requestContext.getUriInfo().getRequestUri().getPath(), usernameAndPassword[0]); + } } catch (UnauthorizedException e) { - LOG.warn("Wrong HTTP Basic Auth authorization", e); - throw new WebApplicationException(Status.UNAUTHORIZED); - } catch (ConnectionException e) { - LOG.error("Authentication went wrong", e); - throw new WebApplicationException(Status.UNAUTHORIZED); - } catch (InternalUnauthenticatedException e) { + LOG.warn("Basic authentication failed. Username or password is incorrect. [Request IP: {}, Request URI: {}]", + getClientIPAddressForRequest(httpRequest), requestContext.getUriInfo().getRequestUri().getPath()); + LOG.warn("Authentication error", e); + throw new WebApplicationException("Username or password is incorrect", Status.UNAUTHORIZED); + } catch (ConnectionException | InternalUnauthenticatedException e) { LOG.error("Authentication went wrong", e); throw new WebApplicationException(Status.UNAUTHORIZED); } } else { if (!auth.equals(plugin.getSecret())) { - LOG.warn("Wrong secret key authorization. Provided key: " + auth); + LOG.warn("Wrong secret key authorization. [Request IP: {}, Request URI: {}, Request auth: {}]", + getClientIPAddressForRequest(httpRequest), requestContext.getUriInfo().getRequestUri().getPath(), auth); throw new WebApplicationException(Status.UNAUTHORIZED); + } else { + //For shared secret, use the authentication scheme to indicate that a username is unknown + setSecurityForContext(requestContext, SHARED_SECRET_AUTHENTICATION_SCHEME, SHARED_SECRET_AUTHENTICATION_SCHEME); + if (JiveGlobals.getBooleanProperty(RESTServicePlugin.SERVICE_LOGGING_ENABLED, false)) { + LOG.info("Authentication - successfully authenticated by secret key. [Request IP: {}, Request URI: {}]", + getClientIPAddressForRequest(httpRequest), requestContext.getUriInfo().getRequestUri().getPath()); + } } } } + private boolean authRequired(ContainerRequestContext requestContext){ + if (requestContext.getUriInfo().getRequestUri().getPath().equals("/plugins/restapi/v1/openapi.yaml")) { + LOG.debug("Authentication was bypassed for openapi.yaml file (documentation)"); + return false; + } + + if (isStatusEndpoint(requestContext.getUriInfo().getRequestUri().getPath())) { + LOG.debug("Authentication was bypassed for a status endpoint"); + return false; + } + + // Let the preflight request through the authentication + if ("OPTIONS".equals(requestContext.getMethod())) { + LOG.debug("Authentication was bypassed because of OPTIONS request"); + return false; + } + + // To be backwards compatible to userservice 1.* + if (requestContext.getUriInfo().getRequestUri().getPath().contains("restapi/v1/userservice")) { + LOG.info("Deprecated 'userservice' endpoint was used. Please switch to the new endpoints"); + return false; + } + + return true; + } + + private void setSecurityForContext(ContainerRequestContext requestContext, String username, String authScheme){ + final SecurityContext currentSecurityContext = requestContext.getSecurityContext(); + requestContext.setSecurityContext(new SecurityContext() { + + @Override + public Principal getUserPrincipal() { + return () -> username; + } + + @Override + public boolean isUserInRole(String role) { + return true; + } + + @Override + public boolean isSecure() { + return currentSecurityContext.isSecure(); + } + + @Override + public String getAuthenticationScheme() { + return authScheme; + } + }); + } + private boolean isStatusEndpoint(String path){ return path.equals("/plugins/restapi/v1/system/liveness") || path.startsWith("/plugins/restapi/v1/system/liveness/") ||