CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-eclipse-jetty--jetty-security

Security framework for Eclipse Jetty providing authentication, authorization, and user identity management.

Pending
Overview
Eval results
Files

user-identity.mddocs/

User Identity Management

User identity management in Jetty Security encompasses user principals, roles, identity services, and the association of security contexts with execution threads.

Core Identity Interfaces

UserIdentity

Central interface representing an authenticated user:

public interface UserIdentity {
    
    // Core identity access
    Subject getSubject();
    Principal getUserPrincipal();
    
    // Role checking
    boolean isUserInRole(String role);
    
    // Static factory methods
    static UserIdentity from(Subject subject, Principal userPrincipal, String... roles);
}

UserIdentity Implementation

public class UserIdentityExample {
    
    public void createUserIdentity() {
        
        // Create subject for user
        Subject subject = new Subject();
        
        // Create user principal
        UserPrincipal userPrincipal = new UserPrincipal("john", 
            Credential.getCredential("password123"));
        
        // Create user identity with roles
        UserIdentity identity = UserIdentity.from(subject, userPrincipal, 
            "user", "developer", "api-access");
        
        // Role checking
        boolean canAccess = identity.isUserInRole("developer");
        boolean isAdmin = identity.isUserInRole("admin");
        
        // Access underlying components
        Subject userSubject = identity.getSubject();
        Principal principal = identity.getUserPrincipal();
    }
}

Identity Service

IdentityService Interface

Manages associations between user identities and execution threads:

public interface IdentityService {
    
    // Thread association
    Association associate(UserIdentity user, RunAsToken runAsToken);
    
    // User lifecycle
    void onLogout(UserIdentity user);
    
    // Factory methods
    UserIdentity newUserIdentity(Subject subject, Principal userPrincipal, String[] roles);
    RunAsToken newRunAsToken(String roleName);
    
    // System user access
    UserIdentity getSystemUserIdentity();
    
    // Nested interfaces
    interface Association extends AutoCloseable {
        @Override
        void close();
    }
    
    interface RunAsToken {
        // Opaque token for run-as operations
    }
}

DefaultIdentityService

Default implementation of IdentityService:

public class DefaultIdentityService implements IdentityService {
    
    @Override
    public Association associate(UserIdentity user, RunAsToken runAsToken) {
        
        // Associate user identity with current thread
        return new AssociationImpl(user, runAsToken);
    }
    
    @Override
    public void onLogout(UserIdentity user) {
        // Clear any thread associations for this user
        clearThreadAssociations(user);
    }
    
    @Override
    public UserIdentity newUserIdentity(Subject subject, Principal userPrincipal, String[] roles) {
        
        // Create DefaultUserIdentity with provided components
        return new DefaultUserIdentity(subject, userPrincipal, roles);
    }
    
    @Override
    public RunAsToken newRunAsToken(String roleName) {
        
        // Create run-as token for the specified role
        return new RoleRunAsToken(roleName);
    }
    
    @Override
    public UserIdentity getSystemUserIdentity() {
        
        // Return null - no system user by default
        return null;
    }
    
    // Static utility methods
    public static boolean isRoleAssociated(String role);
    
    // Private implementation classes
    private static class AssociationImpl implements Association {
        // Implementation details
    }
}

User Principals

UserPrincipal

Represents a user with authentication capabilities:

public class UserPrincipal implements Principal {
    
    // Constructor
    public UserPrincipal(String name, Credential credential);
    
    @Override
    public String getName();
    
    // Authentication methods
    public boolean authenticate(Object credentials);
    public boolean authenticate(Credential c);
    public boolean authenticate(UserPrincipal u);
    
    // Subject integration
    public void configureSubject(Subject subject);
    public void deconfigureSubject(Subject subject);
    
    // Utility methods
    @Override
    public boolean equals(Object other);
    
    @Override
    public int hashCode();
    
    @Override
    public String toString();
}

UserPrincipal Usage

public class UserPrincipalExample {
    
