ExternalSaslMechanism.java
/*
* SPDX-FileCopyrightText: 2023-2025 Lucimber UG
* SPDX-License-Identifier: Apache-2.0
*/
package com.lucimber.dbus.netty.sasl;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.Promise;
import java.nio.charset.StandardCharsets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class ExternalSaslMechanism implements SaslMechanism {
private static final Logger LOGGER = LoggerFactory.getLogger(ExternalSaslMechanism.class);
private String authorizationId;
private boolean complete = false;
@Override
public String getName() {
return "EXTERNAL";
}
@Override
public void init(ChannelHandlerContext ctx) throws SaslMechanismException {
this.authorizationId = AuthorizationIdResolver.resolve();
if (authorizationId == null || authorizationId.isEmpty()) {
throw new SaslMechanismException("EXTERNAL authorization ID is missing.");
}
LOGGER.debug("EXTERNAL initialized with ID: {}", authorizationId);
}
@Override
public Future<String> getInitialResponseAsync(ChannelHandlerContext ctx) {
Promise<String> promise = ctx.executor().newPromise();
findWorkerExecutor(ctx)
.execute(
() -> {
if (authorizationId == null) {
promise.setFailure(
new SaslMechanismException(
"Authorization ID not initialized."));
return;
}
try {
String hexResponse =
SaslUtil.hexEncode(
authorizationId.getBytes(
StandardCharsets.US_ASCII));
promise.setSuccess(hexResponse);
this.complete = true;
} catch (Exception e) {
promise.setFailure(
new SaslMechanismException(
"Failed to hex-encode EXTERNAL ID", e));
}
});
return promise;
}
@Override
public Future<String> processChallengeAsync(ChannelHandlerContext ctx, String challenge) {
Promise<String> promise = ctx.executor().newPromise();
promise.setFailure(
new SaslMechanismException(
"EXTERNAL does not support challenges. Got: " + challenge));
return promise;
}
@Override
public boolean isComplete() {
return complete;
}
@Override
public void dispose() {
this.authorizationId = null;
this.complete = false;
}
private EventExecutor findWorkerExecutor(ChannelHandlerContext ctx) {
return GlobalEventExecutor.INSTANCE;
}
}