Repository: nifi
Updated Branches:
  refs/heads/master 61fe49378 -> 03adaeca2


NIFI-5114: Added Basic auth support to WebSocket components

This closes #2652

Signed-off-by: Mike Thomsen <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/03adaeca
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/03adaeca
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/03adaeca

Branch: refs/heads/master
Commit: 03adaeca222e2b040fce5680bbc03572d0ba473e
Parents: 61fe493
Author: Koji Kawamura <[email protected]>
Authored: Mon Apr 23 11:38:20 2018 +0900
Committer: Mike Thomsen <[email protected]>
Committed: Mon May 14 14:26:47 2018 -0400

----------------------------------------------------------------------
 .../websocket/jetty/JettyWebSocketClient.java   |  53 +++++++
 .../websocket/jetty/JettyWebSocketServer.java   | 144 +++++++++++++++++--
 .../jetty/ITJettyWebSocketCommunication.java    |   7 +
 .../jetty/TestJettyWebSocketServer.java         |  14 ++
 .../src/test/resources/users.properties         |  20 +++
 5 files changed, 229 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/03adaeca/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/main/java/org/apache/nifi/websocket/jetty/JettyWebSocketClient.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/main/java/org/apache/nifi/websocket/jetty/JettyWebSocketClient.java
 
b/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/main/java/org/apache/nifi/websocket/jetty/JettyWebSocketClient.java
index 2a6bb84..f866743 100644
--- 
a/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/main/java/org/apache/nifi/websocket/jetty/JettyWebSocketClient.java
+++ 
b/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/main/java/org/apache/nifi/websocket/jetty/JettyWebSocketClient.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.websocket.jetty;
 
+import org.apache.commons.codec.binary.Base64;
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
 import org.apache.nifi.annotation.documentation.Tags;
 import org.apache.nifi.annotation.lifecycle.OnDisabled;
@@ -28,9 +29,11 @@ import org.apache.nifi.expression.ExpressionLanguageScope;
 import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.processor.util.StandardValidators;
 import org.apache.nifi.ssl.SSLContextService;
+import org.apache.nifi.util.StringUtils;
 import org.apache.nifi.websocket.WebSocketClientService;
 import org.apache.nifi.websocket.WebSocketConfigurationException;
 import org.apache.nifi.websocket.WebSocketMessageRouter;
+import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
@@ -38,6 +41,7 @@ import org.eclipse.jetty.websocket.client.WebSocketClient;
 
 import java.io.IOException;
 import java.net.URI;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -103,6 +107,35 @@ public class JettyWebSocketClient extends 
AbstractJettyWebSocketService implemen
             .defaultValue("10 sec")
             .build();
 