    public void createAndUseUserPrincipal() {
        
        // Create user principal with password
        Credential password = Credential.getCredential("secretPassword");
        UserPrincipal user = new UserPrincipal("alice", password);
        
        // Test authentication
        boolean validPassword = user.authenticate("secretPassword");
        boolean invalidPassword = user.authenticate("wrongPassword");
        
        // Test credential object authentication
        Credential testCred = Credential.getCredential("secretPassword");
        boolean credValid = user.authenticate(testCred);
        
        // Test user-to-user authentication
        UserPrincipal sameUser = new UserPrincipal("alice", password);
        boolean sameUserAuth = user.authenticate(sameUser);
        
        // Configure subject
        Subject subject = new Subject();
        user.configureSubject(subject);
        
        // Subject now contains the user principal
        Set<Principal> principals = subject.getPrincipals();
        boolean containsUser = principals.contains(user);
    }
    
    public void createUserWithHashedPassword() {
        
        // Create user with MD5 hashed password
        Credential md5Cred = Credential.getCredential("MD5:5d41402abc4b2a76b9719d911017c592");
        UserPrincipal user = new UserPrincipal("bob", md5Cred);
        
        // Authenticate with plain text (will be hashed for comparison)
        boolean authenticated = user.authenticate("hello");
        
        // Create user with crypt password
        Credential cryptCred = Credential.getCredential("CRYPT:$1$salt$hash");
        UserPrincipal cryptUser = new UserPrincipal("charlie", cryptCred);
    }
}

Credential Management

Credential

Utility class for secure credential handling and password storage:

public abstract class Credential {
    
    // Factory method for creating credentials
    public static Credential getCredential(String credential);
    
    // Credential verification
    public abstract boolean check(Object credentials);
    
    // Common credential types supported:
    // - Plain text passwords: "password123"
    // - MD5 hashed: "MD5:5d41402abc4b2a76b9719d911017c592"
    // - Crypt hashed: "CRYPT:$1$salt$hash"
    // - BCrypt hashed: "$2y$10$..."
}

The Credential class provides secure password storage and verification. It automatically detects the credential type and applies appropriate hashing and verification algorithms.

Role Principals

RolePrincipal

Represents a role that can be associated with subjects:

public class RolePrincipal implements Principal {
    
    // Constructor
    public RolePrincipal(String name);
    
    @Override
    public String getName();
    
    // Subject integration
    public void configureSubject(Subject subject);
    public void deconfigureSubject(Subject subject);
    
    @Override
    public boolean equals(Object other);
    
    @Override
    public int hashCode();
    
    @Override
    public String toString();
}

Role Management

public class RoleManagementExample {
    
    public void manageUserRoles() {
        
        // Create user
        UserPrincipal user = new UserPrincipal("manager", 
            Credential.getCredential("password"));
        
        // Create roles
        RolePrincipal userRole = new RolePrincipal("user");
        RolePrincipal managerRole = new RolePrincipal("manager");
        RolePrincipal adminRole = new RolePrincipal("admin");
        
        // Create subject and configure
        Subject subject = new Subject();
        user.configureSubject(subject);
        userRole.configureSubject(subject);
        managerRole.configureSubject(subject);
        
        // Create user identity
        IdentityService identityService = new DefaultIdentityService();
        UserIdentity identity = identityService.newUserIdentity(subject, user, 
            new String[]{"user", "manager"});
        
        // Check roles
        boolean isUser = identity.isUserInRole("user");        // true
        boolean isManager = identity.isUserInRole("manager");  // true
        boolean isAdmin = identity.isUserInRole("admin");      // false
    }
    
