CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-eclipse-jetty--jetty-plus

JNDI support for Eclipse Jetty web server including dependency injection, lifecycle management, JNDI resource binding, and database-backed security services

Pending
Overview
Eval results
Files

security-services.mddocs/

Database Security Services

Database-backed authentication and authorization using JNDI DataSources with configurable user/role schema and full Jetty security integration for enterprise applications.

Capabilities

Database Login Service

Comprehensive database-backed login service that obtains user credentials and role information via JNDI DataSource with configurable table schema and SQL generation.

class DataSourceLoginService extends AbstractLoginService {
    // Constructors
    DataSourceLoginService();
    DataSourceLoginService(String name);
    DataSourceLoginService(String name, IdentityService identityService);
    
    // JNDI DataSource configuration
    void setJndiName(String jndi);
    String getJndiName();
    
    // Server integration
    void setServer(Server server);
    Server getServer();
    
    // Database schema management
    void setCreateTables(boolean createTables);
    boolean getCreateTables();
    
    // User table configuration
    void setUserTableName(String name);
    String getUserTableName();
    void setUserTableKey(String tableKey);
    String getUserTableKey();
    void setUserTableUserField(String tableUserField);
    String getUserTableUserField();
    void setUserTablePasswordField(String tablePasswordField);
    String getUserTablePasswordField();
    
    // Role table configuration
    void setRoleTableName(String tableName);
    String getRoleTableName();
    void setRoleTableKey(String tableKey);
    String getRoleTableKey();
    void setRoleTableRoleField(String tableRoleField);
    String getRoleTableRoleField();
    
    // User-role junction table configuration
    void setUserRoleTableName(String roleTableName);
    String getUserRoleTableName();
    void setUserRoleTableUserKey(String roleTableUserKey);
    String getUserRoleTableUserKey();
    void setUserRoleTableRoleKey(String roleTableRoleKey);
    String getUserRoleTableRoleKey();
    
    // Core authentication methods
    UserPrincipal loadUserInfo(String username);
    List<RolePrincipal> loadRoleInfo(UserPrincipal user);
    
    // Database initialization
    void initDb() throws NamingException, SQLException;
}

Database User Principal

Enhanced user principal that includes database key information for efficient role lookups and database operations.

static class DBUserPrincipal extends UserPrincipal {
    // Get database primary key for this user
    int getKey();
}

Configuration Patterns

Basic Configuration

// Create and configure basic database login service
DataSourceLoginService loginService = new DataSourceLoginService("MyRealm");

// Configure JNDI DataSource
loginService.setJndiName("jdbc/SecurityDB");

// Use default table schema (users, roles, user_roles tables)
loginService.setCreateTables(false); // Don't auto-create tables

// Add to server
Server server = new Server();
server.addBean(loginService);

Custom Schema Configuration

// Configure custom database schema
DataSourceLoginService loginService = new DataSourceLoginService("CustomRealm");
loginService.setJndiName("jdbc/AuthDB");

// Custom user table
loginService.setUserTableName("app_users");
loginService.setUserTableKey("user_id");
loginService.setUserTableUserField("username");
loginService.setUserTablePasswordField("password_hash");

// Custom role table
loginService.setRoleTableName("app_roles");
loginService.setRoleTableKey("role_id");
loginService.setRoleTableRoleField("role_name");

// Custom user-role junction table
loginService.setUserRoleTableName("user_role_assignments");
loginService.setUserRoleTableUserKey("user_id");
loginService.setUserRoleTableRoleKey("role_id");

// Enable automatic table creation (for development)
loginService.setCreateTables(true);

Complete Security Setup

// Set up complete database security for Jetty server
Server server = new Server(8080);

// 1. Create and configure login service
DataSourceLoginService loginService = new DataSourceLoginService("SecureRealm");
loginService.setJndiName("jdbc/SecurityDB");
loginService.setServer(server);

// Custom schema configuration
loginService.setUserTableName("users");
loginService.setUserTableUserField("username");
loginService.setUserTablePasswordField("password");
loginService.setRoleTableName("roles");
loginService.setRoleTableRoleField("rolename");
loginService.setUserRoleTableName("user_roles");

server.addBean(loginService);

// 2. Create security handler
SecurityHandler security = new SecurityHandler();
security.setLoginService(loginService);

