|
6 | 6 |
|
7 | 7 | package io.kroxylicious.proxy.testplugins; |
8 | 8 |
|
| 9 | +import java.util.List; |
| 10 | +import java.util.Map; |
| 11 | + |
| 12 | +import javax.security.auth.callback.Callback; |
| 13 | +import javax.security.auth.callback.NameCallback; |
| 14 | +import javax.security.auth.callback.UnsupportedCallbackException; |
| 15 | +import javax.security.auth.login.AppConfigurationEntry; |
| 16 | + |
| 17 | +import org.apache.kafka.common.security.auth.AuthenticateCallbackHandler; |
| 18 | +import org.apache.kafka.common.security.plain.PlainAuthenticateCallback; |
| 19 | +import org.apache.kafka.common.utils.Utils; |
| 20 | + |
9 | 21 | import io.kroxylicious.proxy.filter.Filter; |
10 | 22 | import io.kroxylicious.proxy.filter.FilterFactory; |
11 | 23 | import io.kroxylicious.proxy.filter.FilterFactoryContext; |
12 | 24 | import io.kroxylicious.proxy.plugin.Plugin; |
13 | 25 | import io.kroxylicious.proxy.plugin.PluginConfigurationException; |
| 26 | +import io.kroxylicious.proxy.plugin.Plugins; |
| 27 | + |
| 28 | +import edu.umd.cs.findbugs.annotations.Nullable; |
14 | 29 |
|
15 | | -@Plugin(configType = Void.class) |
| 30 | +@Plugin(configType = SaslPlainTerminationConfig.class) |
16 | 31 | public class SaslPlainTermination |
17 | | - implements FilterFactory<Void, Void> { |
| 32 | + implements FilterFactory<SaslPlainTerminationConfig, SaslPlainTermination.PasswordVerifier> { |
| 33 | + |
| 34 | + interface PasswordVerifier extends AuthenticateCallbackHandler { |
| 35 | + } |
| 36 | + |
| 37 | + /** |
| 38 | + * This password verifier is insecure in the sense that the user credentials are stored (and configured!) in plaintext. |
| 39 | + */ |
| 40 | + static class InsecurePasswordVerifier implements PasswordVerifier { |
| 41 | + private final Map<String, String> userNameToPassword; |
| 42 | + |
| 43 | + InsecurePasswordVerifier(Map<String, String> userNameToPassword) { |
| 44 | + this.userNameToPassword = userNameToPassword; |
| 45 | + } |
| 46 | + |
| 47 | + @Override |
| 48 | + public void configure(Map<String, ?> configs, String saslMechanism, List<AppConfigurationEntry> jaasConfigEntries) { |
| 49 | + if (!"PLAIN".equals(saslMechanism)) { |
| 50 | + throw new IllegalStateException("This verifier only supports PLAIN authentication"); |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + @Override |
| 55 | + public void close() { |
| 56 | + |
| 57 | + } |
| 58 | + |
| 59 | + @Override |
| 60 | + public void handle(Callback[] callbacks) throws UnsupportedCallbackException { |
| 61 | + String username = null; |
| 62 | + for (Callback callback : callbacks) { |
| 63 | + if (callback instanceof NameCallback) { |
| 64 | + username = ((NameCallback) callback).getDefaultName(); |
| 65 | + } |
| 66 | + else if (callback instanceof PlainAuthenticateCallback) { |
| 67 | + PlainAuthenticateCallback plainCallback = (PlainAuthenticateCallback) callback; |
| 68 | + boolean authenticated = authenticate(username, plainCallback.password()); |
| 69 | + plainCallback.authenticated(authenticated); |
| 70 | + } |
| 71 | + else { |
| 72 | + throw new UnsupportedCallbackException(callback); |
| 73 | + } |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + protected boolean authenticate(@Nullable String username, char[] password) { |
| 78 | + if (username == null) { |
| 79 | + return false; |
| 80 | + } |
| 81 | + else { |
| 82 | + String expectedPassword = userNameToPassword.get(username); |
| 83 | + return expectedPassword != null && Utils.isEqualConstantTime(password, expectedPassword.toCharArray()); |
| 84 | + } |
| 85 | + } |
| 86 | + } |
18 | 87 |
|
19 | 88 | @Override |
20 | | - public Void initialize(FilterFactoryContext context, Void config) throws PluginConfigurationException { |
21 | | - return null; |
| 89 | + public PasswordVerifier initialize(FilterFactoryContext context, SaslPlainTerminationConfig config) throws PluginConfigurationException { |
| 90 | + Plugins.requireConfig(this, config); |
| 91 | + return new InsecurePasswordVerifier(config.userNameToPassword()); |
22 | 92 | } |
23 | 93 |
|
24 | 94 | @Override |
25 | | - public Filter createFilter(FilterFactoryContext context, Void initializationData) { |
26 | | - return new SaslPlainTerminationFilter(); |
| 95 | + public Filter createFilter(FilterFactoryContext context, PasswordVerifier passwordVerifier) { |
| 96 | + return new SaslPlainTerminationFilter(passwordVerifier); |
27 | 97 | } |
28 | 98 | } |
0 commit comments