    public void createHierarchicalRoles() {
        
        // Create role hierarchy
        Map<String, Set<String>> roleHierarchy = new HashMap<>();
        roleHierarchy.put("admin", Set.of("admin", "manager", "user"));
        roleHierarchy.put("manager", Set.of("manager", "user"));
        roleHierarchy.put("user", Set.of("user"));
        
        // Custom identity service with role hierarchy
        IdentityService hierarchicalService = new HierarchicalIdentityService(roleHierarchy);
        
        // User with only "manager" role
        UserPrincipal user = new UserPrincipal("supervisor", 
            Credential.getCredential("password"));
        Subject subject = new Subject();
        user.configureSubject(subject);
        
        UserIdentity identity = hierarchicalService.newUserIdentity(subject, user, 
            new String[]{"manager"});
        
        // Manager can act as user due to hierarchy
        boolean canActAsUser = identity.isUserInRole("user");     // true
        boolean canActAsManager = identity.isUserInRole("manager"); // true
        boolean canActAsAdmin = identity.isUserInRole("admin");   // false
    }
    
    // Custom identity service with role hierarchy
    private static class HierarchicalIdentityService extends DefaultIdentityService {
        private final Map<String, Set<String>> roleHierarchy;
        
        public HierarchicalIdentityService(Map<String, Set<String>> roleHierarchy) {
            this.roleHierarchy = roleHierarchy;
        }
        
        @Override
        public UserIdentity newUserIdentity(Subject subject, Principal userPrincipal, String[] roles) {
            
            // Expand roles based on hierarchy
            Set<String> expandedRoles = new HashSet<>();
            for (String role : roles) {
                Set<String> impliedRoles = roleHierarchy.get(role);
                if (impliedRoles != null) {
                    expandedRoles.addAll(impliedRoles);
                } else {
                    expandedRoles.add(role);
                }
            }
            
            return super.newUserIdentity(subject, userPrincipal, 
                expandedRoles.toArray(new String[0]));
        }
    }
}

Identity Association and RunAs

Thread Association

public class ThreadAssociationExample {
    
    public void demonstrateThreadAssociation() {
        
        IdentityService identityService = new DefaultIdentityService();
        
        // Create user identity
        UserPrincipal user = new UserPrincipal("worker", 
            Credential.getCredential("password"));
        Subject subject = new Subject();
        UserIdentity identity = identityService.newUserIdentity(subject, user, 
            new String[]{"worker", "api-user"});
        
        // Associate with current thread
        try (IdentityService.Association association = 
                identityService.associate(identity, null)) {
            
            // Code in this block runs with user identity
            performSecureOperation();
            
            // Association is automatically cleaned up when block exits
        }
        
        // User identity is no longer associated with thread
    }
    
    public void demonstrateRunAs() {
        
        IdentityService identityService = new DefaultIdentityService();
        
        // Create regular user
        UserIdentity regularUser = createUserIdentity("john", "user");
        
        // Create run-as token for admin role
        IdentityService.RunAsToken adminToken = identityService.newRunAsToken("admin");
        
        // Associate regular user but run as admin
        try (IdentityService.Association association = 
                identityService.associate(regularUser, adminToken)) {
            
            // Check if admin role is associated with thread
            boolean isAdmin = DefaultIdentityService.isRoleAssociated("admin");
            
            // Perform admin operations
            performAdminOperation();
        }
    }
    
    public void demonstrateNestedAssociations() {
        
        IdentityService identityService = new DefaultIdentityService();
        
        UserIdentity user1 = createUserIdentity("alice", "user");
        UserIdentity user2 = createUserIdentity("bob", "manager");
        
        try (IdentityService.Association outer = identityService.associate(user1, null)) {
            
            // Outer context: alice as user
            performUserOperation();
            
            try (IdentityService.Association inner = identityService.associate(user2, null)) {
                
                // Inner context: bob as manager
                performManagerOperation();
                
            } // bob's association ends
            
            // Back to alice's context
            performUserOperation();
            
        } // alice's association ends
    }
    
    private UserIdentity createUserIdentity(String name, String role) {
        IdentityService identityService = new DefaultIdentityService();
        UserPrincipal user = new UserPrincipal(name, Credential.getCredential("password"));
        Subject subject = new Subject();
        return identityService.newUserIdentity(subject, user, new String[]{role});
    }
    
    private void performSecureOperation() {
        // Implementation
    }
    
    private void performAdminOperation() {
        // Implementation requiring admin privileges
    }
    