// 3. Configure authentication method
FormAuthenticator authenticator = new FormAuthenticator();
authenticator.setLoginPage("/login.jsp");
authenticator.setErrorPage("/login-error.jsp");
security.setAuthenticator(authenticator);

// 4. Define security constraints
Constraint constraint = new Constraint();
constraint.setName("auth");
constraint.setAuthenticate(true);
constraint.setRoles(new String[] {"user", "admin"});

ConstraintMapping mapping = new ConstraintMapping();
mapping.setPathSpec("/secure/*");
mapping.setConstraint(constraint);
security.setConstraintMappings(Arrays.asList(mapping));

// 5. Create webapp with security
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/");
webapp.setWar("myapp.war");
webapp.setSecurityHandler(security);

server.setHandler(webapp);

Database Schema Requirements

Default Schema

The DataSourceLoginService expects the following default table structure:

-- Users table
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    username VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(100) NOT NULL
);

-- Roles table  
CREATE TABLE roles (
    id INTEGER PRIMARY KEY,
    rolename VARCHAR(100) NOT NULL UNIQUE
);

-- User-Role junction table
CREATE TABLE user_roles (
    user_id INTEGER NOT NULL,
    role_id INTEGER NOT NULL,
    PRIMARY KEY (user_id, role_id),
    FOREIGN KEY (user_id) REFERENCES users(id),
    FOREIGN KEY (role_id) REFERENCES roles(id)
);

Custom Schema Example

