| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.nifi.jms.cf; |
| |
| import org.apache.nifi.components.PropertyDescriptor; |
| import org.apache.nifi.components.PropertyDescriptor.Builder; |
| import org.apache.nifi.components.ValidationContext; |
| import org.apache.nifi.components.ValidationResult; |
| import org.apache.nifi.components.Validator; |
| import org.apache.nifi.components.resource.ResourceCardinality; |
| import org.apache.nifi.components.resource.ResourceType; |
| import org.apache.nifi.expression.ExpressionLanguageScope; |
| import org.apache.nifi.processor.util.StandardValidators; |
| |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import java.util.stream.Collectors; |
| |
| import static org.apache.nifi.processor.util.StandardValidators.NON_EMPTY_VALIDATOR; |
| |
| public class JndiJmsConnectionFactoryProperties { |
| |
| public static final String URL_SCHEMES_ALLOWED_PROPERTY = "org.apache.nifi.jms.cf.jndi.provider.url.schemes.allowed"; |
| |
| public static final PropertyDescriptor JNDI_INITIAL_CONTEXT_FACTORY = new Builder() |
| .name("java.naming.factory.initial") |
| .displayName("JNDI Initial Context Factory Class") |
| .description("The fully qualified class name of the JNDI Initial Context Factory Class (java.naming.factory.initial).") |
| .required(true) |
| .addValidator(NON_EMPTY_VALIDATOR) |
| .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) |
| .build(); |
| |
| public static final PropertyDescriptor JNDI_PROVIDER_URL = new Builder() |
| .name("java.naming.provider.url") |
| .displayName("JNDI Provider URL") |
| .description("The URL of the JNDI Provider to use as the value for java.naming.provider.url. See additional details documentation for allowed URL schemes.") |
| .required(true) |
| .addValidator(new JndiJmsProviderUrlValidator()) |
| .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) |
| .build(); |
| |
| public static final PropertyDescriptor JNDI_CONNECTION_FACTORY_NAME = new Builder() |
| .name("connection.factory.name") |
| .displayName("JNDI Name of the Connection Factory") |
| .description("The name of the JNDI Object to lookup for the Connection Factory.") |
| .required(true) |
| .addValidator(NON_EMPTY_VALIDATOR) |
| .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) |
| .build(); |
| |
| public static final PropertyDescriptor JNDI_CLIENT_LIBRARIES = new Builder() |
| .name("naming.factory.libraries") |
| .displayName("JNDI / JMS Client Libraries") |
| .description("Specifies jar files and/or directories to add to the ClassPath " + |
| "in order to load the JNDI / JMS client libraries. This should be a comma-separated list of files, directories, and/or URLs. If a directory is given, any files in that directory" + |
| " will be included, but subdirectories will not be included (i.e., it is not recursive).") |
| .required(false) |
| .identifiesExternalResource(ResourceCardinality.MULTIPLE, ResourceType.FILE, ResourceType.DIRECTORY, ResourceType.URL) |
| .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) |
| .dynamicallyModifiesClasspath(true) |
| .build(); |
| |
| public static final PropertyDescriptor JNDI_PRINCIPAL = new Builder() |
| .name("java.naming.security.principal") |
| .displayName("JNDI Principal") |
| .description("The Principal to use when authenticating with JNDI (java.naming.security.principal).") |
| .required(false) |
| .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) |
| .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) |
| .build(); |
| |
| public static final PropertyDescriptor JNDI_CREDENTIALS = new Builder() |
| .name("java.naming.security.credentials") |
| .displayName("JNDI Credentials") |
| .description("The Credentials to use when authenticating with JNDI (java.naming.security.credentials).") |
| .required(false) |
| .addValidator(Validator.VALID) |
| .expressionLanguageSupported(ExpressionLanguageScope.NONE) |
| .sensitive(true) |
| .build(); |
| |
| private static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS = Arrays.asList( |
| JndiJmsConnectionFactoryProperties.JNDI_INITIAL_CONTEXT_FACTORY, |
| JndiJmsConnectionFactoryProperties.JNDI_PROVIDER_URL, |
| JndiJmsConnectionFactoryProperties.JNDI_CONNECTION_FACTORY_NAME, |
| JndiJmsConnectionFactoryProperties.JNDI_CLIENT_LIBRARIES, |
| JndiJmsConnectionFactoryProperties.JNDI_PRINCIPAL, |
| JndiJmsConnectionFactoryProperties.JNDI_CREDENTIALS |
| ); |
| |
| public static List<PropertyDescriptor> getPropertyDescriptors() { |
| return PROPERTY_DESCRIPTORS; |
| } |
| |
| public static PropertyDescriptor getDynamicPropertyDescriptor(final String propertyDescriptorName) { |
| return new Builder() |
| .name(propertyDescriptorName) |
| .displayName(propertyDescriptorName) |
| .description("JNDI Initial Context Environment configuration for '" + propertyDescriptorName + "'") |
| .required(false) |
| .dynamic(true) |
| .addValidator(Validator.VALID) |
| .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) |
| .build(); |
| } |
| |
| static class JndiJmsProviderUrlValidator implements Validator { |
| |
| private static final Pattern URL_SCHEME_PATTERN = Pattern.compile("^([^:]+)://.+$"); |
| |
| private static final int SCHEME_GROUP = 1; |
| |
| private static final String SPACE_SEPARATOR = " "; |
| |
| private static final Set<String> DEFAULT_ALLOWED_SCHEMES = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList( |
| "file", |
| "jgroups", |
| "t3", |
| "t3s", |
| "tcp", |
| "ssl", |
| "udp", |
| "vm" |
| ))); |
| |
| private final Set<String> allowedSchemes; |
| |
| JndiJmsProviderUrlValidator() { |
| final String allowed = System.getProperty(URL_SCHEMES_ALLOWED_PROPERTY); |
| if (allowed == null || allowed.isEmpty()) { |
| allowedSchemes = DEFAULT_ALLOWED_SCHEMES; |
| } else { |
| allowedSchemes = Arrays.stream(allowed.split(SPACE_SEPARATOR)).collect(Collectors.toSet()); |
| } |
| } |
| |
| @Override |
| public ValidationResult validate(final String subject, final String input, final ValidationContext context) { |
| final ValidationResult.Builder builder = new ValidationResult.Builder().subject(subject).input(input); |
| |
| final String url = context.newPropertyValue(input).evaluateAttributeExpressions().getValue(); |
| if (url == null || url.isEmpty()) { |
| builder.valid(false); |
| builder.explanation("URL is required"); |
| } else if (isUrlAllowed(url)) { |
| builder.valid(true); |
| builder.explanation("URL scheme allowed"); |
| } else { |
| builder.valid(false); |
| final String explanation = String.format("URL scheme not allowed. Allowed URL schemes include %s", allowedSchemes); |
| builder.explanation(explanation); |
| } |
| |
| return builder.build(); |
| } |
| |
| private boolean isUrlAllowed(final String input) { |
| final boolean allowed; |
| |
| final String normalizedUrl = input.trim(); |
| final Matcher matcher = URL_SCHEME_PATTERN.matcher(normalizedUrl); |
| if (matcher.matches()) { |
| final String scheme = matcher.group(SCHEME_GROUP); |
| allowed = isSchemeAllowed(scheme); |
| } else { |
| allowed = true; |
| } |
| |
| return allowed; |
| } |
| |
| private boolean isSchemeAllowed(final String scheme) { |
| boolean allowed = false; |
| |
| for (final String allowedScheme : allowedSchemes) { |
| if (allowedScheme.contains(scheme)) { |
| allowed = true; |
| break; |
| } |
| } |
| |
| return allowed; |
| } |
| } |
| } |