WIP switch to internal permissions
Showing
4 changed files
with
97 additions
and
33 deletions
| ... | @@ -44,9 +44,10 @@ | ... | @@ -44,9 +44,10 @@ |
| 44 | <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="McpServices.list#Products" artifactTypeEnumId="AT_SERVICE"/> | 44 | <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="McpServices.list#Products" artifactTypeEnumId="AT_SERVICE"/> |
| 45 | <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.ledger.LedgerServices.find#GlAccount" artifactTypeEnumId="AT_SERVICE"/> | 45 | <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.ledger.LedgerServices.find#GlAccount" artifactTypeEnumId="AT_SERVICE"/> |
| 46 | <!-- Entity Services --> | 46 | <!-- Entity Services --> |
| 47 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="org.moqui.impl.EntityServices.find#Map" artifactTypeEnumId="AT_SERVICE"/> | 47 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="org.moqui.impl.EntityServices.find#Entity" artifactTypeEnumId="AT_SERVICE"/> |
| 48 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="create#moqui.server.Visit" artifactTypeEnumId="AT_SERVICE"/> | 48 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="org.moqui.impl.EntityServices.create#Entity" artifactTypeEnumId="AT_SERVICE"/> |
| 49 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="update#moqui.server.Visit" artifactTypeEnumId="AT_SERVICE"/> | 49 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="org.moqui.impl.EntityServices.update#Entity" artifactTypeEnumId="AT_SERVICE"/> |
| 50 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="org.moqui.impl.EntityServices.delete#Entity" artifactTypeEnumId="AT_SERVICE"/> | ||
| 50 | 51 | ||
| 51 | <!-- Essential Business Entities --> | 52 | <!-- Essential Business Entities --> |
| 52 | <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.order.OrderHeader" artifactTypeEnumId="AT_ENTITY"/> | 53 | <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.order.OrderHeader" artifactTypeEnumId="AT_ENTITY"/> |
| ... | @@ -91,9 +92,4 @@ | ... | @@ -91,9 +92,4 @@ |
| 91 | <!-- Add existing demo users to MCP business group for focused testing --> | 92 | <!-- Add existing demo users to MCP business group for focused testing --> |
| 92 | <moqui.security.UserGroupMember userGroupId="MCP_BUSINESS" userId="ORG_ZIZI_JD" fromDate="2025-01-01 00:00:00.000"/> | 93 | <moqui.security.UserGroupMember userGroupId="MCP_BUSINESS" userId="ORG_ZIZI_JD" fromDate="2025-01-01 00:00:00.000"/> |
| 93 | <moqui.security.UserGroupMember userGroupId="MCP_BUSINESS" userId="ORG_ZIZI_BD" fromDate="2025-01-01 00:00:00.000"/> | 94 | <moqui.security.UserGroupMember userGroupId="MCP_BUSINESS" userId="ORG_ZIZI_BD" fromDate="2025-01-01 00:00:00.000"/> |
| 94 | |||
| 95 | <!-- Keep ADMIN access for system operations --> | ||
| 96 | <moqui.security.UserGroupMember userGroupId="ADMIN" userId="MCP_USER" fromDate="2025-01-01 00:00:00.000"/> | ||
| 97 | <moqui.security.UserGroupMember userGroupId="ADMIN" userId="MCP_BUSINESS" fromDate="2025-01-01 00:00:00.000"/> | ||
| 98 | |||
| 99 | </entity-facade-xml> | 95 | </entity-facade-xml> |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
This diff is collapsed.
Click to expand it.
| ... | @@ -416,20 +416,10 @@ logger.info("Handling Enhanced SSE connection from ${request.remoteAddr}") | ... | @@ -416,20 +416,10 @@ logger.info("Handling Enhanced SSE connection from ${request.remoteAddr}") |
| 416 | return | 416 | return |
| 417 | } | 417 | } |
| 418 | 418 | ||
| 419 | // Verify user has access to this Visit - more permissive for testing | 419 | // Verify user has access to this Visit - rely on Moqui security |
| 420 | logger.info("Session validation: visit.userId=${visit.userId}, ec.user.userId=${ec.user.userId}, ec.user.username=${ec.user.username}") | 420 | logger.info("Session validation: visit.userId=${visit.userId}, ec.user.userId=${ec.user.userId}, ec.user.username=${ec.user.username}") |
| 421 | 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()}") | ||
| 422 | if (visit.userId && ec.user.userId && visit.userId.toString() != ec.user.userId.toString()) { | 421 | if (visit.userId && ec.user.userId && visit.userId.toString() != ec.user.userId.toString()) { |
| 423 | logger.warn("Visit userId ${visit.userId} doesn't match current user userId ${ec.user.userId}") | 422 | logger.warn("Visit userId ${visit.userId} doesn't match current user userId ${ec.user.userId} - access denied") |
| 424 | |||
| 425 | // Special case: MCP services run with ADMIN privileges but authenticate as MCP_USER or MCP_BUSINESS | ||
| 426 | boolean specialMcpCase = visit.userId == "ADMIN" && (ec.user.userId == "MCP_USER" || ec.user.userId == "MCP_BUSINESS") | ||
| 427 | 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}") | ||
| 428 | if (specialMcpCase) { | ||
| 429 | logger.info("Allowing MCP service access: Visit created with ADMIN, accessed by ${ec.user.userId}") | ||
| 430 | } else if (visit.userCreated == "Y" && ec.user.username) { | ||
| 431 | logger.info("Allowing access for user ${ec.user.username} to Visit ${sessionId}") | ||
| 432 | } else { | ||
| 433 | response.setContentType("application/json") | 423 | response.setContentType("application/json") |
| 434 | response.setCharacterEncoding("UTF-8") | 424 | response.setCharacterEncoding("UTF-8") |
| 435 | response.setStatus(HttpServletResponse.SC_FORBIDDEN) | 425 | response.setStatus(HttpServletResponse.SC_FORBIDDEN) |
| ... | @@ -439,7 +429,6 @@ logger.info("Handling Enhanced SSE connection from ${request.remoteAddr}") | ... | @@ -439,7 +429,6 @@ logger.info("Handling Enhanced SSE connection from ${request.remoteAddr}") |
| 439 | ])) | 429 | ])) |
| 440 | return | 430 | return |
| 441 | } | 431 | } |
| 442 | } | ||
| 443 | 432 | ||
| 444 | // Create session wrapper for this Visit | 433 | // Create session wrapper for this Visit |
| 445 | VisitBasedMcpSession session = new VisitBasedMcpSession(visit, response.writer, ec) | 434 | VisitBasedMcpSession session = new VisitBasedMcpSession(visit, response.writer, ec) |
| ... | @@ -689,18 +678,8 @@ logger.info("Handling Enhanced SSE connection from ${request.remoteAddr}") | ... | @@ -689,18 +678,8 @@ logger.info("Handling Enhanced SSE connection from ${request.remoteAddr}") |
| 689 | // Allow access if: | 678 | // Allow access if: |
| 690 | // 1. Visit userId matches current user, OR | 679 | // 1. Visit userId matches current user, OR |
| 691 | // 2. Visit was created with ADMIN (for privileged access) but current user is MCP_USER (actual authenticated user) | 680 | // 2. Visit was created with ADMIN (for privileged access) but current user is MCP_USER (actual authenticated user) |
| 692 | boolean accessAllowed = false | 681 | // Rely on Moqui security - only allow access if visit and current user match |
| 693 | if (visit.userId && ec.user.userId) { | 682 | if (!visit.userId || !ec.user.userId || visit.userId.toString() != ec.user.userId.toString()) { |
| 694 | if (visit.userId.toString() == ec.user.userId.toString()) { | ||
| 695 | accessAllowed = true | ||
| 696 | } else if (visit.userId.toString() == "ADMIN" && (ec.user.userId.toString() == "MCP_USER" || ec.user.userId.toString() == "MCP_BUSINESS")) { | ||
| 697 | // Special case: MCP services run with ADMIN privileges but authenticate as MCP_USER or MCP_BUSINESS | ||
| 698 | accessAllowed = true | ||
| 699 | logger.info("Allowing MCP privileged access: Visit created with ADMIN, accessed by ${ec.user.userId}") | ||
| 700 | } | ||
| 701 | } | ||
| 702 | |||
| 703 | if (!accessAllowed) { | ||
| 704 | response.setStatus(HttpServletResponse.SC_FORBIDDEN) | 683 | response.setStatus(HttpServletResponse.SC_FORBIDDEN) |
| 705 | response.setContentType("application/json") | 684 | response.setContentType("application/json") |
| 706 | response.writer.write(groovy.json.JsonOutput.toJson([ | 685 | response.writer.write(groovy.json.JsonOutput.toJson([ | ... | ... |
src/main/webapp/WEB-INF/web.xml
0 → 100644
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <!-- | ||
| 3 | This software is in the public domain under CC0 1.0 Universal plus a | ||
| 4 | Grant of Patent License. | ||
| 5 | |||
| 6 | To the extent possible under law, author(s) have dedicated all | ||
| 7 | copyright and related and neighboring rights to this software to the | ||
| 8 | public domain worldwide. This software is distributed without any | ||
| 9 | warranty. | ||
| 10 | |||
| 11 | You should have received a copy of the CC0 Public Domain Dedication | ||
| 12 | along with this software (see the LICENSE.md file). If not, see | ||
| 13 | <http://creativecommons.org/publicdomain/zero/1.0/>. | ||
| 14 | --> | ||
| 15 | |||
| 16 | <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" | ||
| 17 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 18 | xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee | ||
| 19 | http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" | ||
| 20 | version="4.0"> | ||
| 21 | |||
| 22 | <!-- Service-Based MCP Servlet Configuration --> | ||
| 23 | <servlet> | ||
| 24 | <servlet-name>EnhancedMcpServlet</servlet-name> | ||
| 25 | <servlet-class>org.moqui.mcp.EnhancedMcpServlet</servlet-class> | ||
| 26 | |||
| 27 | <init-param> | ||
| 28 | <param-name>keepAliveIntervalSeconds</param-name> | ||
| 29 | <param-value>30</param-value> | ||
| 30 | </init-param> | ||
| 31 | <init-param> | ||
| 32 | <param-name>maxConnections</param-name> | ||
| 33 | <param-value>100</param-value> | ||
| 34 | </init-param> | ||
| 35 | |||
| 36 | <!-- Enable async support for SSE --> | ||
| 37 | <async-supported>true</async-supported> | ||
| 38 | |||
| 39 | <!-- Load on startup --> | ||
| 40 | <load-on-startup>5</load-on-startup> | ||
| 41 | </servlet> | ||
| 42 | |||
| 43 | <servlet-mapping> | ||
| 44 | <servlet-name>EnhancedMcpServlet</servlet-name> | ||
| 45 | <url-pattern>/mcp/*</url-pattern> | ||
| 46 | </servlet-mapping> | ||
| 47 | |||
| 48 | <!-- Session Configuration --> | ||
| 49 | <session-config> | ||
| 50 | <session-timeout>30</session-timeout> | ||
| 51 | <cookie-config> | ||
| 52 | <http-only>true</http-only> | ||
| 53 | <secure>false</secure> | ||
| 54 | </cookie-config> | ||
| 55 | </session-config> | ||
| 56 | |||
| 57 | <!-- Security Constraints (optional - uncomment if needed) --> | ||
| 58 | <!-- | ||
| 59 | <security-constraint> | ||
| 60 | <web-resource-collection> | ||
| 61 | <web-resource-name>MCP Endpoints</web-resource-name> | ||
| 62 | <url-pattern>/sse/*</url-pattern> | ||
| 63 | <url-pattern>/mcp/message/*</url-pattern> | ||
| 64 | <url-pattern>/rpc/*</url-pattern> | ||
| 65 | </web-resource-collection> | ||
| 66 | <auth-constraint> | ||
| 67 | <role-name>admin</role-name> | ||
| 68 | </auth-constraint> | ||
| 69 | </security-constraint> | ||
| 70 | |||
| 71 | <login-config> | ||
| 72 | <auth-method>BASIC</auth-method> | ||
| 73 | <realm-name>Moqui MCP</realm-name> | ||
| 74 | </login-config> | ||
| 75 | --> | ||
| 76 | |||
| 77 | <!-- MIME Type Mappings --> | ||
| 78 | <mime-mapping> | ||
| 79 | <extension>json</extension> | ||
| 80 | <mime-type>application/json</mime-type> | ||
| 81 | </mime-mapping> | ||
| 82 | |||
| 83 | <!-- Default Welcome Files --> | ||
| 84 | <welcome-file-list> | ||
| 85 | <welcome-file>index.html</welcome-file> | ||
| 86 | <welcome-file>index.jsp</welcome-file> | ||
| 87 | </welcome-file-list> | ||
| 88 | |||
| 89 | </web-app> |
-
Please register or sign in to post a comment