CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-co-cask-cdap--cdap-security-spi

Service Provider Interface for CDAP's security and authorization framework enabling pluggable authorization mechanisms.

Pending
Overview
Eval results
Files

authentication.mddocs/

Authentication Context

The authentication context components provide thread-local user identification and authentication details for authorization requests in the CDAP security framework.

Core Authentication Interfaces

AuthenticationContext

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();
}

Security Request Context

SecurityRequestContext

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();
}

Usage Examples

Setting Request Context

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);
  }
}

Using Authentication Context in Authorizers

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);
    }
  }
}

Thread-Safe Context Management

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);
  }
}

Integration with Web Filters

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);
  }
}

Custom Authentication Context Implementation

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;
  }
}

Integration Patterns

With Spring Security

@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);
  }
}

With OAuth/JWT

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

docs

authentication.md

authorization.md

exceptions.md

index.md

tile.json