943c1230 by Ean Schuessler

WIP switch to internal permissions

1 parent 48c958e9
......@@ -44,9 +44,10 @@
<moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="McpServices.list#Products" artifactTypeEnumId="AT_SERVICE"/>
<moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.ledger.LedgerServices.find#GlAccount" artifactTypeEnumId="AT_SERVICE"/>
<!-- Entity Services -->
<moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="org.moqui.impl.EntityServices.find#Map" artifactTypeEnumId="AT_SERVICE"/>
<moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="create#moqui.server.Visit" artifactTypeEnumId="AT_SERVICE"/>
<moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="update#moqui.server.Visit" artifactTypeEnumId="AT_SERVICE"/>
<moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="org.moqui.impl.EntityServices.find#Entity" artifactTypeEnumId="AT_SERVICE"/>
<moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="org.moqui.impl.EntityServices.create#Entity" artifactTypeEnumId="AT_SERVICE"/>
<moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="org.moqui.impl.EntityServices.update#Entity" artifactTypeEnumId="AT_SERVICE"/>
<moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="org.moqui.impl.EntityServices.delete#Entity" artifactTypeEnumId="AT_SERVICE"/>
<!-- Essential Business Entities -->
<moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.order.OrderHeader" artifactTypeEnumId="AT_ENTITY"/>
......@@ -91,9 +92,4 @@
<!-- Add existing demo users to MCP business group for focused testing -->
<moqui.security.UserGroupMember userGroupId="MCP_BUSINESS" userId="ORG_ZIZI_JD" fromDate="2025-01-01 00:00:00.000"/>
<moqui.security.UserGroupMember userGroupId="MCP_BUSINESS" userId="ORG_ZIZI_BD" fromDate="2025-01-01 00:00:00.000"/>
<!-- Keep ADMIN access for system operations -->
<moqui.security.UserGroupMember userGroupId="ADMIN" userId="MCP_USER" fromDate="2025-01-01 00:00:00.000"/>
<moqui.security.UserGroupMember userGroupId="ADMIN" userId="MCP_BUSINESS" fromDate="2025-01-01 00:00:00.000"/>
</entity-facade-xml>
\ No newline at end of file
......
......@@ -416,20 +416,10 @@ logger.info("Handling Enhanced SSE connection from ${request.remoteAddr}")
return
}
// Verify user has access to this Visit - more permissive for testing
// Verify user has access to this Visit - rely on Moqui security
logger.info("Session validation: visit.userId=${visit.userId}, ec.user.userId=${ec.user.userId}, ec.user.username=${ec.user.username}")
logger.info("DEBUG2: visit.userId exists=${visit.userId != null}, ec.user.userId exists=${ec.user.userId != null}, notEqual=${visit.userId?.toString() != ec.user.userId?.toString()}")
if (visit.userId && ec.user.userId && visit.userId.toString() != ec.user.userId.toString()) {
logger.warn("Visit userId ${visit.userId} doesn't match current user userId ${ec.user.userId}")
// Special case: MCP services run with ADMIN privileges but authenticate as MCP_USER or MCP_BUSINESS
boolean specialMcpCase = visit.userId == "ADMIN" && (ec.user.userId == "MCP_USER" || ec.user.userId == "MCP_BUSINESS")
logger.info("DEBUG: visit.userId='${visit.userId}' (class: ${visit.userId?.class?.name}), ec.user.userId='${ec.user.userId}' (class: ${ec.user.userId?.class?.name}), specialMcpCase=${specialMcpCase}")
if (specialMcpCase) {
logger.info("Allowing MCP service access: Visit created with ADMIN, accessed by ${ec.user.userId}")
} else if (visit.userCreated == "Y" && ec.user.username) {
logger.info("Allowing access for user ${ec.user.username} to Visit ${sessionId}")
} else {
logger.warn("Visit userId ${visit.userId} doesn't match current user userId ${ec.user.userId} - access denied")
response.setContentType("application/json")
response.setCharacterEncoding("UTF-8")
response.setStatus(HttpServletResponse.SC_FORBIDDEN)
......@@ -439,7 +429,6 @@ logger.info("Handling Enhanced SSE connection from ${request.remoteAddr}")
]))
return
}
}
// Create session wrapper for this Visit
VisitBasedMcpSession session = new VisitBasedMcpSession(visit, response.writer, ec)
......@@ -689,18 +678,8 @@ logger.info("Handling Enhanced SSE connection from ${request.remoteAddr}")
// Allow access if:
// 1. Visit userId matches current user, OR
// 2. Visit was created with ADMIN (for privileged access) but current user is MCP_USER (actual authenticated user)
boolean accessAllowed = false
if (visit.userId && ec.user.userId) {
if (visit.userId.toString() == ec.user.userId.toString()) {
accessAllowed = true
} else if (visit.userId.toString() == "ADMIN" && (ec.user.userId.toString() == "MCP_USER" || ec.user.userId.toString() == "MCP_BUSINESS")) {
// Special case: MCP services run with ADMIN privileges but authenticate as MCP_USER or MCP_BUSINESS
accessAllowed = true
logger.info("Allowing MCP privileged access: Visit created with ADMIN, accessed by ${ec.user.userId}")
}
}
if (!accessAllowed) {
// Rely on Moqui security - only allow access if visit and current user match
if (!visit.userId || !ec.user.userId || visit.userId.toString() != ec.user.userId.toString()) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN)
response.setContentType("application/json")
response.writer.write(groovy.json.JsonOutput.toJson([
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
This software is in the public domain under CC0 1.0 Universal plus a
Grant of Patent License.
To the extent possible under law, author(s) have dedicated all
copyright and related and neighboring rights to this software to the
public domain worldwide. This software is distributed without any
warranty.
You should have received a copy of the CC0 Public Domain Dedication
along with this software (see the LICENSE.md file). If not, see
<http://creativecommons.org/publicdomain/zero/1.0/>.
-->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- Service-Based MCP Servlet Configuration -->
<servlet>
<servlet-name>EnhancedMcpServlet</servlet-name>
<servlet-class>org.moqui.mcp.EnhancedMcpServlet</servlet-class>
<init-param>
<param-name>keepAliveIntervalSeconds</param-name>
<param-value>30</param-value>
</init-param>
<init-param>
<param-name>maxConnections</param-name>
<param-value>100</param-value>
</init-param>
<!-- Enable async support for SSE -->
<async-supported>true</async-supported>
<!-- Load on startup -->
<load-on-startup>5</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>EnhancedMcpServlet</servlet-name>
<url-pattern>/mcp/*</url-pattern>
</servlet-mapping>
<!-- Session Configuration -->
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<http-only>true</http-only>
<secure>false</secure>
</cookie-config>
</session-config>
<!-- Security Constraints (optional - uncomment if needed) -->
<!--
<security-constraint>
<web-resource-collection>
<web-resource-name>MCP Endpoints</web-resource-name>
<url-pattern>/sse/*</url-pattern>
<url-pattern>/mcp/message/*</url-pattern>
<url-pattern>/rpc/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Moqui MCP</realm-name>
</login-config>
-->
<!-- MIME Type Mappings -->
<mime-mapping>
<extension>json</extension>
<mime-type>application/json</mime-type>
</mime-mapping>
<!-- Default Welcome Files -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>