You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by bb...@apache.org on 2016/02/19 19:22:17 UTC
[1/2] nifi git commit: NIFI-1497: - Introducing a one time use
password service for use in query parameters when accessing UI extensions and
downloading resources. - Using one time use tokens when accessing ui
extensions and downloading resources. - Ensu
Repository: nifi
Updated Branches:
refs/heads/master 86ab4428f -> a8edab2e7
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
index 414d9f8..23b49b7 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationServiceTest.java
@@ -26,7 +26,7 @@ import org.apache.nifi.authorization.Authority;
import org.apache.nifi.user.NiFiUser;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.UntrustedProxyException;
-import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthorizationRequestToken;
import org.apache.nifi.web.security.user.NiFiUserDetails;
import org.junit.Assert;
import org.junit.Before;
@@ -104,8 +104,8 @@ public class NiFiAuthorizationServiceTest {
authorizationService.setUserService(userService);
}
- private NiFiAuthortizationRequestToken createRequestAuthentication(final String... identities) {
- return new NiFiAuthortizationRequestToken(Arrays.asList(identities));
+ private NiFiAuthorizationRequestToken createRequestAuthentication(final String... identities) {
+ return new NiFiAuthorizationRequestToken(Arrays.asList(identities));
}
/**
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/otp/OtpAuthenticationFilterTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/otp/OtpAuthenticationFilterTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/otp/OtpAuthenticationFilterTest.java
new file mode 100644
index 0000000..d309191
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/otp/OtpAuthenticationFilterTest.java
@@ -0,0 +1,210 @@
+/*
+ * 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.web.security.otp;
+
+import org.apache.nifi.web.security.InvalidAuthenticationException;
+import org.apache.nifi.web.security.token.NiFiAuthorizationRequestToken;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import javax.servlet.http.HttpServletRequest;
+
+import java.util.List;
+import java.util.UUID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class OtpAuthenticationFilterTest {
+
+ private final static String UI_EXTENSION_AUTHENTICATED_USER = "ui-extension-token-authenticated-user";
+ private final static String UI_EXTENSION_TOKEN = "ui-extension-token";
+
+ private final static String DOWNLOAD_AUTHENTICATED_USER = "download-token-authenticated-user";
+ private final static String DOWNLOAD_TOKEN = "download-token";
+
+ private OtpService otpService;
+ private OtpAuthenticationFilter otpAuthenticationFilter;
+
+ @Before
+ public void setUp() throws Exception {
+ otpService = mock(OtpService.class);
+ doAnswer(new Answer<String>() {
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ String downloadToken = (String) args[0];
+
+ if (DOWNLOAD_TOKEN.equals(downloadToken)) {
+ return DOWNLOAD_AUTHENTICATED_USER;
+ }
+
+ throw new OtpAuthenticationException("Invalid token");
+ }
+ }).when(otpService).getAuthenticationFromDownloadToken(anyString());
+ doAnswer(new Answer<String>() {
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ String uiExtensionToken = (String) args[0];
+
+ if (UI_EXTENSION_TOKEN.equals(uiExtensionToken)) {
+ return UI_EXTENSION_AUTHENTICATED_USER;
+ }
+
+ throw new OtpAuthenticationException("Invalid token");
+ }
+ }).when(otpService).getAuthenticationFromUiExtensionToken(anyString());
+
+ otpAuthenticationFilter = new OtpAuthenticationFilter();
+ otpAuthenticationFilter.setOtpService(otpService);
+ }
+
+ @Test
+ public void testInsecureHttp() throws Exception {
+ final HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.isSecure()).thenReturn(false);
+ assertNull(otpAuthenticationFilter.attemptAuthentication(request));
+ }
+
+ @Test
+ public void testNoAccessToken() throws Exception {
+ final HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.isSecure()).thenReturn(true);
+ when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn(null);
+ assertNull(otpAuthenticationFilter.attemptAuthentication(request));
+ }
+
+ @Test(expected = InvalidAuthenticationException.class)
+ public void testTokenSupportDisabled() throws Exception {
+ final HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.isSecure()).thenReturn(true);
+ when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn("my-access-token");
+
+ final OtpAuthenticationFilter noTokenAuthenticationFilter = new OtpAuthenticationFilter();
+ noTokenAuthenticationFilter.attemptAuthentication(request);
+ }
+
+ @Test
+ public void testUnsupportedDownloadPath() throws Exception {
+ final HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.isSecure()).thenReturn(true);
+ when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn("my-access-token");
+ when(request.getContextPath()).thenReturn("/nifi-api");
+ when(request.getPathInfo()).thenReturn("/controller/config");
+
+ assertNull(otpAuthenticationFilter.attemptAuthentication(request));
+ }
+
+ @Test
+ public void testUiExtensionPath() throws Exception {
+ final HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.isSecure()).thenReturn(true);
+ when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn(UI_EXTENSION_TOKEN);
+ when(request.getContextPath()).thenReturn("/nifi-update-attribute-ui");
+
+ final NiFiAuthorizationRequestToken result = otpAuthenticationFilter.attemptAuthentication(request);
+ final List<String> chain = result.getChain();
+ assertEquals(1, chain.size());
+ assertEquals(UI_EXTENSION_AUTHENTICATED_USER, chain.get(0));
+
+ verify(otpService, times(1)).getAuthenticationFromUiExtensionToken(UI_EXTENSION_TOKEN);
+ verify(otpService, never()).getAuthenticationFromDownloadToken(anyString());
+ }
+
+ @Test
+ public void testProvenanceInputContentDownload() throws Exception {
+ final HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.isSecure()).thenReturn(true);
+ when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn(DOWNLOAD_TOKEN);
+ when(request.getContextPath()).thenReturn("/nifi-api");
+ when(request.getPathInfo()).thenReturn("/controller/provenance/events/0/content/input");
+
+ final NiFiAuthorizationRequestToken result = otpAuthenticationFilter.attemptAuthentication(request);
+ final List<String> chain = result.getChain();
+ assertEquals(1, chain.size());
+ assertEquals(DOWNLOAD_AUTHENTICATED_USER, chain.get(0));
+
+ verify(otpService, never()).getAuthenticationFromUiExtensionToken(anyString());
+ verify(otpService, times(1)).getAuthenticationFromDownloadToken(DOWNLOAD_TOKEN);
+ }
+
+ @Test
+ public void testProvenanceOutputContentDownload() throws Exception {
+ final HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.isSecure()).thenReturn(true);
+ when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn(DOWNLOAD_TOKEN);
+ when(request.getContextPath()).thenReturn("/nifi-api");
+ when(request.getPathInfo()).thenReturn("/controller/provenance/events/0/content/output");
+
+ final NiFiAuthorizationRequestToken result = otpAuthenticationFilter.attemptAuthentication(request);
+ final List<String> chain = result.getChain();
+ assertEquals(1, chain.size());
+ assertEquals(DOWNLOAD_AUTHENTICATED_USER, chain.get(0));
+
+ verify(otpService, never()).getAuthenticationFromUiExtensionToken(anyString());
+ verify(otpService, times(1)).getAuthenticationFromDownloadToken(DOWNLOAD_TOKEN);
+ }
+
+ @Test
+ public void testFlowFileContentDownload() throws Exception {
+ final String uuid = UUID.randomUUID().toString();
+
+ final HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.isSecure()).thenReturn(true);
+ when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn(DOWNLOAD_TOKEN);
+ when(request.getContextPath()).thenReturn("/nifi-api");
+ when(request.getPathInfo()).thenReturn(String.format("/controller/process-groups/root/connections/%s/flowfiles/%s/content", uuid, uuid));
+
+ final NiFiAuthorizationRequestToken result = otpAuthenticationFilter.attemptAuthentication(request);
+ final List<String> chain = result.getChain();
+ assertEquals(1, chain.size());
+ assertEquals(DOWNLOAD_AUTHENTICATED_USER, chain.get(0));
+
+ verify(otpService, never()).getAuthenticationFromUiExtensionToken(anyString());
+ verify(otpService, times(1)).getAuthenticationFromDownloadToken(DOWNLOAD_TOKEN);
+ }
+
+ @Test
+ public void testTemplateDownload() throws Exception {
+ final String uuid = UUID.randomUUID().toString();
+
+ final HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.isSecure()).thenReturn(true);
+ when(request.getParameter(OtpAuthenticationFilter.ACCESS_TOKEN)).thenReturn(DOWNLOAD_TOKEN);
+ when(request.getContextPath()).thenReturn("/nifi-api");
+ when(request.getPathInfo()).thenReturn(String.format("/controller/templates/%s", uuid));
+
+ final NiFiAuthorizationRequestToken result = otpAuthenticationFilter.attemptAuthentication(request);
+ final List<String> chain = result.getChain();
+ assertEquals(1, chain.size());
+ assertEquals(DOWNLOAD_AUTHENTICATED_USER, chain.get(0));
+
+ verify(otpService, never()).getAuthenticationFromUiExtensionToken(anyString());
+ verify(otpService, times(1)).getAuthenticationFromDownloadToken(DOWNLOAD_TOKEN);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/otp/OtpServiceTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/otp/OtpServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/otp/OtpServiceTest.java
new file mode 100644
index 0000000..56fc9b1
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/otp/OtpServiceTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.web.security.otp;
+
+import org.apache.nifi.web.security.token.OtpAuthenticationToken;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+public class OtpServiceTest {
+
+ private final static String USER_1 = "user-identity-1";
+
+ private OtpService otpService;
+
+ @Before
+ public void setUp() throws Exception {
+ otpService = new OtpService();
+ }
+
+ @Test
+ public void testGetAuthenticationForValidDownloadToken() throws Exception {
+ final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
+ final String downloadToken = otpService.generateDownloadToken(authenticationToken);
+ final String authenticatedUser = otpService.getAuthenticationFromDownloadToken(downloadToken);
+
+ assertNotNull(authenticatedUser);
+ assertEquals(USER_1, authenticatedUser);
+
+ try {
+ // ensure the token is no longer valid
+ otpService.getAuthenticationFromDownloadToken(downloadToken);
+ fail();
+ } catch (final OtpAuthenticationException oae) {}
+ }
+
+ @Test
+ public void testGetAuthenticationForValidUiExtensionToken() throws Exception {
+ final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
+ final String uiExtensionToken = otpService.generateUiExtensionToken(authenticationToken);
+ final String authenticatedUser = otpService.getAuthenticationFromUiExtensionToken(uiExtensionToken);
+
+ assertNotNull(authenticatedUser);
+ assertEquals(USER_1, authenticatedUser);
+
+ try {
+ // ensure the token is no longer valid
+ otpService.getAuthenticationFromUiExtensionToken(uiExtensionToken);
+ fail();
+ } catch (final OtpAuthenticationException oae) {}
+ }
+
+ @Test(expected = OtpAuthenticationException.class)
+ public void testGetNonExistentDownloadToken() throws Exception {
+ otpService.getAuthenticationFromDownloadToken("Not a real download token");
+ }
+
+ @Test(expected = OtpAuthenticationException.class)
+ public void testGetNonExistentUiExtensionToken() throws Exception {
+ otpService.getAuthenticationFromUiExtensionToken("Not a real ui extension token");
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testMaxDownloadTokenLimit() throws Exception {
+ // ensure we'll try to loop past the limit
+ for (int i = 1; i < OtpService.MAX_CACHE_SOFT_LIMIT + 10; i++) {
+ try {
+ final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken("user-identity-" + i);
+ otpService.generateDownloadToken(authenticationToken);
+ } catch (final IllegalStateException iae) {
+ // ensure we failed when we've past the limit
+ assertEquals(OtpService.MAX_CACHE_SOFT_LIMIT + 1, i);
+ throw iae;
+ }
+ }
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testMaxUiExtensionTokenLimit() throws Exception {
+ // ensure we'll try to loop past the limit
+ for (int i = 1; i < OtpService.MAX_CACHE_SOFT_LIMIT + 10; i++) {
+ try {
+ final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken("user-identity-" + i);
+ otpService.generateUiExtensionToken(authenticationToken);
+ } catch (final IllegalStateException iae) {
+ // ensure we failed when we've past the limit
+ assertEquals(OtpService.MAX_CACHE_SOFT_LIMIT + 1, i);
+ throw iae;
+ }
+ }
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testNullTimeUnits() throws Exception {
+ new OtpService(0, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNegativeExpiration() throws Exception {
+ new OtpService(-1, TimeUnit.MINUTES);
+ }
+
+ @Test(expected = OtpAuthenticationException.class)
+ public void testUiExtensionTokenExpiration() throws Exception {
+ final OtpService otpServiceWithTightExpiration = new OtpService(2, TimeUnit.SECONDS);
+
+ final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
+ final String downloadToken = otpServiceWithTightExpiration.generateUiExtensionToken(authenticationToken);
+
+ // sleep for 4 seconds which should sufficiently expire the valid token
+ Thread.sleep(4 * 1000);
+
+ // attempt to get the token now that its expired
+ otpServiceWithTightExpiration.getAuthenticationFromUiExtensionToken(downloadToken);
+ }
+
+ @Test(expected = OtpAuthenticationException.class)
+ public void testDownloadTokenExpiration() throws Exception {
+ final OtpService otpServiceWithTightExpiration = new OtpService(2, TimeUnit.SECONDS);
+
+ final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(USER_1);
+ final String downloadToken = otpServiceWithTightExpiration.generateDownloadToken(authenticationToken);
+
+ // sleep for 4 seconds which should sufficiently expire the valid token
+ Thread.sleep(4 * 1000);
+
+ // attempt to get the token now that its expired
+ otpServiceWithTightExpiration.getAuthenticationFromDownloadToken(downloadToken);
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
index 3f432c2..cd2f722 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
@@ -280,6 +280,7 @@
<include>${staging.dir}/js/nf/nf-dialog.js</include>
<include>${staging.dir}/js/nf/nf-shell.js</include>
<include>${staging.dir}/js/nf/nf-storage.js</include>
+ <include>${staging.dir}/js/nf/nf-ajax-setup.js</include>
<include>${staging.dir}/js/nf/canvas/nf-snippet.js</include>
<include>${staging.dir}/js/nf/canvas/nf-canvas-toolbox.js</include>
<include>${staging.dir}/js/nf/canvas/nf-custom-ui.js</include>
@@ -336,6 +337,7 @@
<include>${staging.dir}/js/nf/nf-universal-capture.js</include>
<include>${staging.dir}/js/nf/nf-dialog.js</include>
<include>${staging.dir}/js/nf/nf-storage.js</include>
+ <include>${staging.dir}/js/nf/nf-ajax-setup.js</include>
<include>${staging.dir}/js/nf/history/nf-history.js</include>
<include>${staging.dir}/js/nf/history/nf-history-table.js</include>
<include>${staging.dir}/js/nf/history/nf-history-model.js</include>
@@ -350,6 +352,7 @@
<include>${staging.dir}/js/nf/nf-universal-capture.js</include>
<include>${staging.dir}/js/nf/nf-dialog.js</include>
<include>${staging.dir}/js/nf/nf-storage.js</include>
+ <include>${staging.dir}/js/nf/nf-ajax-setup.js</include>
<include>${staging.dir}/js/nf/provenance/nf-provenance.js</include>
<include>${staging.dir}/js/nf/provenance/nf-provenance-table.js</include>
</includes>
@@ -363,6 +366,7 @@
<include>${staging.dir}/js/nf/nf-universal-capture.js</include>
<include>${staging.dir}/js/nf/nf-dialog.js</include>
<include>${staging.dir}/js/nf/nf-storage.js</include>
+ <include>${staging.dir}/js/nf/nf-ajax-setup.js</include>
<include>${staging.dir}/js/nf/nf-processor-details.js</include>
<include>${staging.dir}/js/nf/nf-connection-details.js</include>
<include>${staging.dir}/js/nf/summary/nf-summary.js</include>
@@ -379,6 +383,7 @@
<include>${staging.dir}/js/nf/nf-universal-capture.js</include>
<include>${staging.dir}/js/nf/nf-dialog.js</include>
<include>${staging.dir}/js/nf/nf-storage.js</include>
+ <include>${staging.dir}/js/nf/nf-ajax-setup.js</include>
<include>${staging.dir}/js/nf/counters/nf-counters.js</include>
<include>${staging.dir}/js/nf/counters/nf-counters-table.js</include>
</includes>
@@ -392,6 +397,7 @@
<include>${staging.dir}/js/nf/nf-universal-capture.js</include>
<include>${staging.dir}/js/nf/nf-dialog.js</include>
<include>${staging.dir}/js/nf/nf-storage.js</include>
+ <include>${staging.dir}/js/nf/nf-ajax-setup.js</include>
<include>${staging.dir}/js/nf/templates/nf-templates.js</include>
<include>${staging.dir}/js/nf/templates/nf-templates-table.js</include>
</includes>
@@ -405,6 +411,7 @@
<include>${staging.dir}/js/nf/nf-universal-capture.js</include>
<include>${staging.dir}/js/nf/nf-dialog.js</include>
<include>${staging.dir}/js/nf/nf-storage.js</include>
+ <include>${staging.dir}/js/nf/nf-ajax-setup.js</include>
<include>${staging.dir}/js/nf/cluster/nf-cluster.js</include>
<include>${staging.dir}/js/nf/cluster/nf-cluster-table.js</include>
</includes>
@@ -418,6 +425,7 @@
<include>${staging.dir}/js/nf/nf-universal-capture.js</include>
<include>${staging.dir}/js/nf/nf-dialog.js</include>
<include>${staging.dir}/js/nf/nf-storage.js</include>
+ <include>${staging.dir}/js/nf/nf-ajax-setup.js</include>
<include>${staging.dir}/js/nf/users/nf-users.js</include>
<include>${staging.dir}/js/nf/users/nf-users-table.js</include>
</includes>
@@ -431,6 +439,7 @@
<include>${staging.dir}/js/nf/nf-universal-capture.js</include>
<include>${staging.dir}/js/nf/nf-dialog.js</include>
<include>${staging.dir}/js/nf/nf-storage.js</include>
+ <include>${staging.dir}/js/nf/nf-ajax-setup.js</include>
<include>${staging.dir}/js/nf/bulletin-board/nf-bulletin-board.js</include>
</includes>
</aggregation>
@@ -443,6 +452,7 @@
<include>${staging.dir}/js/nf/nf-universal-capture.js</include>
<include>${staging.dir}/js/nf/nf-dialog.js</include>
<include>${staging.dir}/js/nf/nf-storage.js</include>
+ <include>${staging.dir}/js/nf/nf-ajax-setup.js</include>
<include>${staging.dir}/js/nf/login/nf-login.js</include>
</includes>
</aggregation>
@@ -622,6 +632,10 @@
js/nf/nf-namespace.js.gz,
js/nf/nf-universal-capture.js,
js/nf/nf-universal-capture.js.gz,
+ js/nf/nf-storage.js,
+ js/nf/nf-storage.js.gz,
+ js/nf/nf-ajax-setup.js,
+ js/nf/nf-ajax-setup.js.gz,
js/nf/nf-status-history.js,
js/nf/nf-status-history.js.gz,
js/nf/canvas/nf-canvas-all.js,
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties
index 995b0a8..7fe8584 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/bulletin-board.properties
@@ -18,6 +18,7 @@ nf.bulletin.board.script.tags=<script type="text/javascript" src="js/nf/nf-names
<script type="text/javascript" src="js/nf/nf-universal-capture.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-ajax-setup.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/bulletin-board/nf-bulletin-board.js?${project.version}"></script>
nf.bulletin.board.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\
<link rel="stylesheet" href="css/main.css?${project.version}" type="text/css" />\n\
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
index e626859..5443907 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
@@ -21,6 +21,7 @@ nf.canvas.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?
<script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-shell.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-ajax-setup.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-snippet.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-queue-listing.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-component-state.js?${project.version}"></script>\n\
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties
index 183ec6a..0fb872c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/cluster.properties
@@ -18,6 +18,7 @@ nf.cluster.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js
<script type="text/javascript" src="js/nf/nf-universal-capture.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-ajax-setup.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/cluster/nf-cluster.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/cluster/nf-cluster-table.js?${project.version}"></script>
nf.cluster.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties
index cfa7f36..4c947b4 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/counters.properties
@@ -18,6 +18,7 @@ nf.counters.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.j
<script type="text/javascript" src="js/nf/nf-universal-capture.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-ajax-setup.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/counters/nf-counters.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/counters/nf-counters-table.js?${project.version}"></script>
nf.counters.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties
index 0850127..6cee206 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/history.properties
@@ -18,6 +18,7 @@ nf.history.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js
<script type="text/javascript" src="js/nf/nf-universal-capture.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-ajax-setup.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/history/nf-history.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/history/nf-history-table.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/history/nf-history-model.js?${project.version}"></script>
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login.properties
index 7a65ac6..7a1678d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/login.properties
@@ -17,6 +17,7 @@ nf.login.script.tags=<script type="text/javascript" src="js/nf/nf-common.js?${pr
<script type="text/javascript" src="js/nf/nf-universal-capture.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-ajax-setup.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/login/nf-login.js?${project.version}"></script>
nf.login.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\
<link rel="stylesheet" href="css/main.css?${project.version}" type="text/css" />\n\
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties
index 8d825cb..dee70b8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/provenance.properties
@@ -18,6 +18,7 @@ nf.provenance.script.tags=<script type="text/javascript" src="js/nf/nf-namespace
<script type="text/javascript" src="js/nf/nf-universal-capture.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-ajax-setup.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/provenance/nf-provenance.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/provenance/nf-provenance-table.js?${project.version}"></script>
nf.provenance.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties
index b7a26ba..752ca68 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/summary.properties
@@ -18,6 +18,7 @@ nf.summary.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js
<script type="text/javascript" src="js/nf/nf-universal-capture.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-ajax-setup.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-processor-details.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-connection-details.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/summary/nf-summary.js?${project.version}"></script>\n\
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties
index a9299fc..c0cee4c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/templates.properties
@@ -18,6 +18,7 @@ nf.templates.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.
<script type="text/javascript" src="js/nf/nf-universal-capture.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-ajax-setup.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/templates/nf-templates.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/templates/nf-templates-table.js?${project.version}"></script>
nf.templates.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties
index 160cb49..0b37363 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/users.properties
@@ -18,6 +18,7 @@ nf.users.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?$
<script type="text/javascript" src="js/nf/nf-universal-capture.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-dialog.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/nf-storage.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/nf-ajax-setup.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/users/nf-users.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/users/nf-users-table.js?${project.version}"></script>
nf.users.style.tags=<link rel="stylesheet" href="css/reset.css?${project.version}" type="text/css" />\n\
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
index 15eda10..0a9cf28 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
@@ -125,7 +125,7 @@ nf.CanvasUtils = (function () {
}
}
},
-
+
/**
* Calculates the point on the specified bounding box that is closest to the
* specified point.
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-ui.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-ui.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-ui.js
index 365831a..469dcba 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-ui.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-ui.js
@@ -26,22 +26,38 @@ nf.CustomUi = {
* @argument {boolean} editable Whether the custom ui should support editing
*/
showCustomUi: function (id, uri, editable) {
+ return $.Deferred(function (deferred) {
+ nf.Common.getAccessToken('../nifi-api/access/ui-extension-token').done(function (uiExtensionToken) {
+ // record the processor id
+ $('#shell-close-button');
- // record the processor id
- $('#shell-close-button');
+ var revision = nf.Client.getRevision();
- var revision = nf.Client.getRevision();
+ // build the customer ui params
+ var customUiParams = {
+ 'id': id,
+ 'processorId': id, // deprecated
+ 'revision': revision.version,
+ 'clientId': revision.clientId,
+ 'editable': editable
+ };
- // build the customer ui params
- var customUiParams = {
- 'id': id,
- 'processorId': id, // deprecated
- 'revision': revision.version,
- 'clientId': revision.clientId,
- 'editable': editable
- };
+ // conditionally include the ui extension token
+ if (!nf.Common.isBlank(uiExtensionToken)) {
+ customUiParams['access_token'] = uiExtensionToken;
+ }
- // show the shell
- return nf.Shell.showPage('..' + uri + '?' + $.param(customUiParams), false);
+ // show the shell
+ nf.Shell.showPage('..' + uri + '?' + $.param(customUiParams), false).done(function () {
+ deferred.resolve();
+ });
+ }).fail(function () {
+ nf.Dialog.showOkDialog({
+ dialogContent: 'Unable to generate access token for accessing the advanced configuration dialog.',
+ overlayBackground: false
+ });
+ deferred.resolve();
+ });
+ }).promise();
}
};
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js
index ef88c70..76d368e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js
@@ -23,6 +23,16 @@
nf.QueueListing = (function () {
/**
+ * Configuration object used to hold a number of configuration items.
+ */
+ var config = {
+ urls: {
+ uiExtensionToken: '../nifi-api/access/ui-extension-token',
+ downloadToken: '../nifi-api/access/download-token'
+ }
+ };
+
+ /**
* Initializes the listing request status dialog.
*/
var initializeListingRequestStatusDialog = function () {
@@ -57,15 +67,33 @@ nf.QueueListing = (function () {
var downloadContent = function () {
var dataUri = $('#flowfile-uri').text() + '/content';
- // conditionally include the cluster node id
- var clusterNodeId = $('#flowfile-cluster-node-id').text();
- if (!nf.Common.isBlank(clusterNodeId)) {
- window.open(dataUri + '?' + $.param({
- 'clusterNodeId': clusterNodeId
- }));
- } else {
- window.open(dataUri);
- }
+ // perform the request once we've received a token
+ nf.Common.getAccessToken(config.urls.downloadToken).done(function (downloadToken) {
+ var parameters = {};
+
+ // conditionally include the ui extension token
+ if (!nf.Common.isBlank(downloadToken)) {
+ parameters['access_token'] = downloadToken;
+ }
+
+ // conditionally include the cluster node id
+ var clusterNodeId = $('#flowfile-cluster-node-id').text();
+ if (!nf.Common.isBlank(clusterNodeId)) {
+ parameters['clusterNodeId'] = clusterNodeId;
+ }
+
+ // open the url
+ if ($.isEmptyObject(parameters)) {
+ window.open(dataUri);
+ } else {
+ window.open(dataUri + '?' + $.param(parameters));
+ }
+ }).fail(function () {
+ nf.Dialog.showOkDialog({
+ dialogContent: 'Unable to generate access token for downloading content.',
+ overlayBackground: false
+ });
+ });
};
/**
@@ -74,32 +102,80 @@ nf.QueueListing = (function () {
var viewContent = function () {
var dataUri = $('#flowfile-uri').text() + '/content';
- // conditionally include the cluster node id
- var clusterNodeId = $('#flowfile-cluster-node-id').text();
- if (!nf.Common.isBlank(clusterNodeId)) {
- var parameters = {
- 'clusterNodeId': clusterNodeId
- };
+ // generate tokens as necessary
+ var getAccessTokens = $.Deferred(function (deferred) {
+ if (nf.Storage.hasItem('jwt')) {
+ // generate a token for the ui extension and another for the callback
+ var uiExtensionToken = $.ajax({
+ type: 'POST',
+ url: config.urls.uiExtensionToken
+ });
+ var downloadToken = $.ajax({
+ type: 'POST',
+ url: config.urls.downloadToken
+ });
- dataUri = dataUri + '?' + $.param(parameters);
- }
+ // wait for each token
+ $.when(uiExtensionToken, downloadToken).done(function (uiExtensionTokenResult, downloadTokenResult) {
+ var uiExtensionToken = uiExtensionTokenResult[0];
+ var downloadToken = downloadTokenResult[0];
+ deferred.resolve(uiExtensionToken, downloadToken);
+ }).fail(function () {
+ nf.Dialog.showOkDialog({
+ dialogContent: 'Unable to generate access token for viewing content.',
+ overlayBackground: false
+ });
+ deferred.reject();
+ });
+ } else {
+ deferred.resolve('', '');
+ }
+ }).promise();
- // open the content viewer
- var contentViewerUrl = $('#nifi-content-viewer-url').text();
+ // perform the request after we've received the tokens
+ getAccessTokens.done(function (uiExtensionToken, downloadToken) {
+ var dataUriParameters = {};
- // if there's already a query string don't add another ?... this assumes valid
- // input meaning that if the url has already included a ? it also contains at
- // least one query parameter
- if (contentViewerUrl.indexOf('?') === -1) {
- contentViewerUrl += '?';
- } else {
- contentViewerUrl += '&';
- }
+ // conditionally include the cluster node id
+ var clusterNodeId = $('#flowfile-cluster-node-id').text();
+ if (!nf.Common.isBlank(clusterNodeId)) {
+ dataUriParameters['clusterNodeId'] = clusterNodeId;
+ }
+
+ // include the download token if applicable
+ if (!nf.Common.isBlank(downloadToken)) {
+ dataUriParameters['access_token'] = downloadToken;
+ }
+
+ // include parameters if necessary
+ if ($.isEmptyObject(dataUriParameters) === false) {
+ dataUri = dataUri + '?' + $.param(dataUriParameters);
+ }
+
+ // open the content viewer
+ var contentViewerUrl = $('#nifi-content-viewer-url').text();
+
+ // if there's already a query string don't add another ?... this assumes valid
+ // input meaning that if the url has already included a ? it also contains at
+ // least one query parameter
+ if (contentViewerUrl.indexOf('?') === -1) {
+ contentViewerUrl += '?';
+ } else {
+ contentViewerUrl += '&';
+ }
- // open the content viewer
- window.open(contentViewerUrl + $.param({
+ var contentViewerParameters = {
'ref': dataUri
- }));
+ };
+
+ // include the download token if applicable
+ if (!nf.Common.isBlank(uiExtensionToken)) {
+ contentViewerParameters['access_token'] = uiExtensionToken;
+ }
+
+ // open the content viewer
+ window.open(contentViewerUrl + $.param(contentViewerParameters));
+ });
};
/**
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-ajax-setup.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-ajax-setup.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-ajax-setup.js
new file mode 100644
index 0000000..a041207
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-ajax-setup.js
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+/**
+ * Performs ajax setup for use within NiFi.
+ */
+$(document).ready(function () {
+ // include jwt when possible
+ $.ajaxSetup({
+ 'beforeSend': function(xhr) {
+ var hadToken = nf.Storage.hasItem('jwt');
+
+ // get the token to include in all requests
+ var token = nf.Storage.getItem('jwt');
+ if (token !== null) {
+ xhr.setRequestHeader('Authorization', 'Bearer ' + token);
+ } else {
+ // if the current user was logged in with a token and the token just expired, cancel the request
+ if (hadToken === true) {
+ return false;
+ }
+ }
+ }
+ });
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
index bf88a09..7bf401e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
@@ -51,24 +51,6 @@ $(document).ready(function () {
$('div.loading-container').removeClass('ajax-loading');
});
- // include jwt when possible
- $.ajaxSetup({
- 'beforeSend': function(xhr) {
- var hadToken = nf.Storage.hasItem('jwt');
-
- // get the token to include in all requests
- var token = nf.Storage.getItem('jwt');
- if (token !== null) {
- xhr.setRequestHeader('Authorization', 'Bearer ' + token);
- } else {
- // if the current user was logged in with a token and the token just expired, reload
- if (hadToken === true) {
- return false;
- }
- }
- }
- });
-
// initialize the tooltips
$('img.setting-icon').qtip(nf.Common.config.tooltipConfig);
@@ -768,6 +750,29 @@ nf.Common = (function () {
},
/**
+ * Gets an access token from the specified url.
+ *
+ * @param accessTokenUrl The access token
+ * @returns the access token as a deferred
+ */
+ getAccessToken: function (accessTokenUrl) {
+ return $.Deferred(function (deferred) {
+ if (nf.Storage.hasItem('jwt')) {
+ $.ajax({
+ type: 'POST',
+ url: accessTokenUrl
+ }).done(function (token) {
+ deferred.resolve(token);
+ }).fail(function () {
+ deferred.reject();
+ })
+ } else {
+ deferred.resolve('');
+ }
+ }).promise();
+ },
+
+ /**
* Constants for time duration formatting.
*/
MILLIS_PER_DAY: 86400000,
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js
index 7b0c3f6..efb90fe 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-storage.js
@@ -20,7 +20,20 @@
nf.Storage = (function () {
// Store items for two days before being eligible for removal.
- var TWO_DAYS = nf.Common.MILLIS_PER_DAY * 2;
+ var MILLIS_PER_DAY = 86400000;
+ var TWO_DAYS = MILLIS_PER_DAY * 2;
+
+ var isUndefined = function (obj) {
+ return typeof obj === 'undefined';
+ };
+
+ var isNull = function (obj) {
+ return obj === null;
+ };
+
+ var isDefinedAndNotNull = function (obj) {
+ return !isUndefined(obj) && !isNull(obj);
+ };
/**
* Checks the expiration for the specified entry.
@@ -29,7 +42,7 @@ nf.Storage = (function () {
* @returns {boolean}
*/
var checkExpiration = function (entry) {
- if (nf.Common.isDefinedAndNotNull(entry.expires)) {
+ if (isDefinedAndNotNull(entry.expires)) {
// get the expiration
var expires = new Date(entry.expires);
var now = new Date();
@@ -52,7 +65,7 @@ nf.Storage = (function () {
var entry = JSON.parse(localStorage.getItem(key));
// ensure the entry and item are present
- if (nf.Common.isDefinedAndNotNull(entry)) {
+ if (isDefinedAndNotNull(entry)) {
return entry;
} else {
return null;
@@ -89,7 +102,7 @@ nf.Storage = (function () {
*/
setItem: function (key, item, expires) {
// calculate the expiration
- expires = nf.Common.isDefinedAndNotNull(expires) ? expires : new Date().valueOf() + TWO_DAYS;
+ expires = isDefinedAndNotNull(expires) ? expires : new Date().valueOf() + TWO_DAYS;
// create the entry
var entry = {
@@ -132,7 +145,7 @@ nf.Storage = (function () {
}
// if the entry has the specified field return its value
- if (nf.Common.isDefinedAndNotNull(entry['item'])) {
+ if (isDefinedAndNotNull(entry['item'])) {
return entry['item'];
} else {
return null;
@@ -153,7 +166,7 @@ nf.Storage = (function () {
}
// if the entry has the specified field return its value
- if (nf.Common.isDefinedAndNotNull(entry['expires'])) {
+ if (isDefinedAndNotNull(entry['expires'])) {
return entry['expires'];
} else {
return null;
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
index cdad348..a977e08 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
@@ -37,7 +37,9 @@ nf.ProvenanceTable = (function () {
provenance: '../nifi-api/controller/provenance',
cluster: '../nifi-api/cluster',
d3Script: 'js/d3/d3.min.js',
- lineageScript: 'js/nf/provenance/nf-provenance-lineage.js'
+ lineageScript: 'js/nf/provenance/nf-provenance-lineage.js',
+ uiExtensionToken: '../nifi-api/access/ui-extension-token',
+ downloadToken: '../nifi-api/access/download-token'
}
};
@@ -78,17 +80,35 @@ nf.ProvenanceTable = (function () {
var eventId = $('#provenance-event-id').text();
// build the url
- var url = config.urls.provenance + '/events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
-
- // conditionally include the cluster node id
- var clusterNodeId = $('#provenance-event-cluster-node-id').text();
- if (!nf.Common.isBlank(clusterNodeId)) {
- window.open(url + '?' + $.param({
- 'clusterNodeId': clusterNodeId
- }));
- } else {
- window.open(url);
- }
+ var dataUri = config.urls.provenance + '/events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
+
+ // perform the request once we've received a token
+ nf.Common.getAccessToken(config.urls.downloadToken).done(function (downloadToken) {
+ var parameters = {};
+
+ // conditionally include the ui extension token
+ if (!nf.Common.isBlank(downloadToken)) {
+ parameters['access_token'] = downloadToken;
+ }
+
+ // conditionally include the cluster node id
+ var clusterNodeId = $('#provenance-event-cluster-node-id').text();
+ if (!nf.Common.isBlank(clusterNodeId)) {
+ parameters['clusterNodeId'] = clusterNodeId;
+ }
+
+ // open the url
+ if ($.isEmptyObject(parameters)) {
+ window.open(dataUri);
+ } else {
+ window.open(dataUri + '?' + $.param(parameters));
+ }
+ }).fail(function () {
+ nf.Dialog.showOkDialog({
+ dialogContent: 'Unable to generate access token for downloading content.',
+ overlayBackground: false
+ });
+ });
};
/**
@@ -103,32 +123,80 @@ nf.ProvenanceTable = (function () {
// build the uri to the data
var dataUri = controllerUri + '/provenance/events/' + encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
- // conditionally include the cluster node id
- var clusterNodeId = $('#provenance-event-cluster-node-id').text();
- if (!nf.Common.isBlank(clusterNodeId)) {
- var parameters = {
- 'clusterNodeId': clusterNodeId
- };
+ // generate tokens as necessary
+ var getAccessTokens = $.Deferred(function (deferred) {
+ if (nf.Storage.hasItem('jwt')) {
+ // generate a token for the ui extension and another for the callback
+ var uiExtensionToken = $.ajax({
+ type: 'POST',
+ url: config.urls.uiExtensionToken
+ });
+ var downloadToken = $.ajax({
+ type: 'POST',
+ url: config.urls.downloadToken
+ });
- dataUri = dataUri + '?' + $.param(parameters);
- }
+ // wait for each token
+ $.when(uiExtensionToken, downloadToken).done(function (uiExtensionTokenResult, downloadTokenResult) {
+ var uiExtensionToken = uiExtensionTokenResult[0];
+ var downloadToken = downloadTokenResult[0];
+ deferred.resolve(uiExtensionToken, downloadToken);
+ }).fail(function () {
+ nf.Dialog.showOkDialog({
+ dialogContent: 'Unable to generate access token for viewing content.',
+ overlayBackground: false
+ });
+ deferred.reject();
+ });
+ } else {
+ deferred.resolve('', '');
+ }
+ }).promise();
- // open the content viewer
- var contentViewerUrl = $('#nifi-content-viewer-url').text();
+ // perform the request after we've received the tokens
+ getAccessTokens.done(function (uiExtensionToken, downloadToken) {
+ var dataUriParameters = {};
- // if there's already a query string don't add another ?... this assumes valid
- // input meaning that if the url has already included a ? it also contains at
- // least one query parameter
- if (contentViewerUrl.indexOf('?') === -1) {
- contentViewerUrl += '?';
- } else {
- contentViewerUrl += '&';
- }
+ // conditionally include the cluster node id
+ var clusterNodeId = $('#provenance-event-cluster-node-id').text();
+ if (!nf.Common.isBlank(clusterNodeId)) {
+ dataUriParameters['clusterNodeId'] = clusterNodeId;
+ }
+
+ // include the download token if applicable
+ if (!nf.Common.isBlank(downloadToken)) {
+ dataUriParameters['access_token'] = downloadToken;
+ }
- // open the content viewer
- window.open(contentViewerUrl + $.param({
- 'ref': dataUri
- }));
+ // include parameters if necessary
+ if ($.isEmptyObject(dataUriParameters) === false) {
+ dataUri = dataUri + '?' + $.param(dataUriParameters);
+ }
+
+ // open the content viewer
+ var contentViewerUrl = $('#nifi-content-viewer-url').text();
+
+ // if there's already a query string don't add another ?... this assumes valid
+ // input meaning that if the url has already included a ? it also contains at
+ // least one query parameter
+ if (contentViewerUrl.indexOf('?') === -1) {
+ contentViewerUrl += '?';
+ } else {
+ contentViewerUrl += '&';
+ }
+
+ var contentViewerParameters = {
+ 'ref': dataUri
+ };
+
+ // include the download token if applicable
+ if (!nf.Common.isBlank(uiExtensionToken)) {
+ contentViewerParameters['access_token'] = uiExtensionToken;
+ }
+
+ // open the content viewer
+ window.open(contentViewerUrl + $.param(contentViewerParameters));
+ });
};
/**
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js
index 9ec742f..0f9ea8a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js
@@ -28,7 +28,8 @@ nf.TemplatesTable = (function () {
filterList: 'templates-filter-list'
},
urls: {
- templates: '../nifi-api/controller/templates'
+ templates: '../nifi-api/controller/templates',
+ downloadToken: '../nifi-api/access/download-token'
}
};
@@ -150,6 +151,34 @@ nf.TemplatesTable = (function () {
return item[args.property].search(filterExp) >= 0;
};
+ /**
+ * Downloads the specified template.
+ *
+ * @param {object} template The template
+ */
+ var downloadTemplate = function (template) {
+ nf.Common.getAccessToken(config.urls.downloadToken).done(function (downloadToken) {
+ var parameters = {};
+
+ // conditionally include the download token
+ if (!nf.Common.isBlank(downloadToken)) {
+ parameters['access_token'] = downloadToken;
+ }
+
+ // open the url
+ if ($.isEmptyObject(parameters)) {
+ window.open(config.urls.templates + '/' + encodeURIComponent(template.id));
+ } else {
+ window.open(config.urls.templates + '/' + encodeURIComponent(template.id) + '?' + $.param(parameters));
+ }
+ }).fail(function () {
+ nf.Dialog.showOkDialog({
+ dialogContent: 'Unable to generate access token for downloading content.',
+ overlayBackground: false
+ });
+ });
+ };
+
return {
/**
* Initializes the templates list.
@@ -262,7 +291,7 @@ nf.TemplatesTable = (function () {
// determine the desired action
if (templatesGrid.getColumns()[args.cell].id === 'actions') {
if (target.hasClass('export-template')) {
- window.open(config.urls.templates + '/' + encodeURIComponent(item.id));
+ downloadTemplate(item);
} else if (target.hasClass('prompt-to-delete-template')) {
promptToDeleteTemplate(item);
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/java/org/apache/nifi/update/attributes/api/RuleResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/java/org/apache/nifi/update/attributes/api/RuleResource.java b/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/java/org/apache/nifi/update/attributes/api/RuleResource.java
index f68227a..b211b72 100644
--- a/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/java/org/apache/nifi/update/attributes/api/RuleResource.java
+++ b/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/java/org/apache/nifi/update/attributes/api/RuleResource.java
@@ -148,7 +148,7 @@ public class RuleResource {
try {
criteria.reorder(requestEntity.getRuleOrder());
} catch (final IllegalArgumentException iae) {
- throw new WebApplicationException(badRequest(iae.getMessage()));
+ throw new WebApplicationException(iae, badRequest(iae.getMessage()));
}
}
@@ -157,7 +157,7 @@ public class RuleResource {
try {
criteria.setFlowFilePolicy(FlowFilePolicy.valueOf(requestEntity.getFlowFilePolicy()));
} catch (final IllegalArgumentException iae) {
- throw new WebApplicationException(badRequest("The specified matching strategy is unknown: " + requestEntity.getFlowFilePolicy()));
+ throw new WebApplicationException(iae, badRequest("The specified matching strategy is unknown: " + requestEntity.getFlowFilePolicy()));
}
}
@@ -227,7 +227,7 @@ public class RuleResource {
rule = factory.createRule(ruleDto);
rule.setId(uuid);
} catch (final IllegalArgumentException iae) {
- throw new WebApplicationException(badRequest(iae.getMessage()));
+ throw new WebApplicationException(iae, badRequest(iae.getMessage()));
}
// add the rule
@@ -268,7 +268,7 @@ public class RuleResource {
condition = factory.createCondition(requestEntity.getCondition());
condition.setId(uuid);
} catch (final IllegalArgumentException iae) {
- throw new WebApplicationException(badRequest(iae.getMessage()));
+ throw new WebApplicationException(iae, badRequest(iae.getMessage()));
}
// build the response
@@ -302,7 +302,7 @@ public class RuleResource {
action = factory.createAction(requestEntity.getAction());
action.setId(uuid);
} catch (final IllegalArgumentException iae) {
- throw new WebApplicationException(badRequest(iae.getMessage()));
+ throw new WebApplicationException(iae, badRequest(iae.getMessage()));
}
// build the response
@@ -522,7 +522,7 @@ public class RuleResource {
rule.setConditions(conditions);
rule.setActions(actions);
} catch (final IllegalArgumentException iae) {
- throw new WebApplicationException(badRequest(iae.getMessage()));
+ throw new WebApplicationException(iae, badRequest(iae.getMessage()));
}
// add the new rule if application
@@ -598,11 +598,11 @@ public class RuleResource {
// load the processor configuration
processorDetails = configurationContext.getComponentDetails(requestContext);
} catch (final InvalidRevisionException ire) {
- throw new WebApplicationException(invalidRevision(ire.getMessage()));
+ throw new WebApplicationException(ire, invalidRevision(ire.getMessage()));
} catch (final Exception e) {
final String message = String.format("Unable to get UpdateAttribute[id=%s] criteria: %s", requestContext.getId(), e);
logger.error(message, e);
- throw new WebApplicationException(error(message));
+ throw new WebApplicationException(e, error(message));
}
Criteria criteria = null;
@@ -612,7 +612,7 @@ public class RuleResource {
} catch (final IllegalArgumentException iae) {
final String message = String.format("Unable to deserialize existing rules for UpdateAttribute[id=%s]. Deserialization error: %s", requestContext.getId(), iae);
logger.error(message, iae);
- throw new WebApplicationException(error(message));
+ throw new WebApplicationException(iae, error(message));
}
}
// ensure the criteria isn't null
@@ -634,11 +634,11 @@ public class RuleResource {
// save the annotation data
configurationContext.setAnnotationData(requestContext, annotationData);
} catch (final InvalidRevisionException ire) {
- throw new WebApplicationException(invalidRevision(ire.getMessage()));
+ throw new WebApplicationException(ire, invalidRevision(ire.getMessage()));
} catch (final Exception e) {
final String message = String.format("Unable to save UpdateAttribute[id=%s] criteria: %s", requestContext.getId(), e);
logger.error(message, e);
- throw new WebApplicationException(error(message));
+ throw new WebApplicationException(e, error(message));
}
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp b/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
index a7f93f1..a92c4e0 100644
--- a/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
+++ b/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
@@ -53,6 +53,9 @@
<script type="text/javascript" src="../nifi/js/jquery/slickgrid/slick.grid.js"></script>
<script type="text/javascript" src="../nifi/js/codemirror/lib/codemirror-compressed.js"></script>
<script type="text/javascript" src="../nifi/js/nf/nf-namespace.js"></script>
+ <script type="text/javascript" src="../nifi/js/nf/nf-common.js"></script>
+ <script type="text/javascript" src="../nifi/js/nf/nf-storage.js"></script>
+ <script type="text/javascript" src="../nifi/js/nf/nf-ajax-setup.js"></script>
<script type="text/javascript" src="../nifi/js/nf/nf-universal-capture.js"></script>
<script type="text/javascript" src="../nifi/js/jquery/nfeditor/languages/nfel.js"></script>
<script type="text/javascript" src="../nifi/js/jquery/nfeditor/jquery.nfeditor.js"></script>
[2/2] nifi git commit: NIFI-1497: - Introducing a one time use
password service for use in query parameters when accessing UI extensions and
downloading resources. - Using one time use tokens when accessing ui
extensions and downloading resources. - Ensu
Posted by bb...@apache.org.
NIFI-1497: - Introducing a one time use password service for use in query parameters when accessing UI extensions and downloading resources. - Using one time use tokens when accessing ui extensions and downloading resources. - Ensuring appropriate roles when accessing component details through the web context for custom UIs. - Addressing typo in class name. - Ensuring appropriate roles when accessing content through the content access. - Code clean up. - Refactoring some basic scripts for accessing JWT tokens so UI extensions can reuse common functionality.
Signed-off-by: Bryan Bende <bb...@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/a8edab2e
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/a8edab2e
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/a8edab2e
Branch: refs/heads/master
Commit: a8edab2e7955b175c5cbd2a3266fb51fa9712aee
Parents: 86ab442
Author: Matt Gilman <ma...@gmail.com>
Authored: Fri Feb 19 10:50:31 2016 -0500
Committer: Bryan Bende <bb...@apache.org>
Committed: Fri Feb 19 10:54:53 2016 -0500
----------------------------------------------------------------------
.../web/NiFiWebApiSecurityConfiguration.java | 35 ++-
.../nifi/web/StandardNiFiContentAccess.java | 70 ++++--
.../StandardNiFiWebConfigurationContext.java | 4 +
.../apache/nifi/web/StandardNiFiWebContext.java | 4 +
.../org/apache/nifi/web/api/AccessResource.java | 122 ++++++++++-
.../src/main/resources/nifi-web-api-context.xml | 1 +
.../accesscontrol/AccessTokenEndpointTest.java | 2 +-
.../nifi/web/ContentViewerController.java | 40 +++-
.../src/main/webapp/WEB-INF/jsp/header.jsp | 64 +++++-
.../src/main/webapp/css/main.css | 8 +
.../web/security/NiFiAuthenticationFilter.java | 6 +-
.../security/NiFiAuthenticationProvider.java | 10 +-
.../anonymous/NiFiAnonymousUserFilter.java | 7 +-
.../authorization/NiFiAuthorizationService.java | 6 +-
.../security/jwt/JwtAuthenticationFilter.java | 6 +-
.../otp/OtpAuthenticationException.java | 34 +++
.../security/otp/OtpAuthenticationFilter.java | 96 ++++++++
.../nifi/web/security/otp/OtpService.java | 219 +++++++++++++++++++
.../NewAccountAuthorizationRequestToken.java | 2 +-
.../token/NiFiAuthorizationRequestToken.java | 54 +++++
.../token/NiFiAuthortizationRequestToken.java | 54 -----
.../security/token/OtpAuthenticationToken.java | 56 +++++
.../security/x509/X509AuthenticationFilter.java | 6 +-
.../resources/nifi-web-security-context.xml | 5 +-
.../NiFiAuthorizationServiceTest.java | 6 +-
.../otp/OtpAuthenticationFilterTest.java | 210 ++++++++++++++++++
.../nifi/web/security/otp/OtpServiceTest.java | 149 +++++++++++++
.../nifi-framework/nifi-web/nifi-web-ui/pom.xml | 14 ++
.../resources/filters/bulletin-board.properties | 1 +
.../main/resources/filters/canvas.properties | 1 +
.../main/resources/filters/cluster.properties | 1 +
.../main/resources/filters/counters.properties | 1 +
.../main/resources/filters/history.properties | 1 +
.../src/main/resources/filters/login.properties | 1 +
.../resources/filters/provenance.properties | 1 +
.../main/resources/filters/summary.properties | 1 +
.../main/resources/filters/templates.properties | 1 +
.../src/main/resources/filters/users.properties | 1 +
.../main/webapp/js/nf/canvas/nf-canvas-utils.js | 2 +-
.../main/webapp/js/nf/canvas/nf-custom-ui.js | 42 ++--
.../webapp/js/nf/canvas/nf-queue-listing.js | 136 +++++++++---
.../src/main/webapp/js/nf/nf-ajax-setup.js | 39 ++++
.../src/main/webapp/js/nf/nf-common.js | 41 ++--
.../src/main/webapp/js/nf/nf-storage.js | 25 ++-
.../js/nf/provenance/nf-provenance-table.js | 136 +++++++++---
.../js/nf/templates/nf-templates-table.js | 33 ++-
.../update/attributes/api/RuleResource.java | 22 +-
.../src/main/webapp/WEB-INF/jsp/worksheet.jsp | 3 +
48 files changed, 1550 insertions(+), 229 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
index 1488aba..64ecc5f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java
@@ -24,7 +24,9 @@ import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter;
import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
import org.apache.nifi.web.security.jwt.JwtService;
import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter;
-import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
+import org.apache.nifi.web.security.otp.OtpAuthenticationFilter;
+import org.apache.nifi.web.security.otp.OtpService;
+import org.apache.nifi.web.security.token.NiFiAuthorizationRequestToken;
import org.apache.nifi.web.security.x509.X509AuthenticationFilter;
import org.apache.nifi.web.security.x509.X509CertificateExtractor;
import org.apache.nifi.web.security.x509.X509IdentityProvider;
@@ -54,12 +56,14 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
private UserService userService;
private AuthenticationUserDetailsService userDetailsService;
private JwtService jwtService;
+ private OtpService otpService;
private X509CertificateExtractor certificateExtractor;
private X509IdentityProvider certificateIdentityProvider;
private LoginIdentityProvider loginIdentityProvider;
private NodeAuthorizedUserFilter nodeAuthorizedUserFilter;
private JwtAuthenticationFilter jwtAuthenticationFilter;
+ private OtpAuthenticationFilter otpAuthenticationFilter;
private X509AuthenticationFilter x509AuthenticationFilter;
private NiFiAnonymousUserFilter anonymousAuthenticationFilter;
@@ -69,9 +73,12 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
@Override
public void configure(WebSecurity webSecurity) throws Exception {
+ // ignore the access endpoints for obtaining the access config, the access token
+ // granting, and access status for a given user (note: we are not ignoring the
+ // the /access/download-token and /access/ui-extension-token endpoints
webSecurity
.ignoring()
- .antMatchers("/access/**");
+ .antMatchers("/access", "/access/config", "/access/token");
}
@Override
@@ -95,6 +102,9 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
// jwt
http.addFilterAfter(jwtFilterBean(), AnonymousAuthenticationFilter.class);
+
+ // otp
+ http.addFilterAfter(otpFilterBean(), AnonymousAuthenticationFilter.class);
}
@Bean
@@ -135,6 +145,20 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
return jwtAuthenticationFilter;
}
+ public OtpAuthenticationFilter otpFilterBean() throws Exception {
+ if (otpAuthenticationFilter == null) {
+ otpAuthenticationFilter = new OtpAuthenticationFilter();
+ otpAuthenticationFilter.setProperties(properties);
+ otpAuthenticationFilter.setAuthenticationManager(authenticationManager());
+
+ // only consider the tokens when configured for login
+ if (loginIdentityProvider != null) {
+ otpAuthenticationFilter.setOtpService(otpService);
+ }
+ }
+ return otpAuthenticationFilter;
+ }
+
@Bean
public X509AuthenticationFilter x509FilterBean() throws Exception {
if (x509AuthenticationFilter == null) {
@@ -157,7 +181,7 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
}
@Autowired
- public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthortizationRequestToken> userDetailsService) {
+ public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthorizationRequestToken> userDetailsService) {
this.userDetailsService = userDetailsService;
}
@@ -177,6 +201,11 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
}
@Autowired
+ public void setOtpService(OtpService otpService) {
+ this.otpService = otpService;
+ }
+
+ @Autowired
public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
this.loginIdentityProvider = loginIdentityProvider;
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
index f994c52..afaf3ed 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
@@ -17,19 +17,10 @@
package org.apache.nifi.web;
import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.ClientResponse.Status;
import com.sun.jersey.core.util.MultivaluedMapImpl;
-import java.io.Serializable;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.ws.rs.HttpMethod;
-import javax.ws.rs.core.MultivaluedMap;
import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authorization.Authority;
import org.apache.nifi.cluster.manager.NodeResponse;
import org.apache.nifi.cluster.manager.exception.UnknownNodeException;
import org.apache.nifi.cluster.manager.impl.WebClusterManager;
@@ -38,13 +29,26 @@ import org.apache.nifi.cluster.protocol.NodeIdentifier;
import org.apache.nifi.controller.repository.claim.ContentDirection;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.user.NiFiUserDetails;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
import org.apache.nifi.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.MultivaluedMap;
+import java.io.Serializable;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
*
*/
@@ -54,17 +58,16 @@ public class StandardNiFiContentAccess implements ContentAccess {
public static final String CLIENT_ID_PARAM = "clientId";
private static final Pattern FLOWFILE_CONTENT_URI_PATTERN = Pattern
- .compile("/controller/process-groups/((?:root)|(?:[a-f0-9\\-]{36}))/connections/([a-f0-9\\-]{36})/flowfiles/([a-f0-9\\-]{36})/content");
+ .compile("/controller/process-groups/((?:root)|(?:[a-f0-9\\-]{36}))/connections/([a-f0-9\\-]{36})/flowfiles/([a-f0-9\\-]{36})/content.*");
private static final Pattern PROVENANCE_CONTENT_URI_PATTERN = Pattern
- .compile("/controller/provenance/events/([0-9]+)/content/((?:input)|(?:output))");
+ .compile("/controller/provenance/events/([0-9]+)/content/((?:input)|(?:output)).*");
private NiFiProperties properties;
private NiFiServiceFacade serviceFacade;
private WebClusterManager clusterManager;
@Override
- @PreAuthorize("hasRole('ROLE_PROVENANCE')")
public DownloadableContent getContent(final ContentRequestContext request) {
// if clustered, send request to cluster manager
if (properties.isClusterManager()) {
@@ -99,6 +102,11 @@ public class StandardNiFiContentAccess implements ContentAccess {
}
}
+ // ensure we were able to detect the cluster node id
+ if (request.getClusterNodeId() == null) {
+ throw new IllegalArgumentException("Unable to determine the which node has the content.");
+ }
+
// get the target node and ensure it exists
final Node targetNode = clusterManager.getNode(request.getClusterNodeId());
if (targetNode == null) {
@@ -113,6 +121,16 @@ public class StandardNiFiContentAccess implements ContentAccess {
final ClientResponse clientResponse = nodeResponse.getClientResponse();
final MultivaluedMap<String, String> responseHeaders = clientResponse.getHeaders();
+ // ensure an appropriate response
+ if (Status.NOT_FOUND.getStatusCode() == clientResponse.getStatusInfo().getStatusCode()) {
+ throw new ResourceNotFoundException(clientResponse.getEntity(String.class));
+ } else if (Status.FORBIDDEN.getStatusCode() == clientResponse.getStatusInfo().getStatusCode()
+ || Status.UNAUTHORIZED.getStatusCode() == clientResponse.getStatusInfo().getStatusCode()) {
+ throw new AccessDeniedException(clientResponse.getEntity(String.class));
+ } else if (Status.OK.getStatusCode() != clientResponse.getStatusInfo().getStatusCode()) {
+ throw new IllegalStateException(clientResponse.getEntity(String.class));
+ }
+
// get the file name
final String contentDisposition = responseHeaders.getFirst("Content-Disposition");
final String filename = StringUtils.substringBetween(contentDisposition, "filename=\"", "\"");
@@ -140,7 +158,7 @@ public class StandardNiFiContentAccess implements ContentAccess {
final String connectionId = flowFileMatcher.group(2);
final String flowfileId = flowFileMatcher.group(3);
- return serviceFacade.getContent(groupId, connectionId, flowfileId, dataUri);
+ return getFlowFileContent(groupId, connectionId, flowfileId, dataUri);
}
// provenance event content
@@ -150,7 +168,7 @@ public class StandardNiFiContentAccess implements ContentAccess {
final Long eventId = Long.parseLong(provenanceMatcher.group(1));
final ContentDirection direction = ContentDirection.valueOf(provenanceMatcher.group(2).toUpperCase());
- return serviceFacade.getContent(eventId, dataUri, direction);
+ return getProvenanceEventContent(eventId, dataUri, direction);
} catch (final IllegalArgumentException iae) {
throw new IllegalArgumentException("The specified data reference URI is not valid.");
}
@@ -161,6 +179,24 @@ public class StandardNiFiContentAccess implements ContentAccess {
}
}
+ private DownloadableContent getFlowFileContent(final String groupId, final String connectionId, final String flowfileId, final String dataUri) {
+ // ensure the user is authorized as DFM - not checking with @PreAuthorized annotation as aspect not trigger on call within a class
+ if (!NiFiUserUtils.getAuthorities().contains(Authority.ROLE_DFM.toString())) {
+ throw new AccessDeniedException("Access is denied.");
+ }
+
+ return serviceFacade.getContent(groupId, connectionId, flowfileId, dataUri);
+ }
+
+ private DownloadableContent getProvenanceEventContent(final Long eventId, final String dataUri, final ContentDirection direction) {
+ // ensure the user is authorized as Provenance - not checking with @PreAuthorized annotation as aspect not trigger on call within a class
+ if (!NiFiUserUtils.getAuthorities().contains(Authority.ROLE_PROVENANCE.toString())) {
+ throw new AccessDeniedException("Access is denied.");
+ }
+
+ return serviceFacade.getContent(eventId, dataUri, direction);
+ }
+
public void setProperties(NiFiProperties properties) {
this.properties = properties;
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java
index 9502bf2..cae1175 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java
@@ -86,6 +86,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
private AuditService auditService;
@Override
+ @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public ControllerService getControllerService(String serviceIdentifier) {
return controllerServiceLookup.getControllerService(serviceIdentifier);
}
@@ -157,6 +158,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
}
@Override
+ @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public String getCurrentUserDn() {
String userIdentity = NiFiUser.ANONYMOUS_USER_IDENTITY;
@@ -169,6 +171,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
}
@Override
+ @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public String getCurrentUserName() {
String userName = NiFiUser.ANONYMOUS_USER_IDENTITY;
@@ -181,6 +184,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
}
@Override
+ @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public ComponentDetails getComponentDetails(final NiFiWebRequestContext requestContext) throws ResourceNotFoundException, ClusterRequestException {
final String id = requestContext.getId();
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
index 7e9e77e..9667ad6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
@@ -81,6 +81,7 @@ public class StandardNiFiWebContext implements NiFiWebContext {
private AuditService auditService;
@Override
+ @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public ControllerService getControllerService(String serviceIdentifier) {
return controllerServiceLookup.getControllerService(serviceIdentifier);
}
@@ -128,6 +129,7 @@ public class StandardNiFiWebContext implements NiFiWebContext {
}
@Override
+ @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public String getCurrentUserDn() {
String userIdentity = NiFiUser.ANONYMOUS_USER_IDENTITY;
@@ -140,6 +142,7 @@ public class StandardNiFiWebContext implements NiFiWebContext {
}
@Override
+ @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public String getCurrentUserName() {
String userName = NiFiUser.ANONYMOUS_USER_IDENTITY;
@@ -152,6 +155,7 @@ public class StandardNiFiWebContext implements NiFiWebContext {
}
@Override
+ @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
public ProcessorInfo getProcessor(final NiFiWebContextConfig config) throws ResourceNotFoundException, ClusterRequestException {
final Revision revision = config.getRevision();
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
index 7bf9690..24536fc 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java
@@ -24,6 +24,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import io.jsonwebtoken.JwtException;
+import org.apache.nifi.user.NiFiUser;
import org.apache.nifi.util.NiFiProperties;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
@@ -60,8 +61,11 @@ import org.apache.nifi.web.security.ProxiedEntitiesUtils;
import org.apache.nifi.web.security.UntrustedProxyException;
import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
import org.apache.nifi.web.security.jwt.JwtService;
+import org.apache.nifi.web.security.otp.OtpService;
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
-import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthorizationRequestToken;
+import org.apache.nifi.web.security.token.OtpAuthenticationToken;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
import org.apache.nifi.web.security.x509.X509CertificateExtractor;
import org.apache.nifi.web.security.x509.X509IdentityProvider;
import org.slf4j.Logger;
@@ -92,8 +96,9 @@ public class AccessResource extends ApplicationResource {
private X509CertificateExtractor certificateExtractor;
private X509IdentityProvider certificateIdentityProvider;
private JwtService jwtService;
+ private OtpService otpService;
- private AuthenticationUserDetailsService<NiFiAuthortizationRequestToken> userDetailsService;
+ private AuthenticationUserDetailsService<NiFiAuthorizationRequestToken> userDetailsService;
/**
* Retrieves the access configuration for this NiFi.
@@ -285,7 +290,107 @@ public class AccessResource extends ApplicationResource {
* @throws AuthenticationException if the proxy chain is not authorized
*/
private UserDetails checkAuthorization(final List<String> proxyChain) throws AuthenticationException {
- return userDetailsService.loadUserDetails(new NiFiAuthortizationRequestToken(proxyChain));
+ return userDetailsService.loadUserDetails(new NiFiAuthorizationRequestToken(proxyChain));
+ }
+
+ /**
+ * Creates a single use access token for downloading FlowFile content.
+ *
+ * @param httpServletRequest the servlet request
+ * @return A token (string)
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ @Produces(MediaType.TEXT_PLAIN)
+ @Path("/download-token")
+ @ApiOperation(
+ value = "Creates a single use access token for downloading FlowFile content.",
+ notes = "The token returned is a base64 encoded string. It is valid for a single request up to five minutes from being issued. " +
+ "It is used as a query parameter name 'access_token'.",
+ response = String.class
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = 403, message = "Client is not authorized to make this request."),
+ @ApiResponse(code = 409, message = "Unable to create the download token because NiFi is not in the appropriate state. " +
+ "(i.e. may not have any tokens to grant or be configured to support username/password login)"),
+ @ApiResponse(code = 500, message = "Unable to create download token because an unexpected error occurred.")
+ }
+ )
+ public Response createDownloadToken(@Context HttpServletRequest httpServletRequest) {
+ // only support access tokens when communicating over HTTPS
+ if (!httpServletRequest.isSecure()) {
+ throw new IllegalStateException("Download tokens are only issued over HTTPS.");
+ }
+
+ // if not configuration for login, don't consider credentials
+ if (loginIdentityProvider == null) {
+ throw new IllegalStateException("Download tokens not supported by this NiFi.");
+ }
+
+ final NiFiUser user = NiFiUserUtils.getNiFiUser();
+ if (user == null) {
+ throw new AccessDeniedException("Unable to determine user details.");
+ }
+
+ final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(user.getIdentity());
+
+ // generate otp for response
+ final String token = otpService.generateDownloadToken(authenticationToken);
+
+ // build the response
+ final URI uri = URI.create(generateResourceUri("access", "download-token"));
+ return generateCreatedResponse(uri, token).build();
+ }
+
+ /**
+ * Creates a single use access token for accessing a NiFi UI extension.
+ *
+ * @param httpServletRequest the servlet request
+ * @return A token (string)
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ @Produces(MediaType.TEXT_PLAIN)
+ @Path("/ui-extension-token")
+ @ApiOperation(
+ value = "Creates a single use access token for accessing a NiFi UI extension.",
+ notes = "The token returned is a base64 encoded string. It is valid for a single request up to five minutes from being issued. " +
+ "It is used as a query parameter name 'access_token'.",
+ response = String.class
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = 403, message = "Client is not authorized to make this request."),
+ @ApiResponse(code = 409, message = "Unable to create the download token because NiFi is not in the appropriate state. " +
+ "(i.e. may not have any tokens to grant or be configured to support username/password login)"),
+ @ApiResponse(code = 500, message = "Unable to create download token because an unexpected error occurred.")
+ }
+ )
+ public Response createUiExtensionToken(@Context HttpServletRequest httpServletRequest) {
+ // only support access tokens when communicating over HTTPS
+ if (!httpServletRequest.isSecure()) {
+ throw new IllegalStateException("UI extension access tokens are only issued over HTTPS.");
+ }
+
+ // if not configuration for login, don't consider credentials
+ if (loginIdentityProvider == null) {
+ throw new IllegalStateException("UI extension access tokens not supported by this NiFi.");
+ }
+
+ final NiFiUser user = NiFiUserUtils.getNiFiUser();
+ if (user == null) {
+ throw new AccessDeniedException("Unable to determine user details.");
+ }
+
+ final OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken(user.getIdentity());
+
+ // generate otp for response
+ final String token = otpService.generateUiExtensionToken(authenticationToken);
+
+ // build the response
+ final URI uri = URI.create(generateResourceUri("access", "ui-extension-token"));
+ return generateCreatedResponse(uri, token).build();
}
/**
@@ -302,6 +407,9 @@ public class AccessResource extends ApplicationResource {
@Path("/token")
@ApiOperation(
value = "Creates a token for accessing the REST API via username/password",
+ notes = "The token returned is formatted as a JSON Web Token (JWT). The token is base64 encoded and comprised of three parts. The header, " +
+ "the body, and the signature. The expiration of the token is a contained within the body. The token can be used in the Authorization header " +
+ "in the format 'Authorization: Bearer <token>'.",
response = String.class
)
@ApiResponses(
@@ -399,7 +507,7 @@ public class AccessResource extends ApplicationResource {
private void authorizeProxyIfNecessary(final List<String> proxyChain) throws AuthenticationException {
if (proxyChain.size() > 1) {
try {
- userDetailsService.loadUserDetails(new NiFiAuthortizationRequestToken(proxyChain));
+ userDetailsService.loadUserDetails(new NiFiAuthorizationRequestToken(proxyChain));
} catch (final UsernameNotFoundException unfe) {
// if a username not found exception was thrown, the proxies were authorized and now
// we can issue a new token to the end user which they will use to identify themselves
@@ -427,6 +535,10 @@ public class AccessResource extends ApplicationResource {
this.jwtService = jwtService;
}
+ public void setOtpService(OtpService otpService) {
+ this.otpService = otpService;
+ }
+
public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) {
this.certificateExtractor = certificateExtractor;
}
@@ -435,7 +547,7 @@ public class AccessResource extends ApplicationResource {
this.certificateIdentityProvider = certificateIdentityProvider;
}
- public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthortizationRequestToken> userDetailsService) {
+ public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthorizationRequestToken> userDetailsService) {
this.userDetailsService = userDetailsService;
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
index b38bca5..c9326b2 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
@@ -259,6 +259,7 @@
<property name="certificateIdentityProvider" ref="certificateIdentityProvider"/>
<property name="loginIdentityProvider" ref="loginIdentityProvider"/>
<property name="jwtService" ref="jwtService"/>
+ <property name="otpService" ref="otpService"/>
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
index 82fe73a..fe48490 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java
@@ -204,7 +204,7 @@ public class AccessTokenEndpointTest {
* @throws Exception ex
*/
@Test
- public void testUnkownUser() throws Exception {
+ public void testUnknownUser() throws Exception {
String url = BASE_URL + "/access/token";
ClientResponse response = TOKEN_USER.testCreateToken(url, "not a real user", "not a real password");
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java
index a6f6aec..9b053f0 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java
@@ -21,6 +21,7 @@ import com.ibm.icu.text.CharsetMatch;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URI;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
@@ -71,7 +72,19 @@ public class ContentViewerController extends HttpServlet {
final ServletContext servletContext = request.getServletContext();
final ContentAccess contentAccess = (ContentAccess) servletContext.getAttribute("nifi-content-access");
- final ContentRequestContext contentRequest = getContentRequest(request);
+ final ContentRequestContext contentRequest;
+ try {
+ contentRequest = getContentRequest(request);
+ } catch (final Exception e) {
+ request.setAttribute("title", "Error");
+ request.setAttribute("messages", "Unable to interpret content request.");
+
+ // forward to the error page
+ final ServletContext viewerContext = servletContext.getContext("/nifi");
+ viewerContext.getRequestDispatcher("/message").forward(request, response);
+ return;
+ }
+
if (contentRequest.getDataUri() == null) {
request.setAttribute("title", "Error");
request.setAttribute("messages", "The data reference must be specified.");
@@ -274,26 +287,41 @@ public class ContentViewerController extends HttpServlet {
* @return Get the content request context based on the specified request
*/
private ContentRequestContext getContentRequest(final HttpServletRequest request) {
+ final String ref = request.getParameter("ref");
+ final String clientId = request.getParameter("clientId");
+ final String proxiedEntitiesChain = request.getHeader("X-ProxiedEntitiesChain");
+
+ final URI refUri = URI.create(ref);
+ final String query = refUri.getQuery();
+ final String[] queryParameters = query.split("&");
+
+ String rawClusterNodeId = null;
+ for (int i = 0; i < queryParameters.length; i++) {
+ if (queryParameters[0].startsWith("clusterNodeId=")) {
+ rawClusterNodeId = StringUtils.substringAfterLast(queryParameters[0], "clusterNodeId=");
+ }
+ }
+ final String clusterNodeId = rawClusterNodeId;
+
return new ContentRequestContext() {
@Override
public String getDataUri() {
- return request.getParameter("ref");
+ return ref;
}
@Override
public String getClusterNodeId() {
- final String ref = request.getParameter("ref");
- return StringUtils.substringAfterLast(ref, "clusterNodeId=");
+ return clusterNodeId;
}
@Override
public String getClientId() {
- return request.getParameter("clientId");
+ return clientId;
}
@Override
public String getProxiedEntitiesChain() {
- return request.getHeader("X-ProxiedEntitiesChain");
+ return proxiedEntitiesChain;
}
};
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp
index 0f6fae3..c000284 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp
@@ -25,14 +25,37 @@
<link href="../nifi/css/message-pane.css" rel="stylesheet" type="text/css" />
<link href="../nifi/css/message-page.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="../nifi/js/jquery/combo/jquery.combo.css" type="text/css" />
+ <link rel="stylesheet" href="../nifi/js/jquery/modal/jquery.modal.css" type="text/css" />
<link rel="stylesheet" href="../nifi/css/reset.css" type="text/css" />
<script type="text/javascript" src="../nifi/js/jquery/jquery-2.1.1.min.js"></script>
+ <script type="text/javascript" src="../nifi/js/jquery/jquery.center.js"></script>
<script type="text/javascript" src="../nifi/js/jquery/combo/jquery.combo.js"></script>
+ <script type="text/javascript" src="../nifi/js/jquery/modal/jquery.modal.js"></script>
<script type="text/javascript" src="../nifi/js/nf/nf-namespace.js"></script>
+ <script type="text/javascript" src="../nifi/js/nf/nf-storage.js"></script>
+ <script type="text/javascript" src="../nifi/js/nf/nf-ajax-setup.js"></script>
<script type="text/javascript" src="../nifi/js/nf/nf-universal-capture.js"></script>
<script type="text/javascript">
var $$ = $.noConflict(true);
$$(document).ready(function () {
+ // initialize the dialog
+ $$('#content-viewer-message-dialog').modal({
+ overlayBackground: false,
+ buttons: [{
+ buttonText: 'Ok',
+ handler: {
+ click: function () {
+ $$('#content-viewer-message-dialog').modal('hide');
+ }
+ }
+ }],
+ handler: {
+ close: function () {
+ $$('#content-viewer-message').text('');
+ }
+ }
+ });
+
var url = $$('#requestUrl').text();
var ref = $$('#ref').text();
@@ -74,12 +97,40 @@
currentLocation = option.value;
return;
}
-
+
// if the selection has changesd, reload the page
if (currentLocation !== option.value) {
- window.location.href = url + '?' + $$.param($$.extend({
- mode: option.value
- }, params));
+ // get an access token if necessary
+ var getAccessToken = $.Deferred(function (deferred) {
+ if (nf.Storage.hasItem('jwt')) {
+ $.ajax({
+ type: 'POST',
+ url: '../nifi-api/access/ui-extension-token'
+ }).done(function (token) {
+ deferred.resolve(token);
+ }).fail(function () {
+ $$('#content-viewer-message').text('Unable to generate a token to view the content.');
+ $$('#content-viewer-message-dialog').modal('show');
+ deferred.reject();
+ })
+ } else {
+ deferred.resolve('');
+ }
+ }).promise();
+
+ // reload as appropriate
+ getAccessToken.done(function (uiExtensionToken) {
+ var contentParameter = {
+ mode: option.value
+ };
+
+ // include the download token if applicable
+ if (typeof uiExtensionToken !== 'undefined' && uiExtensionToken !== null && $$.trim(uiExtensionToken) !== '') {
+ contentParameter['access_token'] = uiExtensionToken;
+ }
+
+ window.location.href = url + '?' + $$.param($$.extend(contentParameter, params));
+ });
}
}
});
@@ -91,6 +142,11 @@
<span id="clusterNodeId" class="hidden"><%= request.getParameter("clusterNodeId") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("clusterNodeId")) %></span>
<span id="mode" class="hidden"><%= request.getParameter("mode") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("mode")) %></span>
<span id="requestUrl" class="hidden"><%= org.apache.nifi.util.EscapeUtils.escapeHtml(request.getRequestURL().toString()) %></span>
+ <div id="content-viewer-message-dialog">
+ <div class="dialog-content" style="margin-top: -20px;">
+ <div id="content-viewer-message"></div>
+ </div>
+ </div>
<div id="view-as-label">View as</div>
<div id="view-as" class="pointer button-normal"></div>
<div id="content-filename"><span class="content-label">filename</span><%= request.getAttribute("filename") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("filename").toString()) %></div>
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/css/main.css
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/css/main.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/css/main.css
index fe68b62..6e83c76 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/css/main.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/css/main.css
@@ -19,6 +19,14 @@
padding: 0;
}
+#content-viewer-message-dialog {
+ z-index: 1302;
+ display: none;
+ height: 225px;
+ width: 375px;
+ font-size: 62.5%
+}
+
#view-as-label {
position: absolute;
top: 72px;
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
index d63f01e..0520ac8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
@@ -27,7 +27,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.user.NiFiUser;
import org.apache.nifi.util.NiFiProperties;
-import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthorizationRequestToken;
import org.apache.nifi.web.security.user.NiFiUserUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -82,7 +82,7 @@ public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
private void authenticate(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException {
String dnChain = null;
try {
- final NiFiAuthortizationRequestToken authenticated = attemptAuthentication(request);
+ final NiFiAuthorizationRequestToken authenticated = attemptAuthentication(request);
if (authenticated != null) {
dnChain = ProxiedEntitiesUtils.formatProxyDn(StringUtils.join(authenticated.getChain(), "><"));
@@ -125,7 +125,7 @@ public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
* @return The NiFiAutorizationRequestToken used to later authorized the client
* @throws InvalidAuthenticationException If the request contained an authentication attempt, but could not authenticate
*/
- public abstract NiFiAuthortizationRequestToken attemptAuthentication(HttpServletRequest request);
+ public abstract NiFiAuthorizationRequestToken attemptAuthentication(HttpServletRequest request);
protected void successfulAuthorization(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
if (log.isDebugEnabled()) {
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
index 0887901..e51a26e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java
@@ -18,7 +18,7 @@ package org.apache.nifi.web.security;
import org.apache.nifi.web.security.token.NewAccountAuthorizationRequestToken;
import org.apache.nifi.web.security.token.NewAccountAuthorizationToken;
-import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthorizationRequestToken;
import org.apache.nifi.web.security.token.NiFiAuthorizationToken;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
@@ -32,15 +32,15 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
*/
public class NiFiAuthenticationProvider implements AuthenticationProvider {
- private final AuthenticationUserDetailsService<NiFiAuthortizationRequestToken> userDetailsService;
+ private final AuthenticationUserDetailsService<NiFiAuthorizationRequestToken> userDetailsService;
- public NiFiAuthenticationProvider(final AuthenticationUserDetailsService<NiFiAuthortizationRequestToken> userDetailsService) {
+ public NiFiAuthenticationProvider(final AuthenticationUserDetailsService<NiFiAuthorizationRequestToken> userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
- final NiFiAuthortizationRequestToken request = (NiFiAuthortizationRequestToken) authentication;
+ final NiFiAuthorizationRequestToken request = (NiFiAuthorizationRequestToken) authentication;
try {
// defer to the nifi user details service to authorize the user
@@ -67,7 +67,7 @@ public class NiFiAuthenticationProvider implements AuthenticationProvider {
@Override
public boolean supports(Class<?> authentication) {
- return NiFiAuthortizationRequestToken.class.isAssignableFrom(authentication);
+ return NiFiAuthorizationRequestToken.class.isAssignableFrom(authentication);
}
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
index e67ed2c..05c5fb8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java
@@ -58,8 +58,11 @@ public class NiFiAnonymousUserFilter extends AnonymousAuthenticationFilter {
user.getAuthorities().addAll(EnumSet.allOf(Authority.class));
}
- // only create an authentication token if the anonymous user has some authorities
- if (!user.getAuthorities().isEmpty()) {
+ // only create an authentication token if the anonymous user has some authorities or they are accessing a ui
+ // extension. ui extensions have run this security filter but we shouldn't require authentication/authorization
+ // when accessing static resources like images, js, and css. authentication/authorization is required when
+ // interacting with nifi however and that will be verified in the NiFiWebContext or NiFiWebConfigurationContext
+ if (!user.getAuthorities().isEmpty() || !request.getContextPath().startsWith("/nifi-api")) {
NiFiUserDetails userDetails = new NiFiUserDetails(user);
// get the granted authorities
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
index 75c01bf..dd87cfa 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/authorization/NiFiAuthorizationService.java
@@ -30,7 +30,7 @@ import org.apache.nifi.user.NiFiUser;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.security.UntrustedProxyException;
import org.apache.nifi.web.security.user.NiFiUserDetails;
-import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthorizationRequestToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
@@ -44,7 +44,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* UserDetailsService that will verify user identity and grant user authorities.
*/
-public class NiFiAuthorizationService implements AuthenticationUserDetailsService<NiFiAuthortizationRequestToken> {
+public class NiFiAuthorizationService implements AuthenticationUserDetailsService<NiFiAuthorizationRequestToken> {
private static final Logger logger = LoggerFactory.getLogger(NiFiAuthorizationService.class);
@@ -63,7 +63,7 @@ public class NiFiAuthorizationService implements AuthenticationUserDetailsServic
* @throws org.springframework.dao.DataAccessException ex
*/
@Override
- public synchronized UserDetails loadUserDetails(NiFiAuthortizationRequestToken request) throws UsernameNotFoundException, DataAccessException {
+ public synchronized UserDetails loadUserDetails(NiFiAuthorizationRequestToken request) throws UsernameNotFoundException, DataAccessException {
NiFiUserDetails userDetails = null;
final List<String> chain = new ArrayList<>(request.getChain());
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
index faf3cde..e13fcea 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/jwt/JwtAuthenticationFilter.java
@@ -20,7 +20,7 @@ import io.jsonwebtoken.JwtException;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.web.security.NiFiAuthenticationFilter;
import org.apache.nifi.web.security.token.NewAccountAuthorizationRequestToken;
-import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthorizationRequestToken;
import org.apache.nifi.web.security.user.NewAccountRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,7 +40,7 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
private JwtService jwtService;
@Override
- public NiFiAuthortizationRequestToken attemptAuthentication(final HttpServletRequest request) {
+ public NiFiAuthorizationRequestToken attemptAuthentication(final HttpServletRequest request) {
// only suppport jwt login when running securely
if (!request.isSecure()) {
return null;
@@ -68,7 +68,7 @@ public class JwtAuthenticationFilter extends NiFiAuthenticationFilter {
if (isNewAccountRequest(request)) {
return new NewAccountAuthorizationRequestToken(new NewAccountRequest(Arrays.asList(jwtPrincipal), getJustification(request)));
} else {
- return new NiFiAuthortizationRequestToken(Arrays.asList(jwtPrincipal));
+ return new NiFiAuthorizationRequestToken(Arrays.asList(jwtPrincipal));
}
} catch (JwtException e) {
throw new InvalidAuthenticationException(e.getMessage(), e);
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationException.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationException.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationException.java
new file mode 100644
index 0000000..ee2d573
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationException.java
@@ -0,0 +1,34 @@
+/*
+ * 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.web.security.otp;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * Thrown if a one time use token fails authentication.
+ */
+public class OtpAuthenticationException extends AuthenticationException {
+
+ public OtpAuthenticationException(String msg) {
+ super(msg);
+ }
+
+ public OtpAuthenticationException(String msg, Throwable t) {
+ super(msg, t);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationFilter.java
new file mode 100644
index 0000000..9c44298
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpAuthenticationFilter.java
@@ -0,0 +1,96 @@
+/*
+ * 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.web.security.otp;
+
+import org.apache.nifi.web.security.InvalidAuthenticationException;
+import org.apache.nifi.web.security.NiFiAuthenticationFilter;
+import org.apache.nifi.web.security.token.NiFiAuthorizationRequestToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+/**
+ */
+public class OtpAuthenticationFilter extends NiFiAuthenticationFilter {
+
+ private static final Logger logger = LoggerFactory.getLogger(OtpAuthenticationFilter.class);
+
+ private static final Pattern PROVENANCE_DOWNLOAD_PATTERN =
+ Pattern.compile("/controller/provenance/events/[0-9]+/content/(?:(?:output)|(?:input))");
+ private static final Pattern QUEUE_DOWNLOAD_PATTERN =
+ Pattern.compile("/controller/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/connections/[a-f0-9\\-]{36}/flowfiles/[a-f0-9\\-]{36}/content");
+ private static final Pattern TEMPLATE_DOWNLOAD_PATTERN =
+ Pattern.compile("/controller/templates/[a-f0-9\\-]{36}");
+
+ protected static final String ACCESS_TOKEN = "access_token";
+
+ private OtpService otpService;
+
+ @Override
+ public NiFiAuthorizationRequestToken attemptAuthentication(final HttpServletRequest request) {
+ // only support otp login when running securely
+ if (!request.isSecure()) {
+ return null;
+ }
+
+ // get the accessToken out of the query string
+ final String accessToken = request.getParameter(ACCESS_TOKEN);
+
+ // if there is no authorization header, we don't know the user
+ if (accessToken == null) {
+ return null;
+ } else {
+ if (otpService == null) {
+ throw new InvalidAuthenticationException("NiFi is not configured to support username/password logins.");
+ }
+
+ try {
+ String identity = null;
+ if (request.getContextPath().equals("/nifi-api")) {
+ if (isDownloadRequest(request.getPathInfo())) {
+ // handle download requests
+ identity = otpService.getAuthenticationFromDownloadToken(accessToken);
+ }
+ } else {
+ // handle requests to other context paths (other UI extensions)
+ identity = otpService.getAuthenticationFromUiExtensionToken(accessToken);
+ }
+
+ // the path is a support path for otp tokens
+ if (identity == null) {
+ return null;
+ }
+
+ return new NiFiAuthorizationRequestToken(Arrays.asList(identity));
+ } catch (final OtpAuthenticationException oae) {
+ throw new InvalidAuthenticationException(oae.getMessage(), oae);
+ }
+ }
+ }
+
+ private boolean isDownloadRequest(final String pathInfo) {
+ return PROVENANCE_DOWNLOAD_PATTERN.matcher(pathInfo).matches() || QUEUE_DOWNLOAD_PATTERN.matcher(pathInfo).matches() || TEMPLATE_DOWNLOAD_PATTERN.matcher(pathInfo).matches();
+ }
+
+ public void setOtpService(OtpService otpService) {
+ this.otpService = otpService;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpService.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpService.java
new file mode 100644
index 0000000..cd5a90c
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/otp/OtpService.java
@@ -0,0 +1,219 @@
+/*
+ * 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.web.security.otp;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.nifi.web.security.token.OtpAuthenticationToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * OtpService is a service for generating and verifying one time password tokens.
+ */
+public class OtpService {
+
+ private static final Logger logger = LoggerFactory.getLogger(OtpService.class);
+
+ private static final String HMAC_SHA256 = "HmacSHA256";
+
+ // protected for testing purposes
+ protected static final int MAX_CACHE_SOFT_LIMIT = 100;
+
+ private final Cache<CacheKey, String> downloadTokenCache;
+ private final Cache<CacheKey, String> uiExtensionCache;
+
+ /**
+ * Creates a new OtpService with an expiration of 5 minutes.
+ */
+ public OtpService() {
+ this(5, TimeUnit.MINUTES);
+ }
+
+ /**
+ * Creates a new OtpService.
+ *
+ * @param duration The expiration duration
+ * @param units The expiration units
+ * @throws NullPointerException If units is null
+ * @throws IllegalArgumentException If duration is negative
+ */
+ public OtpService(final int duration, final TimeUnit units) {
+ downloadTokenCache = CacheBuilder.newBuilder().expireAfterWrite(duration, units).build();
+ uiExtensionCache = CacheBuilder.newBuilder().expireAfterWrite(duration, units).build();
+ }
+
+ /**
+ * Generates a download token for the specified authentication.
+ *
+ * @param authenticationToken The authentication
+ * @return The one time use download token
+ */
+ public String generateDownloadToken(final OtpAuthenticationToken authenticationToken) {
+ return generateToken(downloadTokenCache.asMap(), authenticationToken);
+ }
+
+ /**
+ * Gets the authenticated identity from the specified one time use download token. This method will not return null.
+ *
+ * @param token The one time use download token
+ * @return The authenticated identity
+ * @throws OtpAuthenticationException When the specified token does not correspond to an authenticated identity
+ */
+ public String getAuthenticationFromDownloadToken(final String token) throws OtpAuthenticationException {
+ return getAuthenticationFromToken(downloadTokenCache.asMap(), token);
+ }
+
+ /**
+ * Generates a UI extension token for the specified authentication.
+ *
+ * @param authenticationToken The authentication
+ * @return The one time use UI extension token
+ */
+ public String generateUiExtensionToken(final OtpAuthenticationToken authenticationToken) {
+ return generateToken(uiExtensionCache.asMap(), authenticationToken);
+ }
+
+ /**
+ * Gets the authenticated identity from the specified one time use UI extension token. This method will not return null.
+ *
+ * @param token The one time use UI extension token
+ * @return The authenticated identity
+ * @throws OtpAuthenticationException When the specified token does not correspond to an authenticated identity
+ */
+ public String getAuthenticationFromUiExtensionToken(final String token) throws OtpAuthenticationException {
+ return getAuthenticationFromToken(uiExtensionCache.asMap(), token);
+ }
+
+ /**
+ * Generates a token and stores it in the specified cache.
+ *
+ * @param cache The cache
+ * @param authenticationToken The authentication
+ * @return The one time use token
+ */
+ private String generateToken(final ConcurrentMap<CacheKey, String> cache, final OtpAuthenticationToken authenticationToken) {
+ if (cache.size() >= MAX_CACHE_SOFT_LIMIT) {
+ throw new IllegalStateException("The maximum number of single use tokens have been issued.");
+ }
+
+ // hash the authentication and build a cache key
+ final CacheKey cacheKey = new CacheKey(hash(authenticationToken));
+
+ // store the token unless the token is already stored which should not update it's original timestamp
+ cache.putIfAbsent(cacheKey, authenticationToken.getName());
+
+ // return the token
+ return cacheKey.getToken();
+ }
+
+ /**
+ * Gets the corresponding authentication for the specified one time use token. The specified token will be removed.
+ *
+ * @param cache The cache
+ * @param token The one time use token
+ * @return The authenticated identity
+ */
+ private String getAuthenticationFromToken(final ConcurrentMap<CacheKey, String> cache, final String token) throws OtpAuthenticationException {
+ final String authenticatedUser = cache.remove(new CacheKey(token));
+ if (authenticatedUser == null) {
+ throw new OtpAuthenticationException("Unable to validate the access token.");
+ }
+
+ return authenticatedUser;
+ }
+
+ /**
+ * Hashes the specified authentication token. The resulting value will be used as the one time use token.
+ *
+ * @param authenticationToken the authentication token
+ * @return the one time use token
+ */
+ private String hash(final OtpAuthenticationToken authenticationToken) {
+ try {
+ // input is the user identity and timestamp
+ final String input = authenticationToken.getName() + "-" + System.nanoTime();
+
+ // create the secret using secure random
+ final SecureRandom secureRandom = new SecureRandom();
+ final byte[] randomBytes = new byte[32];
+ secureRandom.nextBytes(randomBytes);
+ final SecretKeySpec secret = new SecretKeySpec(randomBytes, HMAC_SHA256); // 256 bit
+
+ // hash the input
+ final Mac hmacSha256 = Mac.getInstance(HMAC_SHA256);
+ hmacSha256.init(secret);
+ final byte[] output = hmacSha256.doFinal(input.getBytes(StandardCharsets.UTF_8));
+
+ // return the result as a base 64 string
+ return Base64.encodeBase64URLSafeString(output);
+ } catch (final NoSuchAlgorithmException | InvalidKeyException e) {
+ final String errorMessage = "There was an error generating the OTP";
+ logger.error(errorMessage, e);
+ throw new IllegalStateException("Unable to generate single use token.");
+ }
+ }
+
+ /**
+ * Key for the cache. Necessary to override the default String.equals() to utilize MessageDigest.isEquals() to prevent timing attacks.
+ */
+ private static class CacheKey {
+ final String token;
+
+ public CacheKey(String token) {
+ this.token = token;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final CacheKey otherCacheKey = (CacheKey) o;
+ return MessageDigest.isEqual(token.getBytes(StandardCharsets.UTF_8), otherCacheKey.token.getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Override
+ public int hashCode() {
+ return token.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "CacheKey{token ending in '..." + token.substring(token.length() - 6) + "'}";
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthorizationRequestToken.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthorizationRequestToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthorizationRequestToken.java
index 35c371d..693d420 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthorizationRequestToken.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NewAccountAuthorizationRequestToken.java
@@ -21,7 +21,7 @@ import org.apache.nifi.web.security.user.NewAccountRequest;
/**
* An authentication token that is used as an authorization request when submitting a new account.
*/
-public class NewAccountAuthorizationRequestToken extends NiFiAuthortizationRequestToken {
+public class NewAccountAuthorizationRequestToken extends NiFiAuthorizationRequestToken {
final NewAccountRequest newAccountRequest;
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthorizationRequestToken.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthorizationRequestToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthorizationRequestToken.java
new file mode 100644
index 0000000..c20aaf3
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthorizationRequestToken.java
@@ -0,0 +1,54 @@
+/*
+ * 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.web.security.token;
+
+import java.util.Collections;
+import java.util.List;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+
+/**
+ * An authentication token that is used as an authorization request. The request has already been authenticated and is now going to be authorized.
+ * The request chain is specified during creation and is used authorize the user(s).
+ */
+public class NiFiAuthorizationRequestToken extends AbstractAuthenticationToken {
+
+ private final List<String> chain;
+
+ public NiFiAuthorizationRequestToken(final List<String> chain) {
+ super(null);
+ this.chain = chain;
+ }
+
+ @Override
+ public Object getCredentials() {
+ return null;
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return chain;
+ }
+
+ public List<String> getChain() {
+ return Collections.unmodifiableList(chain);
+ }
+
+ @Override
+ public final void setAuthenticated(boolean authenticated) {
+ throw new IllegalArgumentException("Cannot change the authenticated state.");
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthortizationRequestToken.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthortizationRequestToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthortizationRequestToken.java
deleted file mode 100644
index a1459a4..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/NiFiAuthortizationRequestToken.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.web.security.token;
-
-import java.util.Collections;
-import java.util.List;
-import org.springframework.security.authentication.AbstractAuthenticationToken;
-
-/**
- * An authentication token that is used as an authorization request. The request has already been authenticated and is now going to be authorized.
- * The request chain is specified during creation and is used authorize the user(s).
- */
-public class NiFiAuthortizationRequestToken extends AbstractAuthenticationToken {
-
- private final List<String> chain;
-
- public NiFiAuthortizationRequestToken(final List<String> chain) {
- super(null);
- this.chain = chain;
- }
-
- @Override
- public Object getCredentials() {
- return null;
- }
-
- @Override
- public Object getPrincipal() {
- return chain;
- }
-
- public List<String> getChain() {
- return Collections.unmodifiableList(chain);
- }
-
- @Override
- public final void setAuthenticated(boolean authenticated) {
- throw new IllegalArgumentException("Cannot change the authenticated state.");
- }
-}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/OtpAuthenticationToken.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/OtpAuthenticationToken.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/OtpAuthenticationToken.java
new file mode 100644
index 0000000..fc51912
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/token/OtpAuthenticationToken.java
@@ -0,0 +1,56 @@
+/*
+ * 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.web.security.token;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+
+/**
+ * This is an Authentication Token for logging in. Once a user is authenticated, they can be issued an ID token.
+ */
+public class OtpAuthenticationToken extends AbstractAuthenticationToken {
+
+ private final String identity;
+
+ /**
+ * Creates a representation of the otp authentication token for a user.
+ *
+ * @param identity The unique identifier for this user
+ */
+ public OtpAuthenticationToken(final String identity) {
+ super(null);
+ setAuthenticated(true);
+ this.identity = identity;
+ }
+
+ @Override
+ public Object getCredentials() {
+ return null;
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return identity;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("OtpAuthenticationToken for ")
+ .append(getName())
+ .toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
index 2c792f6..019a53c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/X509AuthenticationFilter.java
@@ -24,7 +24,7 @@ import org.apache.nifi.web.security.InvalidAuthenticationException;
import org.apache.nifi.web.security.NiFiAuthenticationFilter;
import org.apache.nifi.web.security.ProxiedEntitiesUtils;
import org.apache.nifi.web.security.token.NewAccountAuthorizationRequestToken;
-import org.apache.nifi.web.security.token.NiFiAuthortizationRequestToken;
+import org.apache.nifi.web.security.token.NiFiAuthorizationRequestToken;
import org.apache.nifi.web.security.user.NewAccountRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,7 +40,7 @@ public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
private X509IdentityProvider certificateIdentityProvider;
@Override
- public NiFiAuthortizationRequestToken attemptAuthentication(final HttpServletRequest request) {
+ public NiFiAuthorizationRequestToken attemptAuthentication(final HttpServletRequest request) {
// only suppport x509 login when running securely
if (!request.isSecure()) {
return null;
@@ -64,7 +64,7 @@ public class X509AuthenticationFilter extends NiFiAuthenticationFilter {
if (isNewAccountRequest(request)) {
return new NewAccountAuthorizationRequestToken(new NewAccountRequest(proxyChain, getJustification(request)));
} else {
- return new NiFiAuthortizationRequestToken(proxyChain);
+ return new NiFiAuthorizationRequestToken(proxyChain);
}
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/a8edab2e/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
index d079293..44a93af 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/resources/nifi-web-security-context.xml
@@ -49,7 +49,10 @@
<bean id="jwtService" class="org.apache.nifi.web.security.jwt.JwtService">
<constructor-arg ref="userService"/>
</bean>
-
+
+ <!-- otp service -->
+ <bean id="otpService" class="org.apache.nifi.web.security.otp.OtpService"/>
+
<!-- login identity provider -->
<bean id="loginIdentityProvider" class="org.apache.nifi.web.security.spring.LoginIdentityProviderFactoryBean">
<property name="properties" ref="nifiProperties"/>