Skip to content

Commit 9ecdff4

Browse files
committed
fix
1 parent 4a3c474 commit 9ecdff4

File tree

9 files changed

+400
-1
lines changed

9 files changed

+400
-1
lines changed

docker/auth-test/setup-test-users.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
-- Initial security data (roles required for the application to function)
2+
-- These must be created before WebAPI can properly handle authentication
3+
INSERT INTO webapi.sec_role (id, name, system_role) VALUES (1, 'public', true) ON CONFLICT (id) DO NOTHING;
4+
INSERT INTO webapi.sec_role (id, name, system_role) VALUES (2, 'admin', true) ON CONFLICT (id) DO NOTHING;
5+
INSERT INTO webapi.sec_role (id, name, system_role) VALUES (1001, 'Atlas users', true) ON CONFLICT (id) DO NOTHING;
6+
INSERT INTO webapi.sec_role (id, name, system_role) VALUES (1002, 'Moderator', true) ON CONFLICT (id) DO NOTHING;
7+
8+
-- Anonymous user (required for public endpoints)
9+
INSERT INTO webapi.sec_user (id, login, name) VALUES (1, 'anonymous', 'anonymous') ON CONFLICT (id) DO NOTHING;
10+
INSERT INTO webapi.sec_user_role (id, user_id, role_id, origin) VALUES (1, 1, 1, 'SYSTEM') ON CONFLICT (id) DO NOTHING;
11+
12+
-- Update sequences to avoid conflicts
13+
SELECT setval('webapi.sec_role_sequence', GREATEST((SELECT COALESCE(MAX(id), 0) + 1 FROM webapi.sec_role), nextval('webapi.sec_role_sequence')));
14+
SELECT setval('webapi.sec_user_sequence', GREATEST((SELECT COALESCE(MAX(id), 0) + 1 FROM webapi.sec_user), nextval('webapi.sec_user_sequence')));
15+
SELECT setval('webapi.sec_user_role_sequence', GREATEST((SELECT COALESCE(MAX(id), 0) + 1 FROM webapi.sec_user_role), nextval('webapi.sec_user_role_sequence')));
16+
17+
-- Test users table for JDBC authentication
118
CREATE TABLE IF NOT EXISTS webapi.users (
219
id SERIAL PRIMARY KEY,
320
email VARCHAR(255) UNIQUE NOT NULL,

docker/auth-test/test-results/auth-test-results.xml

Lines changed: 298 additions & 0 deletions
Large diffs are not rendered by default.

src/main/java/org/ohdsi/webapi/ShiroConfiguration.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ public ShiroFilterFactoryBean shiroFilter(Security security, LockoutPolicy locko
6464

6565
Map<String, String> filterChain = security.getFilterChain();
6666

67+
// Debug: log the filter chain configuration
68+
log.info("=== Shiro Filter Chain Configuration ===");
69+
log.info("Security implementation: {}", security.getClass().getName());
70+
log.info("Number of filters: {}", filters.size());
71+
log.info("Filter names: {}", filters.keySet());
72+
log.info("Filter chain paths ({} entries):", filterChain.size());
73+
filterChain.forEach((path, chain) -> log.info(" {} -> {}", path, chain));
74+
log.info("=== End Shiro Filter Chain Configuration ===");
75+
6776
shiroFilter.setFilterChainDefinitionMap(filterChain);
6877

6978
return shiroFilter;

src/main/java/org/ohdsi/webapi/shiro/filters/AuthenticatingPropagationFilter.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import jakarta.servlet.ServletResponse;
55
import jakarta.servlet.http.HttpServletResponse;
66
import org.ohdsi.webapi.arachne.logging.event.FailedLoginEvent;
7+
import java.io.IOException;
78
import org.ohdsi.webapi.arachne.logging.event.SuccessLoginEvent;
89
import org.ohdsi.webapi.shiro.ServletBridge;
910
import org.apache.shiro.authc.AuthenticationException;
@@ -55,6 +56,17 @@ protected boolean onLoginFailure(AuthenticationToken token, AuthenticationExcept
5556
boolean result = super.onLoginFailure(token, e, request, response);
5657
eventPublisher.publishEvent(new FailedLoginEvent(this, username));
5758
eventPublisher.publishEvent(new AuditTrailLoginFailedEvent(this, username, request.getRemoteHost()));
59+
60+
// Write response body and commit to prevent Spring MVC from processing
61+
try {
62+
httpResponse.setContentType("application/json");
63+
String errorMessage = e instanceof LockedAccountException ? e.getMessage() : "Invalid credentials";
64+
httpResponse.getWriter().write("{\"error\":\"" + errorMessage + "\"}");
65+
httpResponse.flushBuffer(); // Commit the response
66+
} catch (IOException ioe) {
67+
// Response may already be committed, ignore
68+
}
69+
5870
return result;
5971
}
6072
}

src/main/java/org/ohdsi/webapi/shiro/filters/SendTokenInHeaderFilter.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ protected boolean preHandle(ServletRequest request, ServletResponse response) {
4444
httpResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
4545
httpResponse.setStatus(HttpServletResponse.SC_OK);
4646

47-
try (final PrintWriter responseWriter = response.getWriter()) {
47+
try {
48+
final PrintWriter responseWriter = response.getWriter();
4849
responseWriter.print(objectMapper.writeValueAsString(permissions));
50+
httpResponse.flushBuffer(); // Commit the response
4951
} catch (IOException e) {
5052
LOGGER.error(ERROR_WRITING_PERMISSIONS_TO_RESPONSE_LOG, e);
5153
}

src/main/java/org/ohdsi/webapi/shiro/filters/auth/AbstractLdapAuthFilter.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,23 @@
1818
*/
1919
package org.ohdsi.webapi.shiro.filters.auth;
2020

21+
import java.io.IOException;
2122
import jakarta.servlet.ServletRequest;
2223
import jakarta.servlet.ServletResponse;
24+
import jakarta.servlet.http.HttpServletResponse;
2325

2426
import org.apache.shiro.authc.AuthenticationException;
27+
import org.slf4j.Logger;
28+
import org.slf4j.LoggerFactory;
2529
import org.apache.shiro.authc.AuthenticationToken;
2630
import org.apache.shiro.authc.UsernamePasswordToken;
2731
import org.ohdsi.webapi.shiro.filters.AuthenticatingPropagationFilter;
2832
import org.springframework.context.ApplicationEventPublisher;
2933

3034
public abstract class AbstractLdapAuthFilter<T extends UsernamePasswordToken> extends AuthenticatingPropagationFilter {
35+
36+
private static final Logger log = LoggerFactory.getLogger(AbstractLdapAuthFilter.class);
37+
3138
protected AbstractLdapAuthFilter(ApplicationEventPublisher eventPublisher) {
3239
super(eventPublisher);
3340
}
@@ -58,8 +65,23 @@ protected boolean onAccessDenied(ServletRequest request, ServletResponse respons
5865

5966
if (request.getParameter("login") != null) {
6067
loggedIn = executeLogin(request, response);
68+
} else {
69+
// No credentials provided - write error response and commit
70+
writeUnauthorizedResponse(response, "Missing credentials");
6171
}
6272

6373
return loggedIn;
6474
}
75+
76+
private void writeUnauthorizedResponse(ServletResponse response, String message) {
77+
try {
78+
HttpServletResponse httpResponse = (HttpServletResponse) response;
79+
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
80+
httpResponse.setContentType("application/json");
81+
httpResponse.getWriter().write("{\"error\":\"" + message + "\"}");
82+
httpResponse.flushBuffer(); // Commit the response
83+
} catch (IOException e) {
84+
log.error("Failed to write unauthorized response", e);
85+
}
86+
}
6587
}

src/main/java/org/ohdsi/webapi/shiro/filters/auth/AtlasJwtAuthFilter.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.jsonwebtoken.Claims;
44
import io.jsonwebtoken.JwtException;
5+
import java.io.IOException;
56
import jakarta.servlet.ServletRequest;
67
import jakarta.servlet.ServletResponse;
78
import jakarta.servlet.http.HttpServletResponse;
@@ -46,6 +47,14 @@ protected boolean onAccessDenied(ServletRequest request, ServletResponse respons
4647
if (!loggedIn) {
4748
HttpServletResponse httpResponse = ServletBridge.toHttp(response);
4849
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
50+
// Write response body and commit to prevent Spring MVC from processing
51+
try {
52+
httpResponse.setContentType("application/json");
53+
httpResponse.getWriter().write("{\"error\":\"Invalid or missing JWT token\"}");
54+
httpResponse.flushBuffer(); // Commit the response
55+
} catch (IOException e) {
56+
logger.error("Failed to write unauthorized response", e);
57+
}
4958
}
5059

5160
return loggedIn;

src/main/java/org/ohdsi/webapi/shiro/filters/auth/JdbcAuthFilter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424

2525
import jakarta.servlet.ServletRequest;
2626
import jakarta.servlet.ServletResponse;
27+
import jakarta.servlet.http.HttpServletResponse;
2728

2829
import org.ohdsi.webapi.shiro.filters.AuthenticatingPropagationFilter;
30+
import java.io.IOException;
2931
import org.slf4j.Logger;
3032
import org.slf4j.LoggerFactory;
3133
import org.springframework.context.ApplicationEventPublisher;
@@ -57,8 +59,23 @@ protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse
5759

5860
if (servletRequest.getParameter("login") != null) {
5961
loggedIn = executeLogin(servletRequest, servletResponse);
62+
} else {
63+
// No credentials provided - write error response and commit
64+
writeUnauthorizedResponse(servletResponse, "Missing credentials");
6065
}
6166
return loggedIn;
6267
}
6368

69+
private void writeUnauthorizedResponse(ServletResponse response, String message) {
70+
try {
71+
HttpServletResponse httpResponse = (HttpServletResponse) response;
72+
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
73+
httpResponse.setContentType("application/json");
74+
httpResponse.getWriter().write("{\"error\":\"" + message + "\"}");
75+
httpResponse.flushBuffer(); // Commit the response
76+
} catch (IOException e) {
77+
log.error("Failed to write unauthorized response", e);
78+
}
79+
}
80+
6481
}

src/main/java/org/ohdsi/webapi/shiro/filters/auth/KerberosAuthFilter.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,24 @@
1818
*/
1919
package org.ohdsi.webapi.shiro.filters.auth;
2020

21+
import java.io.IOException;
2122
import java.util.Base64;
2223
import jakarta.servlet.ServletRequest;
2324
import jakarta.servlet.ServletResponse;
2425
import jakarta.servlet.http.HttpServletRequest;
2526
import jakarta.servlet.http.HttpServletResponse;
2627
import org.ohdsi.webapi.shiro.ServletBridge;
28+
import org.slf4j.Logger;
29+
import org.slf4j.LoggerFactory;
2730
import org.apache.shiro.authc.AuthenticationException;
2831
import org.apache.shiro.authc.AuthenticationToken;
2932
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
3033
import org.ohdsi.webapi.shiro.tokens.SpnegoToken;
3134

3235
public class KerberosAuthFilter extends AuthenticatingFilter {
3336

37+
private static final Logger log = LoggerFactory.getLogger(KerberosAuthFilter.class);
38+
3439
private String getAuthHeader(ServletRequest servletRequest) {
3540

3641
HttpServletRequest request = ServletBridge.toHttp(servletRequest);
@@ -69,6 +74,14 @@ protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse
6974
HttpServletResponse response = ServletBridge.toHttp(servletResponse);
7075
response.addHeader("WWW-Authenticate", "Negotiate");
7176
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
77+
// Write response body and commit to prevent Spring MVC from processing
78+
try {
79+
response.setContentType("application/json");
80+
response.getWriter().write("{\"error\":\"Kerberos authentication required\"}");
81+
response.flushBuffer(); // Commit the response
82+
} catch (IOException e) {
83+
log.error("Failed to write unauthorized response", e);
84+
}
7285
}
7386

7487
return loggedIn;

0 commit comments

Comments
 (0)