    private void performUserOperation() {
        // Implementation for regular users
    }
    
    private void performManagerOperation() {
        // Implementation for managers
    }
}

RoleDelegateUserIdentity

Delegated Role Checking

public class RoleDelegateUserIdentity implements UserIdentity {
    
    private final UserIdentity delegate;
    private final Subject subject;
    private final Principal userPrincipal;
    
    // Constructor
    public RoleDelegateUserIdentity(UserIdentity delegate, Subject subject, Principal userPrincipal);
    
    @Override
    public Subject getSubject() {
        return subject;
    }
    
    @Override
    public Principal getUserPrincipal() {
        return userPrincipal;
    }
    
    @Override
    public boolean isUserInRole(String role) {
        
        // Delegate role checking to another UserIdentity
        return delegate.isUserInRole(role);
    }
}

Role Delegation Usage

public class RoleDelegationExample {
    
    public void demonstrateRoleDelegation() {
        
        // Create primary user identity with roles
        UserPrincipal primaryUser = new UserPrincipal("primary", 
            Credential.getCredential("password"));
        Subject primarySubject = new Subject();
        IdentityService identityService = new DefaultIdentityService();
        UserIdentity primaryIdentity = identityService.newUserIdentity(primarySubject, 
            primaryUser, new String[]{"admin", "manager", "user"});
        
        // Create secondary user with different principal but same role checking
        UserPrincipal secondaryUser = new UserPrincipal("secondary", 
            Credential.getCredential("password"));
        Subject secondarySubject = new Subject();
        
        // Create role delegate identity
        RoleDelegateUserIdentity delegateIdentity = new RoleDelegateUserIdentity(
            primaryIdentity,     // Delegate role checking to primary
            secondarySubject,    // But use secondary subject
            secondaryUser        // And secondary principal
        );
        
        // Identity has secondary user's principal
        String name = delegateIdentity.getUserPrincipal().getName(); // "secondary"
        
        // But role checking is delegated to primary user
        boolean isAdmin = delegateIdentity.isUserInRole("admin");    // true (from primary)
        boolean isManager = delegateIdentity.isUserInRole("manager"); // true (from primary)
    }
    
    public void createServiceAccountWithUserRoles() {
        
        // Create service account identity
        UserPrincipal serviceAccount = new UserPrincipal("api-service", 
            Credential.getCredential("service-key"));
        Subject serviceSubject = new Subject();
        
        // Get user identity to delegate roles from
        UserIdentity userIdentity = getCurrentUserIdentity(); // Assume this returns current user
        
        // Create service account that inherits user's roles
        RoleDelegateUserIdentity serviceIdentity = new RoleDelegateUserIdentity(
            userIdentity,        // Delegate to current user's roles
            serviceSubject,      // Service account subject
            serviceAccount       // Service account principal
        );
        
        // Service account can perform operations with user's roles
        try (IdentityService.Association association = 
                new DefaultIdentityService().associate(serviceIdentity, null)) {
            
            performServiceOperation();
        }
    }
    
    private UserIdentity getCurrentUserIdentity() {
        // Implementation to get current user identity
        return null;
    }
    
    private void performServiceOperation() {
        // Service operation that requires user's roles
    }
}

User Store Integration

UserStore Interface

public interface UserStore {
    
    // User retrieval
    UserPrincipal getUserPrincipal(String username);
    List<RolePrincipal> getRolePrincipals(String username);
    
    // User management (optional)
    default void addUser(String username, Credential credential, String[] roles) {
        throw new UnsupportedOperationException();
    }
    
    default void removeUser(String username) {
        throw new UnsupportedOperationException();
    }
    
    default void updateUser(String username, Credential credential, String[] roles) {
        throw new UnsupportedOperationException();
    }
}

Custom Identity Management

public class CustomIdentityManager {
    
    private final IdentityService identityService;
    private final UserStore userStore;
    
    public CustomIdentityManager(IdentityService identityService, UserStore userStore) {
        this.identityService = identityService;
        this.userStore = userStore;
    }
    
