Service Provider Interface for CDAP's security and authorization framework enabling pluggable authorization mechanisms.
—
The authentication context components provide thread-local user identification and authentication details for authorization requests in the CDAP security framework.
Interface for determining authentication details in authorization contexts.
interface AuthenticationContext {
/**
* Get the Principal making the current authorization request.
*
* @return the Principal making the authorization request
*/
Principal getPrincipal();
}Thread-local context for maintaining authenticated user information across request processing.
final class SecurityRequestContext {
/**
* Get the user ID set on the current thread.
*
* @return the user ID or null if not set
*/
@Nullable
static String getUserId();
/**
* Get the user IP address set on the current thread.
*
* @return the user IP or null if not set
*/
@Nullable
static String getUserIP();
/**
* Set the user ID on the current thread.
*
* @param userIdParam the user ID to set
*/
static void setUserId(String userIdParam);
/**
* Set the user IP address on the current thread.
*
* @param userIPParam the user IP to set
*/
static void setUserIP(String userIPParam);
/**
* Create a Principal for the user set on the current thread.
*
* @return Principal with USER type for the current thread's user ID
*/
static Principal toPrincipal();
}import co.cask.cdap.security.spi.authentication.SecurityRequestContext;
// At the beginning of request processing
public void handleRequest(HttpServletRequest request) {
// Extract user information from request (e.g., JWT token, session, etc.)
String userId = extractUserFromToken(request.getHeader("Authorization"));
String clientIP = request.getRemoteAddr();
// Set context for current thread
SecurityRequestContext.setUserId(userId);
SecurityRequestContext.setUserIP(clientIP);
try {
// Process request - authorization checks can now access user context
processBusinessLogic();
} finally {
// Clean up thread-local variables to prevent memory leaks
SecurityRequestContext.setUserId(null);
SecurityRequestContext.setUserIP(null);
}
}public class CustomAuthorizer extends AbstractAuthorizer {
@Override
public void initialize(AuthorizationContext context) throws Exception {
// AuthorizationContext extends AuthenticationContext
// You can get the current principal during initialization if needed
Principal currentPrincipal = context.getPrincipal();
// Initialize your authorization backend
setupAuthorizationBackend();
}
@Override
public void enforce(EntityId entity, Principal principal, Set<Action> actions)
throws Exception {
// The principal parameter contains the user information
// Additional context can be obtained from SecurityRequestContext if needed
String userIP = SecurityRequestContext.getUserIP();
// Log authorization attempt with IP for auditing
auditLog.info("Authorization check for user {} from IP {} on entity {} for actions {}",
principal.getName(), userIP, entity, actions);
// Perform authorization logic
if (!isAuthorized(entity, principal, actions)) {
throw new UnauthorizedException(principal, actions, entity);
}
}
}public class RequestProcessor {
public void processMultipleRequests(List<UserRequest> requests) {
// Process requests in parallel, each with its own thread context
requests.parallelStream().forEach(this::processRequest);
}
private void processRequest(UserRequest request) {
// Each thread gets its own SecurityRequestContext
SecurityRequestContext.setUserId(request.getUserId());
SecurityRequestContext.setUserIP(request.getClientIP());
try {
// Process request - context is isolated per thread
Principal principal = SecurityRequestContext.toPrincipal();
authorizer.enforce(request.getTargetEntity(), principal, request.getActions());
// Perform business logic
executeBusinessLogic(request);
} catch (UnauthorizedException e) {
handleUnauthorizedAccess(request, e);
} finally {
// Clean up to prevent memory leaks
clearSecurityContext();
}
}
private void clearSecurityContext() {
SecurityRequestContext.setUserId(null);
SecurityRequestContext.setUserIP(null);
}
}public class SecurityContextFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
try {
// Extract authentication information
String authHeader = httpRequest.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
UserInfo userInfo = validateToken(token);
// Set security context for downstream processing
SecurityRequestContext.setUserId(userInfo.getUserId());
SecurityRequestContext.setUserIP(httpRequest.getRemoteAddr());
}
// Continue with request processing
chain.doFilter(request, response);
} finally {
// Always clean up thread-local state
SecurityRequestContext.setUserId(null);
SecurityRequestContext.setUserIP(null);
}
}
private UserInfo validateToken(String token) {
// Token validation logic
return jwtValidator.validate(token);
}
}public class EnhancedAuthenticationContext implements AuthenticationContext {
@Override
public Principal getPrincipal() {
// Use SecurityRequestContext to build Principal
String userId = SecurityRequestContext.getUserId();
if (userId == null) {
throw new IllegalStateException("No user context available");
}
return new Principal(userId, Principal.PrincipalType.USER);
}
/**
* Additional method for enhanced context information.
*/
public String getClientIP() {
return SecurityRequestContext.getUserIP();
}
/**
* Check if the current request has authentication context.
*/
public boolean hasAuthenticationContext() {
return SecurityRequestContext.getUserId() != null;
}
}@Component
public class SpringSecurityContextAdapter {
public void setSecurityContextFromSpring() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.isAuthenticated()) {
String username = auth.getName();
SecurityRequestContext.setUserId(username);
// Extract IP from request attributes if available
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
if (attributes instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes) attributes).getRequest();
SecurityRequestContext.setUserIP(request.getRemoteAddr());
}
}
}
@PreDestroy
public void clearContext() {
SecurityRequestContext.setUserId(null);
SecurityRequestContext.setUserIP(null);
}
}public class JwtAuthenticationHandler {
private final JwtDecoder jwtDecoder;
public void handleJwtAuthentication(String jwtToken, String clientIP) {
try {
Jwt jwt = jwtDecoder.decode(jwtToken);
String userId = jwt.getClaimAsString("sub");
String email = jwt.getClaimAsString("email");
// Use email as user ID if sub is not human-readable
SecurityRequestContext.setUserId(email != null ? email : userId);
SecurityRequestContext.setUserIP(clientIP);
} catch (JwtException e) {
throw new UnauthorizedException("Invalid JWT token");
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-co-cask-cdap--cdap-security-spi