-- Custom table names and fields
CREATE TABLE app_users (
    user_id INTEGER PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    email VARCHAR(100),
    created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE app_roles (
    role_id INTEGER PRIMARY KEY,
    role_name VARCHAR(50) NOT NULL UNIQUE,
    description VARCHAR(200)
);

CREATE TABLE user_role_assignments (
    user_id INTEGER NOT NULL,
    role_id INTEGER NOT NULL,
    assigned_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (user_id, role_id),
    FOREIGN KEY (user_id) REFERENCES app_users(user_id),
    FOREIGN KEY (role_id) REFERENCES app_roles(role_id)
);

Integration Examples

JNDI DataSource Setup

// Set up JNDI DataSource for security database
DataSource securityDataSource = createSecurityDataSource();
Resource securityDB = new Resource("jdbc/SecurityDB", securityDataSource);

// Alternative: scoped to specific webapp
Resource webappSecurityDB = new Resource(webapp, "jdbc/SecurityDB", securityDataSource);

Programmatic User Management

// Example of programmatic user/role management
// (Note: This requires direct database access, not through DataSourceLoginService)

DataSource ds = (DataSource) new InitialContext().lookup("jdbc/SecurityDB");
try (Connection conn = ds.getConnection()) {
    // Create user
    PreparedStatement createUser = conn.prepareStatement(
        "INSERT INTO users (username, password) VALUES (?, ?)");
    createUser.setString(1, "newuser");
    createUser.setString(2, hashedPassword);
    createUser.executeUpdate();
    
    // Get user ID
    PreparedStatement getUser = conn.prepareStatement(
        "SELECT id FROM users WHERE username = ?");
    getUser.setString(1, "newuser");
    ResultSet rs = getUser.executeQuery();
    int userId = rs.next() ? rs.getInt(1) : -1;
    
    // Assign role
    PreparedStatement assignRole = conn.prepareStatement(
        "INSERT INTO user_roles (user_id, role_id) " +
        "SELECT ?, id FROM roles WHERE rolename = ?");
    assignRole.setInt(1, userId);
    assignRole.setString(2, "user");
    assignRole.executeUpdate();
}

Authentication Testing

// Test authentication programmatically
DataSourceLoginService loginService = new DataSourceLoginService("TestRealm");
loginService.setJndiName("jdbc/SecurityDB");

// Initialize the service
loginService.start();

// Test user authentication
UserPrincipal user = loginService.login("testuser", "password");
if (user != null) {
    System.out.println("Authentication successful for: " + user.getName());
    
    // Check roles
    if (loginService.getUserPrincipal("testuser") instanceof DataSourceLoginService.DBUserPrincipal) {
        DataSourceLoginService.DBUserPrincipal dbUser = 
            (DataSourceLoginService.DBUserPrincipal) user;
        System.out.println("User database key: " + dbUser.getKey());
    }
    
    // Get user roles
    List<RolePrincipal> roles = loginService.loadRoleInfo(user);
    for (RolePrincipal role : roles) {
        System.out.println("User has role: " + role.getName());
    }
} else {
    System.out.println("Authentication failed");
}

Integration with Web Security

// Complete web security configuration
WebAppContext webapp = new WebAppContext();

// Set up database login service
DataSourceLoginService loginService = new DataSourceLoginService("WebRealm");
loginService.setJndiName("jdbc/SecurityDB");

// Configure form authentication
SecurityHandler security = new SecurityHandler();
security.setLoginService(loginService);

FormAuthenticator authenticator = new FormAuthenticator("/login", "/login?error=1");
security.setAuthenticator(authenticator);

// Define role-based constraints
Constraint adminConstraint = new Constraint();
adminConstraint.setName("admin");
adminConstraint.setAuthenticate(true);
adminConstraint.setRoles(new String[] {"admin"});

ConstraintMapping adminMapping = new ConstraintMapping();
adminMapping.setPathSpec("/admin/*");
adminMapping.setConstraint(adminConstraint);

Constraint userConstraint = new Constraint();
userConstraint.setName("user");
userConstraint.setAuthenticate(true);
userConstraint.setRoles(new String[] {"user", "admin"});

ConstraintMapping userMapping = new ConstraintMapping();
userMapping.setPathSpec("/user/*");
userMapping.setConstraint(userConstraint);

security.setConstraintMappings(Arrays.asList(adminMapping, userMapping));

webapp.setSecurityHandler(security);

Error Handling and Monitoring

Database Connection Issues

DataSourceLoginService loginService = new DataSourceLoginService("MyRealm");
loginService.setJndiName("jdbc/SecurityDB");

try {
    loginService.initDb();
    System.out.println("Database connection successful");
} catch (NamingException e) {
    System.err.println("JNDI lookup failed: " + e.getMessage());
    // Handle missing DataSource
} catch (SQLException e) {
    System.err.println("Database error: " + e.getMessage());
    // Handle database connectivity issues
}

Authentication Monitoring

// Custom login service with logging
DataSourceLoginService loginService = new DataSourceLoginService("MonitoredRealm") {
    @Override
    public UserPrincipal loadUserInfo(String username) {
        long start = System.currentTimeMillis();
        try {
            UserPrincipal user = super.loadUserInfo(username);
            long duration = System.currentTimeMillis() - start;
            
            if (user != null) {
                LOG.info("User '{}' authentication successful in {}ms", username, duration);
            } else {
                LOG.warn("User '{}' authentication failed in {}ms", username, duration);
            }
            
            return user;
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - start;
            LOG.error("User '{}' authentication error in {}ms: {}", username, duration, e.getMessage());
            throw e;
        }
    }
};

Performance Considerations

Connection Pooling

// Use connection pooling for better performance
HikariDataSource hikariDS = new HikariDataSource();
hikariDS.setJdbcUrl("jdbc:postgresql://localhost:5432/security");
hikariDS.setUsername("security_user");
hikariDS.setPassword("password");
hikariDS.setMaximumPoolSize(10);
hikariDS.setMinimumIdle(2);
hikariDS.setConnectionTimeout(30000);

Resource securityDB = new Resource("jdbc/SecurityDB", hikariDS);

// Configure login service
DataSourceLoginService loginService = new DataSourceLoginService("PooledRealm");
loginService.setJndiName("jdbc/SecurityDB");

Caching Strategies

// For high-traffic applications, consider implementing caching
// Note: This would be custom implementation extending DataSourceLoginService

public class CachedDataSourceLoginService extends DataSourceLoginService {
    private final Map<String, UserPrincipal> userCache = new ConcurrentHashMap<>();
    private final Map<String, List<RolePrincipal>> roleCache = new ConcurrentHashMap<>();
    
    @Override
    public UserPrincipal loadUserInfo(String username) {
        // Check cache first
        UserPrincipal cached = userCache.get(username);
        if (cached != null && isCacheValid(cached)) {
            return cached;
        }
        
        // Load from database
        UserPrincipal user = super.loadUserInfo(username);
        if (user != null) {
            userCache.put(username, user);
        }
        return user;
    }
    
    // Implement cache invalidation, TTL, etc.
}

Install with Tessl CLI

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

docs

annotation-support.md

index.md

jndi-integration.md

security-services.md

tile.json