    public UserIdentity authenticateUser(String username, String password) {
        
        // Get user principal from store
        UserPrincipal userPrincipal = userStore.getUserPrincipal(username);
        if (userPrincipal == null) {
            return null;
        }
        
        // Authenticate credentials
        if (!userPrincipal.authenticate(password)) {
            return null;
        }
        
        // Get user roles
        List<RolePrincipal> rolePrincipals = userStore.getRolePrincipals(username);
        String[] roles = rolePrincipals.stream()
            .map(RolePrincipal::getName)
            .toArray(String[]::new);
        
        // Create subject and configure
        Subject subject = new Subject();
        userPrincipal.configureSubject(subject);
        for (RolePrincipal role : rolePrincipals) {
            role.configureSubject(subject);
        }
        
        // Create user identity
        return identityService.newUserIdentity(subject, userPrincipal, roles);
    }
    
    public boolean addUser(String username, String password, String... roles) {
        
        try {
            // Create credential
            Credential credential = Credential.getCredential(password);
            
            // Add to user store
            userStore.addUser(username, credential, roles);
            
            return true;
            
        } catch (Exception e) {
            logger.error("Failed to add user: " + username, e);
            return false;
        }
    }
    
    public boolean updateUserRoles(String username, String... newRoles) {
        
        try {
            // Get existing user
            UserPrincipal existing = userStore.getUserPrincipal(username);
            if (existing == null) {
                return false;
            }
            
            // Update with new roles (keeping same credential)
            userStore.updateUser(username, existing.getCredential(), newRoles);
            
            return true;
            
        } catch (Exception e) {
            logger.error("Failed to update user roles: " + username, e);
            return false;
        }
    }
    
    public void logoutUser(UserIdentity user) {
        
        // Notify identity service of logout
        identityService.onLogout(user);
        
        // Additional cleanup if needed
        performLogoutCleanup(user);
    }
    
    private void performLogoutCleanup(UserIdentity user) {
        // Custom logout cleanup logic
    }
}

Advanced Identity Patterns

Composite Identity Service

public class CompositeIdentityService implements IdentityService {
    
    private final List<IdentityService> delegates;
    
    public CompositeIdentityService(IdentityService... delegates) {
        this.delegates = Arrays.asList(delegates);
    }
    
    @Override
    public Association associate(UserIdentity user, RunAsToken runAsToken) {
        
        // Associate with all delegates
        List<Association> associations = delegates.stream()
            .map(service -> service.associate(user, runAsToken))
            .collect(Collectors.toList());
        
        return new CompositeAssociation(associations);
    }
    
    @Override
    public UserIdentity newUserIdentity(Subject subject, Principal userPrincipal, String[] roles) {
        
        // Use first delegate for identity creation
        return delegates.get(0).newUserIdentity(subject, userPrincipal, roles);
    }
    
    @Override
    public void onLogout(UserIdentity user) {
        
        // Notify all delegates
        delegates.forEach(service -> service.onLogout(user));
    }
    
    @Override
    public RunAsToken newRunAsToken(String roleName) {
        
        // Create composite run-as token
        List<RunAsToken> tokens = delegates.stream()
            .map(service -> service.newRunAsToken(roleName))
            .collect(Collectors.toList());
        
        return new CompositeRunAsToken(tokens);
    }
    
    @Override
    public UserIdentity getSystemUserIdentity() {
        
        // Return system user from first delegate that has one
        return delegates.stream()
            .map(IdentityService::getSystemUserIdentity)
            .filter(Objects::nonNull)
            .findFirst()
            .orElse(null);
    }
    
    private static class CompositeAssociation implements Association {
        private final List<Association> associations;
        
        public CompositeAssociation(List<Association> associations) {
            this.associations = associations;
        }
        
        @Override
        public void close() {
            associations.forEach(Association::close);
        }
    }
    
    private static class CompositeRunAsToken implements RunAsToken {
        private final List<RunAsToken> tokens;
        
        public CompositeRunAsToken(List<RunAsToken> tokens) {
            this.tokens = tokens;
        }
    }
}

