org.wildfly.security.ssl.ProtocolSelector Maven / Gradle / Ivy
The newest version!
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.security.ssl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* An immutable filter for SSL/TLS protocols.
*
* @author David M. Lloyd
*/
public abstract class ProtocolSelector {
final ProtocolSelector prev;
ProtocolSelector(final ProtocolSelector prev) {
this.prev = prev;
}
/* -- predicates -- */
private static final ProtocolSelector EMPTY = new ProtocolSelector(null) {
void applyFilter(final Set enabled, final EnumMap supported) {
}
void toString(final StringBuilder b) {
b.append("(empty)");
}
};
public final String toString() {
final StringBuilder b = new StringBuilder();
toString(b);
return b.toString();
}
abstract void toString(final StringBuilder b);
/**
* Get the basic empty SSL protocol selector.
*
* @return the empty selector
*/
public static ProtocolSelector empty() {
return EMPTY;
}
/**
* Get the default SSL protocol selector.
*
* @return the default selector
*/
public static ProtocolSelector defaultProtocols() {
return DEFAULT_SELECTOR;
}
/* -- Put this after the EMPTY selector for proper static ordering -- */
static final ProtocolSelector DEFAULT_SELECTOR = empty().add(Protocol.TLSv1, Protocol.TLSv1_1, Protocol.TLSv1_2, Protocol.TLSv1_3);
/* -- delete -- */
/**
* Permanently delete the given protocol. Matching protocols cannot
* be re-added by a later rule (such rules will be ignored).
*
* @param protocolName the name of the protocol to remove
* @return a new selector which includes the new rule
*/
public ProtocolSelector deleteFully(final String protocolName) {
return deleteFully(Protocol.forName(protocolName));
}
/**
* Permanently delete the given protocol. Matching protocols cannot
* be re-added by a later rule (such rules will be ignored).
*
* @param protocol the protocol to remove
* @return a new selector which includes the new rule
*/
public ProtocolSelector deleteFully(final Protocol protocol) {
return protocol == null ? this : new FullyDeletingProtocolSelector(this, EnumSet.of(protocol));
}
/**
* Permanently delete all of the given protocols. Matching protocols cannot
* be re-added by a later rule (such rules will be ignored).
*
* @param protocols the protocols to remove
* @return a new selector which includes the new rule
*/
public ProtocolSelector deleteFully(final Protocol... protocols) {
return protocols == null || protocols.length == 0 ? this : new FullyDeletingProtocolSelector(this, EnumSet.of(protocols[0], protocols));
}
/**
* Permanently delete all of the given protocols. Matching protocols cannot
* be re-added by a later rule (such rules will be ignored).
*
* @param protocols the protocols to remove
* @return a new selector which includes the new rule
*/
public ProtocolSelector deleteFully(final EnumSet protocols) {
return protocols == null || protocols.isEmpty() ? this : new FullyDeletingProtocolSelector(this, protocols);
}
/* -- remove -- */
/**
* Remove the given protocol. Matching protocols may be re-added by a later rule.
*
* @param protocolName the name of the protocol to remove
* @return a new selector which includes the new rule
*/
public ProtocolSelector remove(final String protocolName) {
return remove(Protocol.forName(protocolName));
}
/**
* Remove the given protocol. Matching protocols may be re-added by a later rule.
*
* @param protocol the protocol to remove
* @return a new selector which includes the new rule
*/
public ProtocolSelector remove(final Protocol protocol) {
return protocol == null ? this : new RemovingProtocolSelector(this, EnumSet.of(protocol));
}
/**
* Remove the given protocols. Matching protocols may be re-added by a later rule.
*
* @param protocols the protocols to remove
* @return a new selector which includes the new rule
*/
public ProtocolSelector remove(final Protocol... protocols) {
return protocols == null || protocols.length == 0 ? this : new RemovingProtocolSelector(this, EnumSet.of(protocols[0], protocols));
}
/**
* Remove the given protocols. Matching protocols may be re-added by a later rule.
*
* @param protocols the protocols to remove
* @return a new selector which includes the new rule
*/
public ProtocolSelector remove(final EnumSet protocols) {
return protocols == null || protocols.isEmpty() ? this : new RemovingProtocolSelector(this, protocols);
}
/* -- add -- */
/**
* Add the given protocol.
*
* @param protocolName the name of the protocol to add
* @return a new selector which includes the new rule
*/
public ProtocolSelector add(final String protocolName) {
return add(Protocol.forName(protocolName));
}
/**
* Add the given protocol.
*
* @param protocol the protocol to add
* @return a new selector which includes the new rule
*/
public ProtocolSelector add(final Protocol protocol) {
return protocol == null ? this : new AddingProtocolSelector(this, EnumSet.of(protocol));
}
/**
* Add the given protocols.
*
* @param protocols the protocols to add
* @return a new selector which includes the new rule
*/
public ProtocolSelector add(final Protocol... protocols) {
return protocols == null || protocols.length == 0 ? this : new AddingProtocolSelector(this, EnumSet.of(protocols[0], protocols));
}
/**
* Add the given protocols.
*
* @param protocols the protocols to add
* @return a new selector which includes the new rule
*/
public ProtocolSelector add(final EnumSet protocols) {
return protocols == null || protocols.isEmpty() ? this : new AddingProtocolSelector(this, protocols);
}
/* -- selector implementation -- */
abstract void applyFilter(Set enabled, EnumMap supported);
private void doEvaluate(Set enabled, EnumMap supported) {
if (prev != null) {
prev.doEvaluate(enabled, supported);
}
applyFilter(enabled, supported);
}
/**
* Evaluate this selector against the given list of JSSE supported protocols.
*
* @param supportedProtocols the supported protocols
* @return the enabled protocols (not {@code null})
*/
public final String[] evaluate(String[] supportedProtocols) {
final EnumMap supported = new EnumMap(Protocol.class);
for (String protocolName : supportedProtocols) {
final Protocol protocol = Protocol.forName(protocolName);
if (protocol != null) {
supported.put(protocol, protocolName);
}
}
final LinkedHashSet enabledSet = new LinkedHashSet<>(supported.size());
doEvaluate(enabledSet, supported);
final ArrayList list = new ArrayList<>(enabledSet.size());
for (Protocol protocol : enabledSet) {
list.add(supported.get(protocol));
}
return list.toArray(new String[enabledSet.size()]);
}
/* -- selector impls -- */
static final class AddingProtocolSelector extends ProtocolSelector {
private final EnumSet protocols;
AddingProtocolSelector(final ProtocolSelector prev, final EnumSet protocols) {
super(prev);
this.protocols = protocols;
}
void toString(final StringBuilder b) {
if (prev != null && prev != EMPTY) {
prev.toString(b);
b.append(", then ");
}
b.append("add protocols (");
Iterator iterator = protocols.iterator();
Protocol protocol;
if (iterator.hasNext()) {
protocol = iterator.next();
b.append(protocol);
while (iterator.hasNext()) {
protocol = iterator.next();
b.append(", ");
b.append(protocol);
}
}
b.append(")");
}
void applyFilter(final Set enabled, final EnumMap supported) {
final List clone = new ArrayList<>(supported.keySet());
clone.retainAll(protocols);
// it will be in reverse-preference order due to the ordering of the enum
Collections.reverse(clone);
enabled.addAll(clone);
}
}
static final class RemovingProtocolSelector extends ProtocolSelector {
private final EnumSet protocols;
RemovingProtocolSelector(final ProtocolSelector prev, final EnumSet protocols) {
super(prev);
this.protocols = protocols;
}
void toString(final StringBuilder b) {
if (prev != null && prev != EMPTY) {
prev.toString(b);
b.append(", then ");
}
b.append("remove protocols (");
Iterator iterator = protocols.iterator();
Protocol protocol;
if (iterator.hasNext()) {
protocol = iterator.next();
b.append(protocol);
while (iterator.hasNext()) {
protocol = iterator.next();
b.append(", ");
b.append(protocol);
}
}
b.append(")");
}
void applyFilter(final Set enabled, final EnumMap supported) {
enabled.removeAll(protocols);
}
}
static class FullyDeletingProtocolSelector extends ProtocolSelector {
private final EnumSet protocols;
FullyDeletingProtocolSelector(final ProtocolSelector prev, final EnumSet protocols) {
super(prev);
this.protocols = protocols;
}
void toString(final StringBuilder b) {
if (prev != null && prev != EMPTY) {
prev.toString(b);
b.append(", then ");
}
b.append("fully remove protocols (");
Iterator iterator = protocols.iterator();
Protocol protocol;
if (iterator.hasNext()) {
protocol = iterator.next();
b.append(protocol);
while (iterator.hasNext()) {
protocol = iterator.next();
b.append(", ");
b.append(protocol);
}
}
b.append(")");
}
void applyFilter(final Set enabled, final EnumMap supported) {
enabled.removeAll(protocols);
for (Protocol protocol : protocols) {
supported.remove(protocol);
}
}
}
}