+    public static final PropertyDescriptor USER_NAME = new 
PropertyDescriptor.Builder()
+            .name("user-name")
+            .displayName("User Name")
+            .description("The user name for Basic Authentication.")
+            .required(false)
+            
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
+            .addValidator(StandardValidators.NON_EMPTY_EL_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor USER_PASSWORD = new 
PropertyDescriptor.Builder()
+            .name("user-password")
+            .displayName("User Password")
+            .description("The user password for Basic Authentication.")
+            .required(false)
+            
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
+            .addValidator(StandardValidators.NON_EMPTY_EL_VALIDATOR)
+            .sensitive(true)
+            .build();
+
+    public static final PropertyDescriptor AUTH_CHARSET = new 
PropertyDescriptor.Builder()
+            .name("authentication-charset")
+            .displayName("Authentication Header Charset")
+            .description("The charset for Basic Authentication header base64 
string.")
+            .required(true)
+            
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
+            .addValidator(StandardValidators.NON_EMPTY_EL_VALIDATOR)
+            .defaultValue("US-ASCII")
+            .build();
+
     private static final List<PropertyDescriptor> properties;
 
     static {
@@ -112,12 +145,16 @@ public class JettyWebSocketClient extends 
AbstractJettyWebSocketService implemen
         props.add(SSL_CONTEXT);
         props.add(CONNECTION_TIMEOUT);
         props.add(SESSION_MAINTENANCE_INTERVAL);
+        props.add(USER_NAME);
+        props.add(USER_PASSWORD);
+        props.add(AUTH_CHARSET);
 
         properties = Collections.unmodifiableList(props);
     }
 
     private WebSocketClient client;
     private URI webSocketUri;
+    private String authorizationHeader;
     private long connectionTimeoutMillis;
     private volatile ScheduledExecutorService sessionMaintenanceScheduler;
     private final ReentrantLock connectionLock = new ReentrantLock();
@@ -139,6 +176,19 @@ public class JettyWebSocketClient extends 
AbstractJettyWebSocketService implemen
         client = new WebSocketClient(sslContextFactory);
 
         configurePolicy(context, client.getPolicy());
+        final String userName = 
context.getProperty(USER_NAME).evaluateAttributeExpressions().getValue();
+        final String userPassword = 
context.getProperty(USER_PASSWORD).evaluateAttributeExpressions().getValue();
+        if (!StringUtils.isEmpty(userName) && 
!StringUtils.isEmpty(userPassword)) {
+            final String charsetName = 
context.getProperty(AUTH_CHARSET).evaluateAttributeExpressions().getValue();
+            if (StringUtils.isEmpty(charsetName)) {
+                throw new 
IllegalArgumentException(AUTH_CHARSET.getDisplayName() + " was not specified.");
+            }
+            final Charset charset = Charset.forName(charsetName);
+            final String base64String = Base64.encodeBase64String((userName + 
":" + userPassword).getBytes(charset));
+            authorizationHeader = "Basic " + base64String;
+        } else {
+            authorizationHeader = null;
+        }
 
         client.start();
         activeSessions.clear();
@@ -201,6 +251,9 @@ public class JettyWebSocketClient extends 
AbstractJettyWebSocketService implemen
             listener.setSessionId(sessionId);
 
             final ClientUpgradeRequest request = new ClientUpgradeRequest();
+            if (!StringUtils.isEmpty(authorizationHeader)) {
+                request.setHeader(HttpHeader.AUTHORIZATION.asString(), 
authorizationHeader);
+            }
             final Future<Session> connect = client.connect(listener, 
webSocketUri, request);
             getLogger().info("Connecting to : {}", new Object[]{webSocketUri});
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/03adaeca/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/main/java/org/apache/nifi/websocket/jetty/JettyWebSocketServer.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/main/java/org/apache/nifi/websocket/jetty/JettyWebSocketServer.java
 
b/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/main/java/org/apache/nifi/websocket/jetty/JettyWebSocketServer.java
index 5a2dbbf..eac43bb 100644
--- 
a/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/main/java/org/apache/nifi/websocket/jetty/JettyWebSocketServer.java
+++ 
b/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/main/java/org/apache/nifi/websocket/jetty/JettyWebSocketServer.java
@@ -16,12 +16,6 @@
  */
 package org.apache.nifi.websocket.jetty;
 
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
 import org.apache.nifi.annotation.documentation.Tags;
 import org.apache.nifi.annotation.lifecycle.OnDisabled;
@@ -29,6 +23,8 @@ import org.apache.nifi.annotation.lifecycle.OnEnabled;
 import org.apache.nifi.annotation.lifecycle.OnShutdown;
 import org.apache.nifi.components.AllowableValue;
 import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
 import org.apache.nifi.controller.ConfigurationContext;
 import org.apache.nifi.expression.ExpressionLanguageScope;
 import org.apache.nifi.processor.util.StandardValidators;
@@ -36,6 +32,11 @@ import org.apache.nifi.ssl.SSLContextService;
 import org.apache.nifi.websocket.WebSocketConfigurationException;
 import org.apache.nifi.websocket.WebSocketMessageRouter;
 import org.apache.nifi.websocket.WebSocketServerService;
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.security.DefaultAuthenticatorFactory;
+import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.security.LoginService;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HttpConfiguration;
@@ -47,6 +48,7 @@ import org.eclipse.jetty.server.SslConnectionFactory;
 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.util.security.Constraint;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
@@ -56,6 +58,14 @@ import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
 import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
 import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
 
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
 @Tags({"WebSocket", "Jetty", "server"})
 @CapabilityDescription("Implementation of WebSocketServerService." +
         " This service uses Jetty WebSocket server module to provide" +
@@ -73,13 +83,17 @@ public class JettyWebSocketServer extends 
AbstractJettyWebSocketService implemen
     public static final AllowableValue CLIENT_WANT = new 
AllowableValue("want", "Want Authentication",
             "Processor will try to verify the client but if unable to verify 
will allow the client to communicate anonymously");
     public static final AllowableValue CLIENT_NEED = new 
AllowableValue("need", "Need Authentication",
-            "Processor will reject communications from any client unless the 
client provides a certificate that is trusted by the TrustStore"
+            "Processor will reject communications from any client unless the 
client provides a certificate that is trusted by the TrustStore "
                     + "specified in the SSL Context Service");
 
+    public static final AllowableValue LOGIN_SERVICE_HASH = new 
AllowableValue("hash", "HashLoginService",
+            "See 
http://www.eclipse.org/jetty/javadoc/current/org/eclipse/jetty/security/HashLoginService.html
 for detail.");
+
     public static final PropertyDescriptor CLIENT_AUTH = new 
PropertyDescriptor.Builder()
             .name("client-authentication")
-            .displayName("Client Authentication")
-            .description("Specifies whether or not the Processor should 
authenticate clients. This value is ignored if the <SSL Context Service> "
+            .displayName("SSL Client Authentication")
+            .description("Specifies whether or not the Processor should 
authenticate client by its certificate. "
+                    + "This value is ignored if the <SSL Context Service> "
                     + "Property is not specified or the SSL Context provided 
uses only a KeyStore and not a TrustStore.")
             .required(true)
             .allowableValues(CLIENT_NONE, CLIENT_WANT, CLIENT_NEED)
@@ -95,6 +109,58 @@ public class JettyWebSocketServer extends 
AbstractJettyWebSocketService implemen
             .addValidator(StandardValidators.PORT_VALIDATOR)
             .build();
 
+    public static final PropertyDescriptor BASIC_AUTH = new 
PropertyDescriptor.Builder()
+            .name("basic-auth")
+            .displayName("Enable Basic Authentication")
+            .description("If enabled, client connection requests are 
authenticated with "
+                    + "Basic authentication using the specified Login 
Provider.")
+            .required(true)
+            .allowableValues("true", "false")
+            .defaultValue("false")
+            .build();
+
+    public static final PropertyDescriptor AUTH_PATH_SPEC = new 
PropertyDescriptor.Builder()
+            .name("auth-path-spec")
+            .displayName("Basic Authentication Path Spec")
+            .description("Specify a Path Spec to apply Basic Authentication.")
+            .required(false)
+            .defaultValue("/*")
+            
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor AUTH_ROLES = new 
PropertyDescriptor.Builder()
+            .name("auth-roles")
+            .displayName("Basic Authentication Roles")
+            .description("The authenticated user must have one of specified 
role. "
+                    + "Multiple roles can be set as comma separated string. "
+                    + "'*' represents any role and so does '**' any role 
including no role.")
+            .required(false)
+            .defaultValue("**")
+            
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor LOGIN_SERVICE = new 
PropertyDescriptor.Builder()
+            .name("login-service")
+            .displayName("Login Service")
+            .description("Specify which Login Service to use for Basic 
Authentication.")
+            .required(false)
+            .allowableValues(LOGIN_SERVICE_HASH)
+            .defaultValue(LOGIN_SERVICE_HASH.getValue())
+            .build();
+
+
+    public static final PropertyDescriptor USERS_PROPERTIES_FILE = new 
PropertyDescriptor.Builder()
+            .name("users-properties-file")
+            .displayName("Users Properties File")
+            .description("Specify a property file containing users for Basic 
Authentication using HashLoginService. "
+                    + "See 
http://www.eclipse.org/jetty/documentation/current/configuring-security.html 
for detail.")
+            .required(false)
+            
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
+            .addValidator(StandardValidators.FILE_EXISTS_VALIDATOR)
+            .build();
+
     private static final List<PropertyDescriptor> properties;
 
     static {
@@ -103,6 +169,12 @@ public class JettyWebSocketServer extends 
AbstractJettyWebSocketService implemen
         props.add(LISTEN_PORT);
         props.add(SSL_CONTEXT);
         props.add(CLIENT_AUTH);
+        props.add(BASIC_AUTH);
+        props.add(AUTH_PATH_SPEC);
+        props.add(AUTH_ROLES);
+        props.add(LOGIN_SERVICE);
+        props.add(USERS_PROPERTIES_FILE);
+
 
         properties = Collections.unmodifiableList(props);
     }
@@ -118,6 +190,23 @@ public class JettyWebSocketServer extends 
AbstractJettyWebSocketService implemen
     }
 
 
+    @Override
+    protected Collection<ValidationResult> customValidate(ValidationContext 
validationContext) {
+
+        final List<ValidationResult> results = new ArrayList<>();
+        if (validationContext.getProperty(BASIC_AUTH).asBoolean()) {
+            final String loginServiceValue = 
validationContext.getProperty(LOGIN_SERVICE).getValue();
+            if (LOGIN_SERVICE_HASH.equals(loginServiceValue)) {
+                if 
(!validationContext.getProperty(USERS_PROPERTIES_FILE).isSet()) {
+                    results.add(new 
ValidationResult.Builder().subject(USERS_PROPERTIES_FILE.getDisplayName())
+                            .explanation("it is required by 
HashLoginService").valid(false).build());
+                }
+            }
+        }
+
+        return results;
+    }
+
     public static class JettyWebSocketServlet extends WebSocketServlet 
implements WebSocketCreator {
         @Override
         public void configure(WebSocketServletFactory webSocketServletFactory) 
{
@@ -174,6 +263,42 @@ public class JettyWebSocketServer extends 
AbstractJettyWebSocketService implemen
         final ContextHandlerCollection handlerCollection = new 
ContextHandlerCollection();
 
         final ServletContextHandler contextHandler = new 
ServletContextHandler();
+
+        // Add basic auth.
+        if (context.getProperty(BASIC_AUTH).asBoolean()) {
+            final ConstraintSecurityHandler securityHandler = new 
ConstraintSecurityHandler();
+            contextHandler.insertHandler(securityHandler);
+
+            final Constraint constraint = new Constraint();
+            constraint.setName("auth");
+            constraint.setAuthenticate(true);
+            // Accessible from any role and any auth once authentication 
succeeds.
+            final String roles = 
context.getProperty(AUTH_ROLES).evaluateAttributeExpressions().getValue();
+            constraint.setRoles(roles.split(","));
+
+            final ConstraintMapping constraintMapping = new 
ConstraintMapping();
+            
constraintMapping.setPathSpec(context.getProperty(AUTH_PATH_SPEC).evaluateAttributeExpressions().getValue());
+            constraintMapping.setConstraint(constraint);
+
+            final DefaultAuthenticatorFactory authenticatorFactory = new 
DefaultAuthenticatorFactory();
+            securityHandler.setAuthenticatorFactory(authenticatorFactory);
+            securityHandler.setAuthMethod(Constraint.__BASIC_AUTH);
+            securityHandler.setRealmName(getClass().getSimpleName());
+            
securityHandler.setConstraintMappings(Collections.singletonList(constraintMapping));
+
+            final LoginService loginService;
+            final String loginServiceValue = 
context.getProperty(LOGIN_SERVICE).getValue();
+            if (LOGIN_SERVICE_HASH.equals(loginServiceValue)) {
+                final String usersFilePath = 
context.getProperty(USERS_PROPERTIES_FILE).evaluateAttributeExpressions().getValue();
+                loginService = new HashLoginService("HashLoginService", 
usersFilePath);
+            } else {
+                throw new IllegalArgumentException("Unsupported Login Service: 
" + loginServiceValue);
+            }
+
+            server.addBean(loginService);
+            securityHandler.setLoginService(loginService);
+        }
+
         servletHandler = new ServletHandler();
         contextHandler.insertHandler(servletHandler);
 
@@ -181,6 +306,7 @@ public class JettyWebSocketServer extends 
AbstractJettyWebSocketService implemen
 
         server.setHandler(handlerCollection);
 
+
         listenPort = 
context.getProperty(LISTEN_PORT).evaluateAttributeExpressions().asInteger();
         final SslContextFactory sslContextFactory = createSslFactory(context);
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/03adaeca/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/java/org/apache/nifi/websocket/jetty/ITJettyWebSocketCommunication.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/java/org/apache/nifi/websocket/jetty/ITJettyWebSocketCommunication.java
 
b/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/java/org/apache/nifi/websocket/jetty/ITJettyWebSocketCommunication.java
index 6d3f063..d5053ac 100644
--- 
a/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/java/org/apache/nifi/websocket/jetty/ITJettyWebSocketCommunication.java
+++ 
b/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/java/org/apache/nifi/websocket/jetty/ITJettyWebSocketCommunication.java
@@ -74,6 +74,10 @@ public class ITJettyWebSocketCommunication {
         serverService = new JettyWebSocketServer();
         serverServiceContext = new ControllerServiceTestContext(serverService, 
"JettyWebSocketServer1");
         serverServiceContext.setCustomValue(JettyWebSocketServer.LISTEN_PORT, 
String.valueOf(serverPort));
+        serverServiceContext.setCustomValue(JettyWebSocketServer.BASIC_AUTH, 
"true");
+        
serverServiceContext.setCustomValue(JettyWebSocketServer.USERS_PROPERTIES_FILE,
+                getClass().getResource("/users.properties").getPath());
+        serverServiceContext.setCustomValue(JettyWebSocketServer.AUTH_ROLES, 
"user,test");
 
         customizeServer();
 
@@ -89,6 +93,9 @@ public class ITJettyWebSocketCommunication {
         clientServiceContext = new ControllerServiceTestContext(clientService, 
"JettyWebSocketClient1");
         clientServiceContext.setCustomValue(JettyWebSocketClient.WS_URI, 
(isSecure() ? "wss" : "ws") + "://localhost:" + serverPort + serverPath);
 
+        clientServiceContext.setCustomValue(JettyWebSocketClient.USER_NAME, 
"user2");
+        
clientServiceContext.setCustomValue(JettyWebSocketClient.USER_PASSWORD, 
"password2");
+
         customizeClient();
 
         
clientService.initialize(clientServiceContext.getInitializationContext());

http://git-wip-us.apache.org/repos/asf/nifi/blob/03adaeca/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/java/org/apache/nifi/websocket/jetty/TestJettyWebSocketServer.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/java/org/apache/nifi/websocket/jetty/TestJettyWebSocketServer.java
 
b/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/java/org/apache/nifi/websocket/jetty/TestJettyWebSocketServer.java
index c056e21..bfd96af 100644
--- 
a/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/java/org/apache/nifi/websocket/jetty/TestJettyWebSocketServer.java
+++ 
b/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/java/org/apache/nifi/websocket/jetty/TestJettyWebSocketServer.java
@@ -38,6 +38,20 @@ public class TestJettyWebSocketServer {
     }
 
     @Test
+    public void testValidationHashLoginService() throws Exception {
+        final JettyWebSocketServer service = new JettyWebSocketServer();
+        final ControllerServiceTestContext context = new 
ControllerServiceTestContext(service, "service-id");
+        context.setCustomValue(JettyWebSocketServer.LISTEN_PORT, "9001");
+        context.setCustomValue(JettyWebSocketServer.LOGIN_SERVICE, "hash");
+        context.setCustomValue(JettyWebSocketServer.BASIC_AUTH, "true");
+        service.initialize(context.getInitializationContext());
+        final Collection<ValidationResult> results = 
service.validate(context.getValidationContext());
+        assertEquals(1, results.size());
+        final ValidationResult result = results.iterator().next();
+        
assertEquals(JettyWebSocketServer.USERS_PROPERTIES_FILE.getDisplayName(), 
result.getSubject());
+    }
+
+    @Test
     public void testValidationSuccess() throws Exception {
         final JettyWebSocketServer service = new JettyWebSocketServer();
         final ControllerServiceTestContext context = new 
ControllerServiceTestContext(service, "service-id");

http://git-wip-us.apache.org/repos/asf/nifi/blob/03adaeca/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/resources/users.properties
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/resources/users.properties
 
b/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/resources/users.properties
new file mode 100644
index 0000000..9892497
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-websocket-bundle/nifi-websocket-services-jetty/src/test/resources/users.properties
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+user1=password1,admin
+user2=password2,user
+# Not associated with any role
+user3=password3

Reply via email to