You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2016/04/07 22:23:33 UTC

[5/9] nifi git commit: Revert "NIFI-1551:"

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/RequestUserAccountActionTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/RequestUserAccountActionTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/RequestUserAccountActionTest.java
new file mode 100644
index 0000000..7bc863b
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/RequestUserAccountActionTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.admin.service.action;
+
+import org.apache.nifi.admin.dao.DAOFactory;
+import org.apache.nifi.admin.dao.DataAccessException;
+import org.apache.nifi.admin.dao.UserDAO;
+import org.apache.nifi.user.AccountStatus;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Test case for RequestUserAccountAction.
+ */
+public class RequestUserAccountActionTest {
+
+    private static final String USER_ID_3 = "3";
+
+    private static final String USER_IDENTITY_1 = "existing user account";
+    private static final String USER_IDENTITY_2 = "data access exception";
+    private static final String USER_IDENTITY_3 = "new account request";
+
+    private DAOFactory daoFactory;
+    private UserDAO userDao;
+
+    @Before
+    public void setup() throws Exception {
+        // mock the user dao
+        userDao = Mockito.mock(UserDAO.class);
+        Mockito.doAnswer(new Answer<NiFiUser>() {
+            @Override
+            public NiFiUser answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                String dn = (String) args[0];
+
+                NiFiUser user = null;
+                if (USER_IDENTITY_1.equals(dn)) {
+                    user = new NiFiUser();
+                }
+                return user;
+            }
+        }).when(userDao).findUserByDn(Mockito.anyString());
+        Mockito.doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                NiFiUser user = (NiFiUser) args[0];
+                switch (user.getIdentity()) {
+                    case USER_IDENTITY_2:
+                        throw new DataAccessException();
+                    case USER_IDENTITY_3:
+                        user.setId(USER_ID_3);
+                        break;
+                }
+
+                // do nothing
+                return null;
+            }
+        }).when(userDao).createUser(Mockito.any(NiFiUser.class));
+
+        // mock the dao factory
+        daoFactory = Mockito.mock(DAOFactory.class);
+        Mockito.when(daoFactory.getUserDAO()).thenReturn(userDao);
+    }
+
+    /**
+     * Tests when a user account already exists.
+     *
+     * @throws Exception ex
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testExistingAccount() throws Exception {
+        RequestUserAccountAction requestUserAccount = new RequestUserAccountAction(USER_IDENTITY_1, StringUtils.EMPTY);
+        requestUserAccount.execute(daoFactory, null);
+    }
+
+    /**
+     * Tests when a DataAccessException occurs while saving the new account
+     * request.
+     *
+     * @throws Exception ex
+     */
+    @Test(expected = DataAccessException.class)
+    public void testDataAccessException() throws Exception {
+        RequestUserAccountAction requestUserAccount = new RequestUserAccountAction(USER_IDENTITY_2, StringUtils.EMPTY);
+        requestUserAccount.execute(daoFactory, null);
+    }
+
+    /**
+     * Tests the general case for requesting a new user account.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testRequestUserAccountAction() throws Exception {
+        RequestUserAccountAction requestUserAccount = new RequestUserAccountAction(USER_IDENTITY_3, StringUtils.EMPTY);
+        NiFiUser user = requestUserAccount.execute(daoFactory, null);
+
+        // verfiy the user
+        Assert.assertEquals(USER_ID_3, user.getId());
+        Assert.assertEquals(USER_IDENTITY_3, user.getIdentity());
+        Assert.assertEquals(AccountStatus.PENDING, user.getStatus());
+
+        // verify interaction with dao
+        Mockito.verify(userDao, Mockito.times(1)).createUser(user);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SeedUserAccountsActionTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SeedUserAccountsActionTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SeedUserAccountsActionTest.java
new file mode 100644
index 0000000..58db56a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SeedUserAccountsActionTest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.admin.service.action;
+
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.nifi.admin.dao.AuthorityDAO;
+import org.apache.nifi.admin.dao.DAOFactory;
+import org.apache.nifi.admin.dao.UserDAO;
+import org.apache.nifi.authorization.Authority;
+import org.apache.nifi.authorization.AuthorityProvider;
+import org.apache.nifi.user.AccountStatus;
+import org.apache.nifi.user.NiFiUser;
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ *
+ */
+public class SeedUserAccountsActionTest {
+
+    private static final String USER_ID_1 = "1";
+    private static final String USER_ID_2 = "2";
+    private static final String USER_ID_3 = "3";
+    private static final String USER_ID_4 = "4";
+
+    private static final String USER_IDENTITY_1 = "user 1 - active user - remove monitor and operator, add dfm";
+    private static final String USER_IDENTITY_2 = "user 2 - active user - no action";
+    private static final String USER_IDENTITY_3 = "user 3 - pending user - add operator";
+    private static final String USER_IDENTITY_4 = "user 4 - new user - add monitor";
+
+    private DAOFactory daoFactory;
+    private UserDAO userDao;
+    private AuthorityDAO authorityDao;
+    private AuthorityProvider authorityProvider;
+
+    @Before
+    public void setup() throws Exception {
+        // mock the user dao
+        userDao = Mockito.mock(UserDAO.class);
+        Mockito.doAnswer(new Answer<NiFiUser>() {
+            @Override
+            public NiFiUser answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                String id = (String) args[0];
+
+                NiFiUser user = null;
+                if (USER_ID_1.equals(id)) {
+                    user = new NiFiUser();
+                    user.setId(USER_ID_1);
+                    user.setIdentity(USER_IDENTITY_1);
+                    user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
+                    user.setStatus(AccountStatus.ACTIVE);
+                } else if (USER_ID_2.equals(id)) {
+                    user = new NiFiUser();
+                    user.setId(USER_ID_2);
+                    user.setIdentity(USER_IDENTITY_2);
+                    user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_ADMIN));
+                    user.setStatus(AccountStatus.ACTIVE);
+                } else if (USER_ID_3.equals(id)) {
+                    user = new NiFiUser();
+                    user.setId(USER_ID_3);
+                    user.setIdentity(USER_IDENTITY_3);
+                    user.setStatus(AccountStatus.PENDING);
+                }
+                return user;
+            }
+        }).when(userDao).findUserById(Mockito.anyString());
+        Mockito.doAnswer(new Answer<NiFiUser>() {
+            @Override
+            public NiFiUser answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                String dn = (String) args[0];
+
+                NiFiUser user = null;
+                if (USER_IDENTITY_1.equals(dn)) {
+                    user = new NiFiUser();
+                    user.setId(USER_ID_1);
+                    user.setIdentity(USER_IDENTITY_1);
+                    user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
+                    user.setStatus(AccountStatus.ACTIVE);
+                } else if (USER_IDENTITY_2.equals(dn)) {
+                    user = new NiFiUser();
+                    user.setId(USER_ID_2);
+                    user.setIdentity(USER_IDENTITY_2);
+                    user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_ADMIN));
+                    user.setStatus(AccountStatus.ACTIVE);
+                } else if (USER_IDENTITY_3.equals(dn)) {
+                    user = new NiFiUser();
+                    user.setId(USER_ID_3);
+                    user.setIdentity(USER_IDENTITY_3);
+                    user.setStatus(AccountStatus.PENDING);
+                }
+                return user;
+            }
+        }).when(userDao).findUserByDn(Mockito.anyString());
+        Mockito.doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                NiFiUser user = (NiFiUser) args[0];
+
+                if (USER_IDENTITY_4.equals(user.getIdentity())) {
+                    user.setId(USER_ID_4);
+                }
+
+                return null;
+            }
+        }).when(userDao).createUser(Mockito.any(NiFiUser.class));
+
+        // mock the authority dao
+        authorityDao = Mockito.mock(AuthorityDAO.class);
+
+        // mock the authority provider
+        authorityProvider = Mockito.mock(AuthorityProvider.class);
+        Mockito.doAnswer(new Answer<Set<String>>() {
+            @Override
+            public Set<String> answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                Authority role = (Authority) args[0];
+
+                Set<String> users = new HashSet<>();
+                if (Authority.ROLE_DFM.equals(role)) {
+                    users.add(USER_IDENTITY_1);
+                } else if (Authority.ROLE_ADMIN.equals(role)) {
+                    users.add(USER_IDENTITY_2);
+                } else if (Authority.ROLE_PROXY.equals(role)) {
+                    users.add(USER_IDENTITY_3);
+                } else if (Authority.ROLE_MONITOR.equals(role)) {
+                    users.add(USER_IDENTITY_4);
+                }
+                return users;
+            }
+        }).when(authorityProvider).getUsers(Mockito.any(Authority.class));
+        Mockito.doAnswer(new Answer<Set<Authority>>() {
+            @Override
+            public Set<Authority> answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                String dn = (String) args[0];
+
+                Set<Authority> authorities = EnumSet.noneOf(Authority.class);
+                switch (dn) {
+                    case USER_IDENTITY_1:
+                        authorities.add(Authority.ROLE_DFM);
+                        break;
+                    case USER_IDENTITY_2:
+                        authorities.add(Authority.ROLE_ADMIN);
+                        break;
+                    case USER_IDENTITY_3:
+                        authorities.add(Authority.ROLE_PROXY);
+                        break;
+                    case USER_IDENTITY_4:
+                        authorities.add(Authority.ROLE_MONITOR);
+                        break;
+                }
+                return authorities;
+            }
+        }).when(authorityProvider).getAuthorities(Mockito.anyString());
+
+        // mock the dao factory
+        daoFactory = Mockito.mock(DAOFactory.class);
+        Mockito.when(daoFactory.getUserDAO()).thenReturn(userDao);
+        Mockito.when(daoFactory.getAuthorityDAO()).thenReturn(authorityDao);
+    }
+
+    /**
+     * Tests seeding the user accounts.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testSeedUsers() throws Exception {
+        SeedUserAccountsAction seedUserAccounts = new SeedUserAccountsAction();
+        seedUserAccounts.execute(daoFactory, authorityProvider);
+
+        // matcher for user 1
+        Matcher<NiFiUser> matchesUser1 = new ArgumentMatcher<NiFiUser>() {
+            @Override
+            public boolean matches(Object argument) {
+                NiFiUser user = (NiFiUser) argument;
+                return USER_ID_1.equals(user.getId());
+            }
+        };
+
+        // verify user 1 - active existing user - remove monitor, operator, add dfm
+        Mockito.verify(userDao, Mockito.times(1)).updateUser(Mockito.argThat(matchesUser1));
+        Mockito.verify(userDao, Mockito.never()).createUser(Mockito.argThat(matchesUser1));
+        Mockito.verify(authorityDao, Mockito.times(1)).createAuthorities(EnumSet.of(Authority.ROLE_DFM), USER_ID_1);
+
+        // matcher for user 2
+        Matcher<NiFiUser> matchesUser2 = new ArgumentMatcher<NiFiUser>() {
+            @Override
+            public boolean matches(Object argument) {
+                NiFiUser user = (NiFiUser) argument;
+                return USER_ID_2.equals(user.getId());
+            }
+        };
+
+        // verify user 2 - active existing user - no actions
+        Mockito.verify(userDao, Mockito.times(1)).updateUser(Mockito.argThat(matchesUser2));
+        Mockito.verify(userDao, Mockito.never()).createUser(Mockito.argThat(matchesUser2));
+        Mockito.verify(authorityDao, Mockito.never()).createAuthorities(Mockito.anySet(), Mockito.eq(USER_ID_2));
+        Mockito.verify(authorityDao, Mockito.never()).deleteAuthorities(Mockito.anySet(), Mockito.eq(USER_ID_2));
+
+        // matchers for user 3
+        Matcher<NiFiUser> matchesPendingUser3 = new ArgumentMatcher<NiFiUser>() {
+            @Override
+            public boolean matches(Object argument) {
+                NiFiUser user = (NiFiUser) argument;
+                return USER_ID_3.equals(user.getId()) && AccountStatus.ACTIVE.equals(user.getStatus());
+            }
+        };
+        Matcher<NiFiUser> matchesUser3 = new ArgumentMatcher<NiFiUser>() {
+            @Override
+            public boolean matches(Object argument) {
+                NiFiUser user = (NiFiUser) argument;
+                return USER_ID_3.equals(user.getId());
+            }
+        };
+
+        // verify user 3 - pending user - add operator
+        Mockito.verify(userDao, Mockito.times(1)).updateUser(Mockito.argThat(matchesPendingUser3));
+        Mockito.verify(userDao, Mockito.never()).createUser(Mockito.argThat(matchesUser3));
+        Mockito.verify(authorityDao, Mockito.times(1)).createAuthorities(EnumSet.of(Authority.ROLE_PROXY), USER_ID_3);
+        Mockito.verify(authorityDao, Mockito.never()).deleteAuthorities(Mockito.anySet(), Mockito.eq(USER_ID_3));
+
+        // matcher for user 4
+        Matcher<NiFiUser> matchesUser4 = new ArgumentMatcher<NiFiUser>() {
+            @Override
+            public boolean matches(Object argument) {
+                NiFiUser user = (NiFiUser) argument;
+                return USER_ID_4.equals(user.getId());
+            }
+        };
+
+        // verify user 4 - new user - add monitor
+        Mockito.verify(userDao, Mockito.never()).updateUser(Mockito.argThat(matchesUser4));
+        Mockito.verify(userDao, Mockito.times(1)).createUser(Mockito.argThat(matchesUser4));
+        Mockito.verify(authorityDao, Mockito.times(1)).createAuthorities(EnumSet.of(Authority.ROLE_MONITOR), USER_ID_4);
+        Mockito.verify(authorityDao, Mockito.never()).deleteAuthorities(Mockito.anySet(), Mockito.eq(USER_ID_4));
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SetUserAuthoritiesActionTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SetUserAuthoritiesActionTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SetUserAuthoritiesActionTest.java
new file mode 100644
index 0000000..5effdbb
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/action/SetUserAuthoritiesActionTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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.admin.service.action;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+import org.apache.nifi.admin.dao.AuthorityDAO;
+import org.apache.nifi.admin.dao.DAOFactory;
+import org.apache.nifi.admin.dao.UserDAO;
+import org.apache.nifi.admin.service.AccountNotFoundException;
+import org.apache.nifi.admin.service.AdministrationException;
+import org.apache.nifi.authorization.Authority;
+import org.apache.nifi.authorization.AuthorityProvider;
+import org.apache.nifi.authorization.exception.AuthorityAccessException;
+import org.apache.nifi.user.AccountStatus;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Test case for SetUserAuthoritiesAction.
+ */
+public class SetUserAuthoritiesActionTest {
+
+    private static final String USER_ID_1 = "1";
+    private static final String USER_ID_2 = "2";
+    private static final String USER_ID_3 = "3";
+
+    private static final String USER_IDENTITY_2 = "user 2";
+    private static final String USER_IDENTITY_3 = "user 3";
+
+    private DAOFactory daoFactory;
+    private UserDAO userDao;
+    private AuthorityDAO authorityDao;
+    private AuthorityProvider authorityProvider;
+
+    @Before
+    public void setup() throws Exception {
+        // mock the user dao
+        userDao = Mockito.mock(UserDAO.class);
+        Mockito.doAnswer(new Answer<NiFiUser>() {
+            @Override
+            public NiFiUser answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                String id = (String) args[0];
+
+                NiFiUser user = null;
+                if (USER_ID_1.equals(id)) {
+                    // leave user uninitialized
+                } else if (USER_ID_2.equals(id)) {
+                    user = new NiFiUser();
+                    user.setId(USER_ID_2);
+                    user.setIdentity(USER_IDENTITY_2);
+                } else if (USER_ID_3.equals(id)) {
+                    user = new NiFiUser();
+                    user.setId(USER_ID_3);
+                    user.setIdentity(USER_IDENTITY_3);
+                    user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
+                    user.setStatus(AccountStatus.ACTIVE);
+                }
+                return user;
+            }
+        }).when(userDao).findUserById(Mockito.anyString());
+        Mockito.doAnswer(new Answer<NiFiUser>() {
+            @Override
+            public NiFiUser answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                String dn = (String) args[0];
+
+                NiFiUser user = null;
+                if (USER_IDENTITY_3.equals(dn)) {
+                    user = new NiFiUser();
+                    user.setId(USER_ID_3);
+                    user.setIdentity(USER_IDENTITY_3);
+                    user.getAuthorities().addAll(EnumSet.of(Authority.ROLE_MONITOR));
+                    user.setStatus(AccountStatus.ACTIVE);
+                }
+                return user;
+            }
+        }).when(userDao).findUserByDn(Mockito.anyString());
+        Mockito.doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                NiFiUser user = (NiFiUser) args[0];
+
+                // do nothing
+                return null;
+            }
+        }).when(userDao).updateUser(Mockito.any(NiFiUser.class));
+
+        // mock the authority dao
+        authorityDao = Mockito.mock(AuthorityDAO.class);
+        Mockito.doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                Set<Authority> authorities = (Set<Authority>) args[0];
+                String id = (String) args[1];
+
+                // do nothing
+                return null;
+            }
+        }).when(authorityDao).createAuthorities(Mockito.anySetOf(Authority.class), Mockito.anyString());
+        Mockito.doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                Set<Authority> authorities = (Set<Authority>) args[0];
+                String id = (String) args[1];
+
+                // do nothing
+                return null;
+            }
+        }).when(authorityDao).deleteAuthorities(Mockito.anySetOf(Authority.class), Mockito.anyString());
+
+        // mock the dao factory
+        daoFactory = Mockito.mock(DAOFactory.class);
+        Mockito.when(daoFactory.getUserDAO()).thenReturn(userDao);
+        Mockito.when(daoFactory.getAuthorityDAO()).thenReturn(authorityDao);
+
+        // mock the authority provider
+        authorityProvider = Mockito.mock(AuthorityProvider.class);
+        Mockito.doAnswer(new Answer<Set<Authority>>() {
+            @Override
+            public Set<Authority> answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                String dn = (String) args[0];
+
+                Set<Authority> authorities = EnumSet.noneOf(Authority.class);
+                if (USER_IDENTITY_3.equals(dn)) {
+                    authorities.add(Authority.ROLE_DFM);
+                }
+
+                return authorities;
+            }
+        }).when(authorityProvider).getAuthorities(Mockito.anyString());
+        Mockito.doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                String dn = (String) args[0];
+                Set<Authority> authorites = (Set<Authority>) args[1];
+
+                if (USER_IDENTITY_2.equals(dn)) {
+                    throw new AuthorityAccessException(StringUtils.EMPTY);
+                }
+
+                // do nothing
+                return null;
+            }
+        }).when(authorityProvider).setAuthorities(Mockito.anyString(), Mockito.anySet());
+    }
+
+    /**
+     * Test activating an unknown user account. User accounts are unknown then
+     * there is no pending account for the user.
+     *
+     * @throws Exception ex
+     */
+    @Test(expected = AccountNotFoundException.class)
+    public void testUnknownUser() throws Exception {
+        UpdateUserAction setUserAuthorities = new UpdateUserAction(USER_ID_1, Collections.EMPTY_SET);
+        setUserAuthorities.execute(daoFactory, authorityProvider);
+    }
+
+    /**
+     * Testing case then an AuthorityAccessException occurs while setting a
+     * users authorities.
+     *
+     * @throws Exception ex
+     */
+    @Test(expected = AdministrationException.class)
+    public void testAuthorityAccessException() throws Exception {
+        UpdateUserAction setUserAuthorities = new UpdateUserAction(USER_ID_2, Collections.EMPTY_SET);
+        setUserAuthorities.execute(daoFactory, authorityProvider);
+    }
+
+    /**
+     * Tests general case of setting user authorities.
+     *
+     * @throws Exception ex
+     */
+    @Test
+    public void testSetAuthorities() throws Exception {
+        UpdateUserAction setUserAuthorities = new UpdateUserAction(USER_ID_3, EnumSet.of(Authority.ROLE_ADMIN));
+        NiFiUser user = setUserAuthorities.execute(daoFactory, authorityProvider);
+
+        // verify user
+        Assert.assertEquals(USER_ID_3, user.getId());
+        Assert.assertEquals(1, user.getAuthorities().size());
+        Assert.assertTrue(user.getAuthorities().contains(Authority.ROLE_ADMIN));
+
+        // verify interaction with dao
+        Mockito.verify(userDao, Mockito.times(1)).updateUser(user);
+        Mockito.verify(authorityDao, Mockito.times(1)).createAuthorities(EnumSet.of(Authority.ROLE_ADMIN), USER_ID_3);
+
+        Set<Authority> authoritiesAddedToProvider = EnumSet.of(Authority.ROLE_ADMIN);
+
+        // verify interaction with provider
+        Mockito.verify(authorityProvider, Mockito.times(1)).setAuthorities(USER_IDENTITY_3, authoritiesAddedToProvider);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/status/ControllerStatusDTO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/status/ControllerStatusDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/status/ControllerStatusDTO.java
index cec51e5..03e2124 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/status/ControllerStatusDTO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/status/ControllerStatusDTO.java
@@ -38,6 +38,8 @@ public class ControllerStatusDTO implements Cloneable {
     private Integer connectedNodeCount = 0;
     private Integer totalNodeCount = 0;
 
+    private Boolean hasPendingAccounts;
+
     private Integer runningCount = 0;
     private Integer stoppedCount = 0;
     private Integer invalidCount = 0;
@@ -125,6 +127,18 @@ public class ControllerStatusDTO implements Cloneable {
     }
 
     /**
+     * @return whether or not there are pending user requests
+     */
+    @ApiModelProperty("Whether there are any pending user account requests.")
+    public Boolean getHasPendingAccounts() {
+        return hasPendingAccounts;
+    }
+
+    public void setHasPendingAccounts(Boolean hasPendingAccounts) {
+        this.hasPendingAccounts = hasPendingAccounts;
+    }
+
+    /**
      * @return number of running components in this controller
      */
     @ApiModelProperty("The number of running components in the NiFi.")
@@ -242,6 +256,7 @@ public class ControllerStatusDTO implements Cloneable {
         other.setConnectedNodes(getConnectedNodes());
         other.setConnectedNodeCount(getConnectedNodeCount());
         other.setTotalNodeCount(getTotalNodeCount());
+        other.setHasPendingAccounts(getHasPendingAccounts());
         other.setRunningCount(getRunningCount());
         other.setStoppedCount(getStoppedCount());
         other.setInvalidCount(getInvalidCount());

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/.gitignore
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/.gitignore b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/.gitignore
new file mode 100755
index 0000000..ea8c4bf
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/.gitignore
@@ -0,0 +1 @@
+/target

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/pom.xml
new file mode 100644
index 0000000..2f0147b
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-framework</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-cluster-authorization-provider</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-file-authorization-provider</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-framework-cluster-protocol</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-framework-cluster</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-socket-utils</artifactId>
+        </dependency>
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/ClusterManagerAuthorizationProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/ClusterManagerAuthorizationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/ClusterManagerAuthorizationProvider.java
new file mode 100644
index 0000000..2b3b38c
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/ClusterManagerAuthorizationProvider.java
@@ -0,0 +1,225 @@
+/*
+ * 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.cluster.authorization;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import org.apache.nifi.authorization.AuthorityProvider;
+import org.apache.nifi.authorization.AuthorityProviderConfigurationContext;
+import org.apache.nifi.authorization.AuthorityProviderInitializationContext;
+import org.apache.nifi.authorization.FileAuthorizationProvider;
+import org.apache.nifi.authorization.annotation.AuthorityProviderContext;
+import org.apache.nifi.authorization.exception.ProviderCreationException;
+import org.apache.nifi.authorization.exception.ProviderDestructionException;
+import org.apache.nifi.cluster.authorization.protocol.message.DoesDnExistMessage;
+import org.apache.nifi.cluster.authorization.protocol.message.GetAuthoritiesMessage;
+import org.apache.nifi.cluster.authorization.protocol.message.GetGroupForUserMessage;
+import org.apache.nifi.cluster.authorization.protocol.message.ProtocolMessage;
+import static org.apache.nifi.cluster.authorization.protocol.message.ProtocolMessage.MessageType.DOES_DN_EXIST;
+import static org.apache.nifi.cluster.authorization.protocol.message.ProtocolMessage.MessageType.GET_AUTHORITIES;
+import static org.apache.nifi.cluster.authorization.protocol.message.ProtocolMessage.MessageType.GET_GROUP_FOR_USER;
+import org.apache.nifi.cluster.authorization.protocol.message.jaxb.JaxbProtocolUtils;
+import org.apache.nifi.cluster.manager.impl.WebClusterManager;
+import org.apache.nifi.cluster.protocol.ProtocolContext;
+import org.apache.nifi.cluster.protocol.ProtocolMessageMarshaller;
+import org.apache.nifi.cluster.protocol.ProtocolMessageUnmarshaller;
+import org.apache.nifi.cluster.protocol.jaxb.JaxbProtocolContext;
+import org.apache.nifi.io.socket.ServerSocketConfiguration;
+import org.apache.nifi.io.socket.SocketListener;
+import org.apache.nifi.io.socket.SocketUtils;
+import org.apache.nifi.io.socket.multicast.DiscoverableService;
+import org.apache.nifi.io.socket.multicast.DiscoverableServiceImpl;
+import org.apache.nifi.logging.NiFiLog;
+import org.apache.nifi.util.NiFiProperties;
+import static org.apache.nifi.util.NiFiProperties.CLUSTER_MANAGER_ADDRESS;
+import org.apache.nifi.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+/**
+ * Provides authorities for the NCM in clustered environments. Communication
+ * occurs over TCP/IP sockets. All method calls are deferred to the
+ * FileAuthorizationProvider.
+ */
+public class ClusterManagerAuthorizationProvider extends FileAuthorizationProvider implements AuthorityProvider, ApplicationContextAware {
+
+    public static final String AUTHORITY_PROVIDER_SERVIVE_NAME = "cluster-authority-provider";
+
+    private static final Logger logger = new NiFiLog(LoggerFactory.getLogger(ClusterManagerAuthorizationProvider.class));
+    private static final String CLUSTER_MANAGER_AUTHORITY_PROVIDER_PORT = "Authority Provider Port";
+    private static final String CLUSTER_MANAGER_AUTHORITY_PROVIDER_THREADS = "Authority Provider Threads";
+    private static final int DEFAULT_CLUSTER_MANAGER_AUTHORITY_PROVIDER_THREADS = 10;
+
+    private WebClusterManager clusterManager;
+    private ProtocolContext<ProtocolMessage> authorityProviderProtocolContext;
+    private SocketListener socketListener;
+    private NiFiProperties properties;
+    private ApplicationContext applicationContext;
+
+    @Override
+    public void initialize(final AuthorityProviderInitializationContext initializationContext) throws ProviderCreationException {
+        super.initialize(initializationContext);
+    }
+
+    @Override
+    public void onConfigured(final AuthorityProviderConfigurationContext configurationContext) throws ProviderCreationException {
+        super.onConfigured(configurationContext);
+
+        // get the socket address of the cluster authority provider
+        final InetSocketAddress clusterAuthorityProviderAddress = getClusterManagerAuthorityProviderAddress(configurationContext);
+
+        // get the cluster manager
+        clusterManager = applicationContext.getBean("clusterManager", WebClusterManager.class);
+
+        // if using multicast, then the authority provider's service is broadcasted
+        if (properties.getClusterProtocolUseMulticast()) {
+
+            // create the authority provider service for discovery
+            final DiscoverableService clusterAuthorityProviderService = new DiscoverableServiceImpl(AUTHORITY_PROVIDER_SERVIVE_NAME, clusterAuthorityProviderAddress);
+
+            // register the authority provider service with the cluster manager
+            clusterManager.addBroadcastedService(clusterAuthorityProviderService);
+        }
+
+        // get the number of protocol listening thread
+        final int numThreads = getClusterManagerAuthorityProviderThreads(configurationContext);
+
+        // the server socket configuration
+        final ServerSocketConfiguration configuration = applicationContext.getBean("protocolServerSocketConfiguration", ServerSocketConfiguration.class);
+
+        // the authority provider listens for node messages
+        socketListener = new SocketListener(numThreads, clusterAuthorityProviderAddress.getPort(), configuration) {
+            @Override
+            public void dispatchRequest(final Socket socket) {
+                ClusterManagerAuthorizationProvider.this.dispatchRequest(socket);
+            }
+        };
+
+        // start the socket listener
+        if (socketListener != null && !socketListener.isRunning()) {
+            try {
+                socketListener.start();
+            } catch (final IOException ioe) {
+                throw new ProviderCreationException("Failed to start Cluster Manager Authorization Provider due to: " + ioe, ioe);
+            }
+        }
+
+        // initialize the protocol context
+        authorityProviderProtocolContext = new JaxbProtocolContext<ProtocolMessage>(JaxbProtocolUtils.JAXB_CONTEXT);
+    }
+
+    @Override
+    public void preDestruction() throws ProviderDestructionException {
+        if (socketListener != null && socketListener.isRunning()) {
+            try {
+                socketListener.stop();
+            } catch (final IOException ioe) {
+                throw new ProviderDestructionException("Failed to stop Cluster Manager Authorization Provider due to: " + ioe, ioe);
+            }
+        }
+        super.preDestruction();
+    }
+
+    private int getClusterManagerAuthorityProviderThreads(final AuthorityProviderConfigurationContext configurationContext) {
+        try {
+            return Integer.parseInt(configurationContext.getProperty(CLUSTER_MANAGER_AUTHORITY_PROVIDER_THREADS));
+        } catch (NumberFormatException nfe) {
+            return DEFAULT_CLUSTER_MANAGER_AUTHORITY_PROVIDER_THREADS;
+        }
+    }
+
+    private InetSocketAddress getClusterManagerAuthorityProviderAddress(final AuthorityProviderConfigurationContext configurationContext) {
+        try {
+            String socketAddress = properties.getProperty(CLUSTER_MANAGER_ADDRESS);
+            if (StringUtils.isBlank(socketAddress)) {
+                socketAddress = "localhost";
+            }
+            return InetSocketAddress.createUnresolved(socketAddress, getClusterManagerAuthorityProviderPort(configurationContext));
+        } catch (Exception ex) {
+            throw new RuntimeException("Invalid manager authority provider address/port due to: " + ex, ex);
+        }
+    }
+
+    private Integer getClusterManagerAuthorityProviderPort(final AuthorityProviderConfigurationContext configurationContext) {
+        final String authorityProviderPort = configurationContext.getProperty(CLUSTER_MANAGER_AUTHORITY_PROVIDER_PORT);
+        if (authorityProviderPort == null || authorityProviderPort.trim().isEmpty()) {
+            throw new ProviderCreationException("The authority provider port must be specified.");
+        }
+
+        return Integer.parseInt(authorityProviderPort);
+    }
+
+    private void dispatchRequest(final Socket socket) {
+        try {
+            // unmarshall message
+            final ProtocolMessageUnmarshaller<ProtocolMessage> unmarshaller = authorityProviderProtocolContext.createUnmarshaller();
+            final ProtocolMessage request = unmarshaller.unmarshal(socket.getInputStream());
+            final ProtocolMessage response = request;
+
+            try {
+                switch (request.getType()) {
+                    case DOES_DN_EXIST: {
+                        final DoesDnExistMessage castedMsg = (DoesDnExistMessage) request;
+                        castedMsg.setResponse(doesDnExist(castedMsg.getDn()));
+                        break;
+                    }
+                    case GET_AUTHORITIES: {
+                        final GetAuthoritiesMessage castedMsg = (GetAuthoritiesMessage) request;
+                        castedMsg.setResponse(getAuthorities(castedMsg.getDn()));
+                        break;
+                    }
+                    case GET_GROUP_FOR_USER: {
+                        final GetGroupForUserMessage castedMsg = (GetGroupForUserMessage) request;
+                        castedMsg.setResponse(getGroupForUser(castedMsg.getDn()));
+                        break;
+                    }
+                    default: {
+                        throw new Exception("Unsupported Message Type: " + request.getType());
+                    }
+                }
+            } catch (final Exception ex) {
+                response.setExceptionClass(ex.getClass().getName());
+                response.setExceptionMessage(ex.getMessage());
+            }
+
+            final ProtocolMessageMarshaller<ProtocolMessage> marshaller = authorityProviderProtocolContext.createMarshaller();
+            marshaller.marshal(response, socket.getOutputStream());
+
+        } catch (final Exception e) {
+            logger.warn("Failed processing Socket Authorization Provider protocol message due to " + e, e);
+        } finally {
+            SocketUtils.closeQuietly(socket);
+        }
+    }
+
+    @Override
+    @AuthorityProviderContext
+    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
+        this.applicationContext = applicationContext;
+    }
+
+    @Override
+    @AuthorityProviderContext
+    public void setNiFiProperties(NiFiProperties properties) {
+        super.setNiFiProperties(properties);
+        this.properties = properties;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/NodeAuthorizationProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/NodeAuthorizationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/NodeAuthorizationProvider.java
new file mode 100644
index 0000000..840422f
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/NodeAuthorizationProvider.java
@@ -0,0 +1,389 @@
+/*
+ * 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.cluster.authorization;
+
+import org.apache.nifi.cluster.authorization.protocol.message.DoesDnExistMessage;
+import org.apache.nifi.cluster.authorization.protocol.message.GetAuthoritiesMessage;
+import org.apache.nifi.cluster.authorization.protocol.message.ProtocolMessage;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.apache.nifi.authorization.Authority;
+import org.apache.nifi.authorization.AuthorityProvider;
+import org.apache.nifi.authorization.AuthorityProviderConfigurationContext;
+import org.apache.nifi.authorization.AuthorityProviderInitializationContext;
+import org.apache.nifi.authorization.DownloadAuthorization;
+import org.apache.nifi.authorization.annotation.AuthorityProviderContext;
+import org.apache.nifi.authorization.exception.AuthorityAccessException;
+import org.apache.nifi.authorization.exception.IdentityAlreadyExistsException;
+import org.apache.nifi.authorization.exception.ProviderCreationException;
+import org.apache.nifi.authorization.exception.ProviderDestructionException;
+import org.apache.nifi.authorization.exception.UnknownIdentityException;
+import org.apache.nifi.cluster.authorization.protocol.message.GetGroupForUserMessage;
+import org.apache.nifi.cluster.authorization.protocol.message.jaxb.JaxbProtocolUtils;
+import org.apache.nifi.io.socket.SocketConfiguration;
+import org.apache.nifi.io.socket.SocketUtils;
+import org.apache.nifi.io.socket.multicast.DiscoverableService;
+import org.apache.nifi.cluster.protocol.ProtocolContext;
+import org.apache.nifi.cluster.protocol.ProtocolMessageMarshaller;
+import org.apache.nifi.cluster.protocol.ProtocolMessageUnmarshaller;
+import org.apache.nifi.cluster.protocol.impl.ClusterServiceDiscovery;
+import org.apache.nifi.cluster.protocol.impl.ClusterServiceLocator;
+import org.apache.nifi.cluster.protocol.jaxb.JaxbProtocolContext;
+import org.apache.nifi.io.socket.multicast.DiscoverableServiceImpl;
+import org.apache.nifi.io.socket.multicast.MulticastConfiguration;
+import org.apache.nifi.logging.NiFiLog;
+import org.apache.nifi.util.NiFiProperties;
+import static org.apache.nifi.util.NiFiProperties.CLUSTER_NODE_UNICAST_MANAGER_ADDRESS;
+import org.apache.nifi.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+/**
+ * Provides authorities for nodes in clustered environments. Communication
+ * occurs over TCP/IP sockets. All method calls are communicated to the cluster
+ * manager provider via socket.
+ */
+public class NodeAuthorizationProvider implements AuthorityProvider, ApplicationContextAware {
+
+    private static final Logger logger = new NiFiLog(LoggerFactory.getLogger(NodeAuthorizationProvider.class));
+    private static final String CLUSTER_NODE_MANAGER_AUTHORITY_PROVIDER_PORT = "Cluster Manager Authority Provider Port";
+
+    private ProtocolContext<ProtocolMessage> authorityProviderProtocolContext;
+    private SocketConfiguration socketConfiguration;
+    private ClusterServiceLocator serviceLocator;
+    private ApplicationContext applicationContext;
+    private NiFiProperties properties;
+
+    @Override
+    public void initialize(AuthorityProviderInitializationContext initializationContext) throws ProviderCreationException {
+    }
+
+    @Override
+    public void onConfigured(final AuthorityProviderConfigurationContext configurationContext) throws ProviderCreationException {
+        // TODO clear user cache?
+
+        // if using multicast, then the authority provider's service is broadcasted
+        if (properties.getClusterProtocolUseMulticast()) {
+            // create the service discovery
+            final ClusterServiceDiscovery serviceDiscovery = new ClusterServiceDiscovery(
+                    ClusterManagerAuthorizationProvider.AUTHORITY_PROVIDER_SERVIVE_NAME,
+                    properties.getClusterProtocolMulticastAddress(),
+                    applicationContext.getBean("protocolMulticastConfiguration", MulticastConfiguration.class),
+                    applicationContext.getBean("protocolContext", ProtocolContext.class));
+
+            // create service location configuration
+            final ClusterServiceLocator.AttemptsConfig config = new ClusterServiceLocator.AttemptsConfig();
+            config.setNumAttempts(3);
+            config.setTimeBetweenAttempts(1);
+            config.setTimeBetweenAttempsUnit(TimeUnit.SECONDS);
+
+            serviceLocator = new ClusterServiceLocator(serviceDiscovery);
+            serviceLocator.setAttemptsConfig(config);
+        } else {
+            final InetSocketAddress serviceAddress = getClusterNodeManagerAuthorityProviderAddress(configurationContext);
+            final DiscoverableService service = new DiscoverableServiceImpl(ClusterManagerAuthorizationProvider.AUTHORITY_PROVIDER_SERVIVE_NAME, serviceAddress);
+            serviceLocator = new ClusterServiceLocator(service);
+        }
+
+        try {
+            // start the service locator
+            serviceLocator.start();
+        } catch (final IOException ioe) {
+            throw new ProviderCreationException(ioe);
+        }
+
+        // the socket configuration
+        socketConfiguration = applicationContext.getBean("protocolSocketConfiguration", SocketConfiguration.class);
+
+        // initialize the protocol context
+        authorityProviderProtocolContext = new JaxbProtocolContext<ProtocolMessage>(JaxbProtocolUtils.JAXB_CONTEXT);
+    }
+
+    private InetSocketAddress getClusterNodeManagerAuthorityProviderAddress(final AuthorityProviderConfigurationContext configurationContext) {
+        try {
+            String socketAddress = properties.getProperty(CLUSTER_NODE_UNICAST_MANAGER_ADDRESS);
+            if (StringUtils.isBlank(socketAddress)) {
+                socketAddress = "localhost";
+            }
+            return InetSocketAddress.createUnresolved(socketAddress, getClusterNodeManagerAuthorityProviderPort(configurationContext));
+        } catch (Exception ex) {
+            throw new ProviderCreationException("Invalid cluster manager authority provider address/port due to: " + ex, ex);
+        }
+    }
+
+    private Integer getClusterNodeManagerAuthorityProviderPort(final AuthorityProviderConfigurationContext configurationContext) {
+        final String nodeAuthorityProviderPort = configurationContext.getProperty(CLUSTER_NODE_MANAGER_AUTHORITY_PROVIDER_PORT);
+        if (nodeAuthorityProviderPort == null || nodeAuthorityProviderPort.trim().isEmpty()) {
+            throw new ProviderCreationException("The cluster manager authority provider port must be specified.");
+        }
+
+        return Integer.parseInt(nodeAuthorityProviderPort);
+    }
+
+    @Override
+    public void setAuthorities(String dn, Set<Authority> authorities) throws AuthorityAccessException {
+        throw new AuthorityAccessException("Nodes are not allowed to set user authorities.");
+    }
+
+    @Override
+    public void addUser(String dn, String group) throws IdentityAlreadyExistsException, AuthorityAccessException {
+        throw new AuthorityAccessException("Nodes are not allowed to add users.");
+    }
+
+    @Override
+    public boolean doesDnExist(String dn) throws AuthorityAccessException {
+        // create message
+        final DoesDnExistMessage msg = new DoesDnExistMessage();
+        msg.setDn(dn);
+
+        Socket socket = null;
+        try {
+
+            final InetSocketAddress socketAddress = getServiceAddress();
+            if (socketAddress == null) {
+                throw new AuthorityAccessException("Cluster Authority Provider's address is not known.");
+            }
+
+            try {
+                // create a socket
+                socket = SocketUtils.createSocket(socketAddress, socketConfiguration);
+            } catch (final IOException ioe) {
+                throw new AuthorityAccessException("Failed to create socket due to: " + ioe, ioe);
+            }
+
+            try {
+                // marshal message to output stream
+                final ProtocolMessageMarshaller marshaller = authorityProviderProtocolContext.createMarshaller();
+                marshaller.marshal(msg, socket.getOutputStream());
+            } catch (final IOException ioe) {
+                throw new AuthorityAccessException("Failed marshalling '" + msg.getType() + "' protocol message due to: " + ioe, ioe);
+            }
+
+            try {
+
+                // unmarshall response and return
+                final ProtocolMessageUnmarshaller<ProtocolMessage> unmarshaller = authorityProviderProtocolContext.createUnmarshaller();
+                final DoesDnExistMessage response = (DoesDnExistMessage) unmarshaller.unmarshal(socket.getInputStream());
+
+                // check if there was an exception
+                if (response.wasException()) {
+                    throw new AuthorityAccessException(response.getExceptionMessage());
+                }
+
+                // return provider's response
+                return response.getResponse();
+
+            } catch (final IOException ioe) {
+                throw new AuthorityAccessException("Failed unmarshalling '" + msg.getType() + "' response protocol message due to: " + ioe, ioe);
+            }
+
+        } finally {
+            SocketUtils.closeQuietly(socket);
+        }
+    }
+
+    @Override
+    public Set<Authority> getAuthorities(String dn) throws UnknownIdentityException, AuthorityAccessException {
+        // create message
+        final GetAuthoritiesMessage msg = new GetAuthoritiesMessage();
+        msg.setDn(dn);
+
+        Socket socket = null;
+        try {
+
+            final InetSocketAddress socketAddress = getServiceAddress();
+            if (socketAddress == null) {
+                throw new AuthorityAccessException("Cluster Authority Provider's address is not known.");
+            }
+
+            try {
+                // create a socket
+                socket = SocketUtils.createSocket(socketAddress, socketConfiguration);
+            } catch (final IOException ioe) {
+                throw new AuthorityAccessException("Failed to create socket due to: " + ioe, ioe);
+            }
+
+            try {
+                // marshal message to output stream
+                final ProtocolMessageMarshaller marshaller = authorityProviderProtocolContext.createMarshaller();
+                marshaller.marshal(msg, socket.getOutputStream());
+            } catch (final IOException ioe) {
+                throw new AuthorityAccessException("Failed marshalling '" + msg.getType() + "' protocol message due to: " + ioe, ioe);
+            }
+
+            try {
+
+                // unmarshall response and return
+                final ProtocolMessageUnmarshaller<ProtocolMessage> unmarshaller = authorityProviderProtocolContext.createUnmarshaller();
+                final GetAuthoritiesMessage response = (GetAuthoritiesMessage) unmarshaller.unmarshal(socket.getInputStream());
+
+                // check if there was an exception
+                if (response.wasException()) {
+                    if (isException(UnknownIdentityException.class, response)) {
+                        throw new UnknownIdentityException(response.getExceptionMessage());
+                    } else {
+                        throw new AuthorityAccessException(response.getExceptionMessage());
+                    }
+                }
+
+                // return provider's response
+                return response.getResponse();
+
+            } catch (final IOException ioe) {
+                throw new AuthorityAccessException("Failed unmarshalling '" + msg.getType() + "' response protocol message due to: " + ioe, ioe);
+            }
+
+        } finally {
+            SocketUtils.closeQuietly(socket);
+        }
+    }
+
+    @Override
+    public Set<String> getUsers(Authority authority) throws AuthorityAccessException {
+        throw new AuthorityAccessException("Nodes are not allowed to get users for a given authority.");
+    }
+
+    @Override
+    public void revokeUser(String dn) throws UnknownIdentityException, AuthorityAccessException {
+        throw new AuthorityAccessException("Nodes are not allowed to revoke users.");
+    }
+
+    @Override
+    public void setUsersGroup(Set<String> dns, String group) throws UnknownIdentityException, AuthorityAccessException {
+        throw new AuthorityAccessException("Nodes are not allowed to set user groups.");
+    }
+
+    @Override
+    public void ungroupUser(String dn) throws UnknownIdentityException, AuthorityAccessException {
+        throw new AuthorityAccessException("Nodes are not allowed to ungroup users.");
+    }
+
+    @Override
+    public void ungroup(String group) throws AuthorityAccessException {
+        throw new AuthorityAccessException("Nodes are not allowed to ungroup.");
+    }
+
+    @Override
+    public DownloadAuthorization authorizeDownload(List<String> dnChain, Map<String, String> attributes) throws UnknownIdentityException, AuthorityAccessException {
+        return DownloadAuthorization.approved();
+    }
+
+    @Override
+    public String getGroupForUser(String dn) throws UnknownIdentityException, AuthorityAccessException {
+        // create message
+        final GetGroupForUserMessage msg = new GetGroupForUserMessage();
+        msg.setDn(dn);
+
+        Socket socket = null;
+        try {
+
+            final InetSocketAddress socketAddress = getServiceAddress();
+            if (socketAddress == null) {
+                throw new AuthorityAccessException("Cluster Authority Provider's address is not known.");
+            }
+
+            try {
+                // create a socket
+                socket = SocketUtils.createSocket(socketAddress, socketConfiguration);
+            } catch (final IOException ioe) {
+                throw new AuthorityAccessException("Failed to create socket due to: " + ioe, ioe);
+            }
+
+            try {
+                // marshal message to output stream
+                final ProtocolMessageMarshaller marshaller = authorityProviderProtocolContext.createMarshaller();
+                marshaller.marshal(msg, socket.getOutputStream());
+            } catch (final IOException ioe) {
+                throw new AuthorityAccessException("Failed marshalling '" + msg.getType() + "' protocol message due to: " + ioe, ioe);
+            }
+
+            try {
+
+                // unmarshall response and return
+                final ProtocolMessageUnmarshaller<ProtocolMessage> unmarshaller = authorityProviderProtocolContext.createUnmarshaller();
+                final GetGroupForUserMessage response = (GetGroupForUserMessage) unmarshaller.unmarshal(socket.getInputStream());
+
+                // check if there was an exception
+                if (response.wasException()) {
+                    if (isException(UnknownIdentityException.class, response)) {
+                        throw new UnknownIdentityException(response.getExceptionMessage());
+                    } else {
+                        throw new AuthorityAccessException(response.getExceptionMessage());
+                    }
+                }
+
+                return response.getResponse();
+            } catch (final IOException ioe) {
+                throw new AuthorityAccessException("Failed unmarshalling '" + msg.getType() + "' response protocol message due to: " + ioe, ioe);
+            }
+
+        } finally {
+            SocketUtils.closeQuietly(socket);
+        }
+    }
+
+    @Override
+    public void revokeGroup(String group) throws UnknownIdentityException, AuthorityAccessException {
+        throw new AuthorityAccessException("Nodes are not allowed to revoke groups.");
+    }
+
+    @Override
+    public void preDestruction() throws ProviderDestructionException {
+        try {
+            if (serviceLocator != null && serviceLocator.isRunning()) {
+                serviceLocator.stop();
+            }
+        } catch (final IOException ioe) {
+            throw new ProviderDestructionException(ioe);
+        }
+    }
+
+    @Override
+    @AuthorityProviderContext
+    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
+        this.applicationContext = applicationContext;
+    }
+
+    @AuthorityProviderContext
+    public void setNiFiProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    private InetSocketAddress getServiceAddress() {
+        final DiscoverableService service = serviceLocator.getService();
+        if (service != null) {
+            return service.getServiceAddress();
+        }
+        return null;
+    }
+
+    private boolean isException(final Class<? extends Exception> exception, final ProtocolMessage protocolMessage) {
+        if (protocolMessage.wasException()) {
+            return exception.getName().equals(protocolMessage.getExceptionClass());
+        } else {
+            return false;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/DoesDnExistMessage.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/DoesDnExistMessage.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/DoesDnExistMessage.java
new file mode 100644
index 0000000..5436140
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/DoesDnExistMessage.java
@@ -0,0 +1,55 @@
+/*
+ * 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.cluster.authorization.protocol.message;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.cluster.authorization.protocol.message.ProtocolMessage.MessageType;
+
+/**
+ */
+@XmlRootElement(name = "doesDnExistMessage")
+public class DoesDnExistMessage extends ProtocolMessage {
+
+    private String dn;
+
+    private boolean response;
+
+    public DoesDnExistMessage() {
+    }
+
+    @Override
+    public MessageType getType() {
+        return MessageType.DOES_DN_EXIST;
+    }
+
+    public String getDn() {
+        return dn;
+    }
+
+    public void setDn(String dn) {
+        this.dn = dn;
+    }
+
+    public boolean getResponse() {
+        return response;
+    }
+
+    public void setResponse(boolean response) {
+        this.response = response;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/GetAuthoritiesMessage.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/GetAuthoritiesMessage.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/GetAuthoritiesMessage.java
new file mode 100644
index 0000000..50d371d
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/GetAuthoritiesMessage.java
@@ -0,0 +1,57 @@
+/*
+ * 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.cluster.authorization.protocol.message;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.authorization.Authority;
+
+/**
+ */
+@XmlRootElement(name = "getAuthoritiesMessage")
+public class GetAuthoritiesMessage extends ProtocolMessage {
+
+    private String dn;
+
+    private Set<Authority> response = new HashSet<>();
+
+    public GetAuthoritiesMessage() {
+    }
+
+    @Override
+    public MessageType getType() {
+        return MessageType.GET_AUTHORITIES;
+    }
+
+    public String getDn() {
+        return dn;
+    }
+
+    public void setDn(String dn) {
+        this.dn = dn;
+    }
+
+    public Set<Authority> getResponse() {
+        return response;
+    }
+
+    public void setResponse(Set<Authority> response) {
+        this.response = response;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/GetGroupForUserMessage.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/GetGroupForUserMessage.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/GetGroupForUserMessage.java
new file mode 100644
index 0000000..72a6af5
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/GetGroupForUserMessage.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.cluster.authorization.protocol.message;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ */
+@XmlRootElement(name = "getGroupForUserMessage")
+public class GetGroupForUserMessage extends ProtocolMessage {
+
+    private String dn;
+
+    private String response;
+
+    public GetGroupForUserMessage() {
+    }
+
+    @Override
+    public MessageType getType() {
+        return MessageType.GET_GROUP_FOR_USER;
+    }
+
+    public String getDn() {
+        return dn;
+    }
+
+    public void setDn(String dn) {
+        this.dn = dn;
+    }
+
+    public String getResponse() {
+        return response;
+    }
+
+    public void setResponse(String response) {
+        this.response = response;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/ProtocolMessage.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/ProtocolMessage.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/ProtocolMessage.java
new file mode 100644
index 0000000..ddeb69e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/ProtocolMessage.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.cluster.authorization.protocol.message;
+
+/**
+ */
+public abstract class ProtocolMessage {
+
+    private String exceptionClass;
+    private String exceptionMessage;
+
+    public static enum MessageType {
+
+        DOES_DN_EXIST,
+        GET_AUTHORITIES,
+        GET_USERS,
+        GET_GROUP_FOR_USER
+    }
+
+    public abstract MessageType getType();
+
+    public boolean wasException() {
+        return exceptionClass != null;
+    }
+
+    public String getExceptionMessage() {
+        return exceptionMessage;
+    }
+
+    public void setExceptionMessage(final String exceptionMessage) {
+        this.exceptionMessage = exceptionMessage;
+    }
+
+    public String getExceptionClass() {
+        return exceptionClass;
+    }
+
+    public void setExceptionClass(String exceptionClass) {
+        this.exceptionClass = exceptionClass;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/jaxb/JaxbProtocolUtils.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/jaxb/JaxbProtocolUtils.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/jaxb/JaxbProtocolUtils.java
new file mode 100644
index 0000000..2a32d84
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/jaxb/JaxbProtocolUtils.java
@@ -0,0 +1,41 @@
+/*
+ * 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.cluster.authorization.protocol.message.jaxb;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+
+/**
+ */
+public final class JaxbProtocolUtils {
+
+    public static final String JAXB_CONTEXT_PATH = ObjectFactory.class.getPackage().getName();
+
+    public static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
+
+    /**
+     * Load the JAXBContext version.
+     */
+    private static JAXBContext initializeJaxbContext() {
+        try {
+            return JAXBContext.newInstance(JAXB_CONTEXT_PATH);
+        } catch (JAXBException e) {
+            throw new RuntimeException("Unable to create JAXBContext.");
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/jaxb/ObjectFactory.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/jaxb/ObjectFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/jaxb/ObjectFactory.java
new file mode 100644
index 0000000..2e70a19
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/java/org/apache/nifi/cluster/authorization/protocol/message/jaxb/ObjectFactory.java
@@ -0,0 +1,44 @@
+/*
+ * 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.cluster.authorization.protocol.message.jaxb;
+
+import javax.xml.bind.annotation.XmlRegistry;
+import org.apache.nifi.cluster.authorization.protocol.message.DoesDnExistMessage;
+import org.apache.nifi.cluster.authorization.protocol.message.GetAuthoritiesMessage;
+import org.apache.nifi.cluster.authorization.protocol.message.GetGroupForUserMessage;
+
+/**
+ */
+@XmlRegistry
+public class ObjectFactory {
+
+    public ObjectFactory() {
+    }
+
+    public DoesDnExistMessage createDoesDnExistMessage() {
+        return new DoesDnExistMessage();
+    }
+
+    public GetAuthoritiesMessage createGetAuthoritiesMessage() {
+        return new GetAuthoritiesMessage();
+    }
+
+    public GetGroupForUserMessage createGetGroupForUserMessage() {
+        return new GetGroupForUserMessage();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/resources/META-INF/services/org.apache.nifi.authorization.AuthorityProvider
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/resources/META-INF/services/org.apache.nifi.authorization.AuthorityProvider b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/resources/META-INF/services/org.apache.nifi.authorization.AuthorityProvider
new file mode 100644
index 0000000..56f4c3e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-cluster-authorization-provider/src/main/resources/META-INF/services/org.apache.nifi.authorization.AuthorityProvider
@@ -0,0 +1,16 @@
+# 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.
+org.apache.nifi.cluster.authorization.ClusterManagerAuthorizationProvider
+org.apache.nifi.cluster.authorization.NodeAuthorizationProvider
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/pom.xml
new file mode 100644
index 0000000..caa75de
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorization-provider/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-framework</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-file-authorization-provider</artifactId>
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+            <resource>
+                <directory>src/main/xsd</directory>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>jaxb2-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>xjc</id>
+                        <goals>
+                            <goal>xjc</goal>
+                        </goals>
+                        <configuration>
+                            <packageName>org.apache.nifi.user.generated</packageName>
+                        </configuration>
+                    </execution>
+                </executions>
+                <configuration>
+                    <generateDirectory>${project.build.directory}/generated-sources/jaxb</generateDirectory>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <configuration>
+                    <excludes>**/user/generated/*.java</excludes>
+                </configuration>
+            </plugin>            
+
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-properties</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>