Caching Identity Service

public class CachingIdentityService implements IdentityService {
    
    private final IdentityService delegate;
    private final Cache<String, UserIdentity> identityCache;
    
    public CachingIdentityService(IdentityService delegate, Duration cacheTTL) {
        this.delegate = delegate;
        this.identityCache = Caffeine.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(cacheTTL)
            .build();
    }
    
    @Override
    public UserIdentity newUserIdentity(Subject subject, Principal userPrincipal, String[] roles) {
        
        String cacheKey = buildCacheKey(userPrincipal, roles);
        
        return identityCache.get(cacheKey, key -> 
            delegate.newUserIdentity(subject, userPrincipal, roles));
    }
    
    @Override
    public Association associate(UserIdentity user, RunAsToken runAsToken) {
        return delegate.associate(user, runAsToken);
    }
    
    @Override
    public void onLogout(UserIdentity user) {
        
        // Invalidate cache entries for this user
        String username = user.getUserPrincipal().getName();
        identityCache.asMap().entrySet().removeIf(entry -> 
            entry.getKey().startsWith(username + ":"));
        
        delegate.onLogout(user);
    }
    
    @Override
    public RunAsToken newRunAsToken(String roleName) {
        return delegate.newRunAsToken(roleName);
    }
    
    @Override
    public UserIdentity getSystemUserIdentity() {
        return delegate.getSystemUserIdentity();
    }
    
    private String buildCacheKey(Principal userPrincipal, String[] roles) {
        return userPrincipal.getName() + ":" + Arrays.toString(roles);
    }
}

Best Practices

Identity Management Best Practices

public class IdentityBestPractices {
    
    public void configureProductionIdentityService() {
        
        // Use caching for performance
        IdentityService baseService = new DefaultIdentityService();
        IdentityService cachingService = new CachingIdentityService(baseService, Duration.ofMinutes(15));
        
        // Create secure user store
        UserStore secureStore = createSecureUserStore();
        
        // Configure identity manager
        CustomIdentityManager identityManager = new CustomIdentityManager(cachingService, secureStore);
        
        // Use with security handler
        SecurityHandler security = new SecurityHandler.PathMapped();
        security.setIdentityService(cachingService);
    }
    
    private UserStore createSecureUserStore() {
        
        // Use property store with secure defaults
        PropertyUserStore store = new PropertyUserStore();
        store.setConfig(Resource.newResource("secure-users.properties"));
        store.setReloadInterval(300); // 5 minutes
        
        return store;
    }
    
    public void demonstrateSecureIdentityCreation() {
        
        IdentityService identityService = new DefaultIdentityService();
        
        // Create user with strong password
        String strongPassword = generateStrongPassword();
        UserPrincipal user = new UserPrincipal("alice", 
            Credential.getCredential(strongPassword));
        
        // Create subject with proper configuration
        Subject subject = new Subject();
        user.configureSubject(subject);
        
        // Add role principals to subject
        RolePrincipal userRole = new RolePrincipal("user");
        RolePrincipal appRole = new RolePrincipal("app-user");
        userRole.configureSubject(subject);
        appRole.configureSubject(subject);
        
        // Create identity with minimal necessary roles
        UserIdentity identity = identityService.newUserIdentity(subject, user, 
            new String[]{"user", "app-user"});
        
        // Always use try-with-resources for associations
        try (IdentityService.Association association = 
                identityService.associate(identity, null)) {
            
            performUserOperation();
        }
    }
    
    private String generateStrongPassword() {
        // Implementation to generate cryptographically strong password
        return "StrongPassword123!";
    }
    
    private void performUserOperation() {
        // Implementation
    }
}

User Identity Management provides comprehensive support for user principals, roles, thread associations, and secure identity contexts, enabling fine-grained access control and secure execution environments.

Install with Tessl CLI

npx tessl i tessl/maven-org-eclipse-jetty--jetty-security

docs

authentication.md

index.md

jaas.md

login-services.md

security-framework.md

user-identity.md

tile.json