http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardUserService.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardUserService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardUserService.java new file mode 100644 index 0000000..424816f --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardUserService.java @@ -0,0 +1,642 @@ +/* + * 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.impl; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.nifi.admin.dao.DataAccessException; +import org.apache.nifi.admin.service.AccountDisabledException; +import org.apache.nifi.admin.service.AccountPendingException; +import org.apache.nifi.admin.service.AdministrationException; +import org.apache.nifi.admin.service.UserService; +import org.apache.nifi.admin.service.action.AuthorizeDownloadAction; +import org.apache.nifi.admin.service.action.AuthorizeUserAction; +import org.apache.nifi.admin.service.action.DeleteUserAction; +import org.apache.nifi.admin.service.action.DisableUserAction; +import org.apache.nifi.admin.service.action.DisableUserGroupAction; +import org.apache.nifi.admin.service.action.FindUserByDnAction; +import org.apache.nifi.admin.service.action.FindUserByIdAction; +import org.apache.nifi.admin.service.action.GetUserGroupAction; +import org.apache.nifi.admin.service.action.GetUsersAction; +import org.apache.nifi.admin.service.action.HasPendingUserAccounts; +import org.apache.nifi.admin.service.action.InvalidateUserAccountAction; +import org.apache.nifi.admin.service.action.InvalidateUserGroupAccountsAction; +import org.apache.nifi.admin.service.action.RequestUserAccountAction; +import org.apache.nifi.admin.service.action.SeedUserAccountsAction; +import org.apache.nifi.admin.service.action.UpdateUserAction; +import org.apache.nifi.admin.service.action.UpdateUserGroupAction; +import org.apache.nifi.admin.service.action.UngroupUserAction; +import org.apache.nifi.admin.service.action.UngroupUserGroupAction; +import org.apache.nifi.admin.service.transaction.Transaction; +import org.apache.nifi.admin.service.transaction.TransactionBuilder; +import org.apache.nifi.admin.service.transaction.TransactionException; +import org.apache.nifi.authorization.Authority; +import org.apache.nifi.authorization.DownloadAuthorization; +import org.apache.nifi.user.NiFiUser; +import org.apache.nifi.user.NiFiUserGroup; +import org.apache.nifi.util.FormatUtils; +import org.apache.nifi.util.NiFiProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + */ +public class StandardUserService implements UserService { + + private static final Logger logger = LoggerFactory.getLogger(StandardUserService.class); + + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); + private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); + + private TransactionBuilder transactionBuilder; + private NiFiProperties properties; + + /** + * Seed any users from the authority provider that are not already present. + */ + public void seedUserAccounts() { + // do not seed node's user cache. when/if the node disconnects its + // cache will be populated lazily (as needed) + if (properties.isNode()) { + return; + } + + Transaction transaction = null; + writeLock.lock(); + try { + // start the transaction + transaction = transactionBuilder.start(); + + // seed the accounts + SeedUserAccountsAction seedUserAccounts = new SeedUserAccountsAction(); + transaction.execute(seedUserAccounts); + + // commit the transaction + transaction.commit(); + } catch (AdministrationException ae) { + rollback(transaction); + throw ae; + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + writeLock.unlock(); + } + } + + @Override + public NiFiUser createPendingUserAccount(String dn, String justification) { + Transaction transaction = null; + + writeLock.lock(); + try { + // start the transaction + transaction = transactionBuilder.start(); + + // create the account request + RequestUserAccountAction requestUserAccount = new RequestUserAccountAction(dn, justification); + NiFiUser user = transaction.execute(requestUserAccount); + + // commit the transaction + transaction.commit(); + + // return the nifi user + return user; + } catch (TransactionException | DataAccessException te) { + rollback(transaction); + throw new AdministrationException(te); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + writeLock.unlock(); + } + } + + @Override + public NiFiUserGroup updateGroup(final String group, final Set<String> userIds, final Set<Authority> authorities) { + Transaction transaction = null; + + writeLock.lock(); + try { + // if user ids have been specified, invalidate the user accounts before performing + // the desired updates. if case of an error, this will ensure that these users are + // authorized the next time the access the application + if (userIds != null) { + for (final String userId : userIds) { + invalidateUserAccount(userId); + } + } + + // start the transaction + transaction = transactionBuilder.start(); + + // set the authorities for each user in this group if specified + final UpdateUserGroupAction updateUserGroup = new UpdateUserGroupAction(group, userIds, authorities); + transaction.execute(updateUserGroup); + + // get all the users that are now in this group + final GetUserGroupAction getUserGroup = new GetUserGroupAction(group); + final NiFiUserGroup userGroup = transaction.execute(getUserGroup); + + // commit the transaction + transaction.commit(); + + return userGroup; + } catch (TransactionException | DataAccessException te) { + rollback(transaction); + throw new AdministrationException(te); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + writeLock.unlock(); + } + } + + @Override + public void ungroupUser(String id) { + Transaction transaction = null; + + writeLock.lock(); + try { + // start the transaction + transaction = transactionBuilder.start(); + + // ungroup the specified user + final UngroupUserAction ungroupUser = new UngroupUserAction(id); + transaction.execute(ungroupUser); + + // commit the transaction + transaction.commit(); + } catch (TransactionException | DataAccessException te) { + rollback(transaction); + throw new AdministrationException(te); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + writeLock.unlock(); + } + } + + @Override + public void ungroup(String group) { + Transaction transaction = null; + + writeLock.lock(); + try { + // start the transaction + transaction = transactionBuilder.start(); + + // ungroup the specified user + final UngroupUserGroupAction ungroupUserGroup = new UngroupUserGroupAction(group); + transaction.execute(ungroupUserGroup); + + // commit the transaction + transaction.commit(); + } catch (TransactionException | DataAccessException te) { + rollback(transaction); + throw new AdministrationException(te); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + writeLock.unlock(); + } + } + + @Override + public NiFiUser checkAuthorization(String dn) { + Transaction transaction = null; + + writeLock.lock(); + try { + // create the connection + transaction = transactionBuilder.start(); + + // determine how long the cache is valid for + final int cacheSeconds; + try { + cacheSeconds = (int) FormatUtils.getTimeDuration(properties.getUserCredentialCacheDuration(), TimeUnit.SECONDS); + } catch (IllegalArgumentException iae) { + throw new AdministrationException("User credential cache duration is not configured correctly."); + } + + // attempt to authorize the user + AuthorizeUserAction authorizeUser = new AuthorizeUserAction(dn, cacheSeconds); + NiFiUser user = transaction.execute(authorizeUser); + + // commit the transaction + transaction.commit(); + + // return the nifi user + return user; + } catch (DataAccessException | TransactionException dae) { + rollback(transaction); + throw new AdministrationException(dae); + } catch (AccountDisabledException | AccountPendingException ade) { + rollback(transaction); + throw ade; + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + writeLock.unlock(); + } + } + + @Override + public void deleteUser(String id) { + Transaction transaction = null; + + writeLock.lock(); + try { + // create the connection + transaction = transactionBuilder.start(); + + // delete the user + DeleteUserAction deleteUser = new DeleteUserAction(id); + transaction.execute(deleteUser); + + // commit the transaction + transaction.commit(); + } catch (DataAccessException | TransactionException dae) { + rollback(transaction); + throw new AdministrationException(dae); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + writeLock.unlock(); + } + } + + @Override + public NiFiUser disable(String id) { + Transaction transaction = null; + + writeLock.lock(); + try { + // create the connection + transaction = transactionBuilder.start(); + + // disable the user + DisableUserAction disableUser = new DisableUserAction(id); + NiFiUser user = transaction.execute(disableUser); + + // commit the transaction + transaction.commit(); + + // return the user + return user; + } catch (DataAccessException | TransactionException dae) { + rollback(transaction); + throw new AdministrationException(dae); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + writeLock.unlock(); + } + } + + @Override + public NiFiUserGroup disableGroup(String group) { + Transaction transaction = null; + + writeLock.lock(); + try { + // create the connection + transaction = transactionBuilder.start(); + + // disable the user + DisableUserGroupAction disableUser = new DisableUserGroupAction(group); + NiFiUserGroup userGroup = transaction.execute(disableUser); + + // commit the transaction + transaction.commit(); + + // return the user + return userGroup; + } catch (DataAccessException | TransactionException dae) { + rollback(transaction); + throw new AdministrationException(dae); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + writeLock.unlock(); + } + } + + @Override + public NiFiUser update(String id, Set<Authority> authorities) { + Transaction transaction = null; + + // may be empty but not null + if (authorities == null) { + throw new IllegalArgumentException("The specified authorities cannot be null."); + } + + writeLock.lock(); + try { + // invalidate the user account in preparation for potential subsequent errors + invalidateUserAccount(id); + + // at this point the current user account has been invalidated so we will + // attempt to update the account. if any part fails we are assured the + // user will be need to be given approval before they access the system at + // a later time + // start the transaction + transaction = transactionBuilder.start(); + + // update the user authorities + UpdateUserAction setUserAuthorities = new UpdateUserAction(id, authorities); + NiFiUser user = transaction.execute(setUserAuthorities); + + // commit the transaction + transaction.commit(); + + // return the user + return user; + } catch (TransactionException | DataAccessException e) { + rollback(transaction); + throw new AdministrationException(e); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + writeLock.unlock(); + } + } + + /** + * Invalidates the user with the specified id. This is done to ensure a user + * account will need to be re-validated in case an error occurs while + * modifying a user account. This method should only be invoked from within + * a write lock. + * + * @param id user account identifier + */ + @Override + public void invalidateUserAccount(String id) { + Transaction transaction = null; + + writeLock.lock(); + try { + // start the transaction + transaction = transactionBuilder.start(); + + // invalidate the user account + InvalidateUserAccountAction invalidateUserAccount = new InvalidateUserAccountAction(id); + transaction.execute(invalidateUserAccount); + + // commit the transaction + transaction.commit(); + } catch (TransactionException | DataAccessException te) { + rollback(transaction); + throw new AdministrationException(te); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + writeLock.unlock(); + } + } + + @Override + public void invalidateUserGroupAccount(String group) { + Transaction transaction = null; + + writeLock.lock(); + try { + // start the transaction + transaction = transactionBuilder.start(); + + // invalidate the user account + InvalidateUserGroupAccountsAction invalidateUserGroupAccounts = new InvalidateUserGroupAccountsAction(group); + transaction.execute(invalidateUserGroupAccounts); + + // commit the transaction + transaction.commit(); + } catch (TransactionException | DataAccessException te) { + rollback(transaction); + throw new AdministrationException(te); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + writeLock.unlock(); + } + } + + // ----------------- + // read only methods + // ----------------- + @Override + public Boolean hasPendingUserAccount() { + Transaction transaction = null; + + readLock.lock(); + try { + // start the transaction + transaction = transactionBuilder.start(); + + final HasPendingUserAccounts hasPendingAccounts = new HasPendingUserAccounts(); + final Boolean hasPendingUserAccounts = transaction.execute(hasPendingAccounts); + + // commit the transaction + transaction.commit(); + + return hasPendingUserAccounts; + } catch (TransactionException | DataAccessException te) { + rollback(transaction); + throw new AdministrationException(te); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + readLock.unlock(); + } + } + + @Override + public DownloadAuthorization authorizeDownload(final List<String> dnChain, final Map<String, String> attributes) { + Transaction transaction = null; + + readLock.lock(); + try { + // start the transaction + transaction = transactionBuilder.start(); + + // authorize the download + AuthorizeDownloadAction authorizeDownload = new AuthorizeDownloadAction(dnChain, attributes); + DownloadAuthorization downloadAuthorization = transaction.execute(authorizeDownload); + + // commit the transaction + transaction.commit(); + + // return the authorization + return downloadAuthorization; + } catch (TransactionException | DataAccessException te) { + rollback(transaction); + throw new AdministrationException(te); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + readLock.unlock(); + } + } + + @Override + public Collection<NiFiUser> getUsers() { + Transaction transaction = null; + + readLock.lock(); + try { + // start the transaction + transaction = transactionBuilder.start(); + + // get all users + GetUsersAction getUsers = new GetUsersAction(); + Collection<NiFiUser> users = transaction.execute(getUsers); + + // commit the transaction + transaction.commit(); + + // return the users + return users; + } catch (TransactionException | DataAccessException te) { + rollback(transaction); + throw new AdministrationException(te); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + readLock.unlock(); + } + } + + @Override + public NiFiUser getUserById(String id) { + Transaction transaction = null; + + readLock.lock(); + try { + // start the transaction + transaction = transactionBuilder.start(); + + // return the desired user + FindUserByIdAction findUserById = new FindUserByIdAction(id); + NiFiUser user = transaction.execute(findUserById); + + // commit the transaction + transaction.commit(); + + // return the user + return user; + } catch (TransactionException | DataAccessException te) { + rollback(transaction); + throw new AdministrationException(te); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + readLock.unlock(); + } + } + + @Override + public NiFiUser getUserByDn(String dn) { + Transaction transaction = null; + + readLock.lock(); + try { + // start the transaction + transaction = transactionBuilder.start(); + + // return the desired user + FindUserByDnAction findUserByDn = new FindUserByDnAction(dn); + NiFiUser user = transaction.execute(findUserByDn); + + // commit the transaction + transaction.commit(); + + // return the user + return user; + } catch (TransactionException | DataAccessException te) { + rollback(transaction); + throw new AdministrationException(te); + } catch (Throwable t) { + rollback(transaction); + throw t; + } finally { + closeQuietly(transaction); + readLock.unlock(); + } + } + + private void rollback(final Transaction transaction) { + if (transaction != null) { + transaction.rollback(); + } + } + + private void closeQuietly(final Transaction transaction) { + if (transaction != null) { + try { + transaction.close(); + } catch (final IOException ioe) { + } + } + } + + public void setTransactionBuilder(TransactionBuilder transactionBuilder) { + this.transactionBuilder = transactionBuilder; + } + + public void setProperties(NiFiProperties properties) { + this.properties = properties; + } + +}
http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/Transaction.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/Transaction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/Transaction.java new file mode 100644 index 0000000..4792581 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/Transaction.java @@ -0,0 +1,49 @@ +/* + * 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.transaction; + +import java.io.Closeable; +import org.apache.nifi.admin.service.action.AdministrationAction; + +/** + * Defines a transaction. + */ +public interface Transaction extends Closeable { + + /** + * Executes the specified action within the current transaction. + * + * @param <T> type of action to execute + * @param action action to execute + * @return executed action + * @throws IllegalStateException - if there is no current transaction + */ + <T> T execute(AdministrationAction<T> action); + + /** + * Commits the current transaction. + * + * @throws TransactionException - if the transaction is unable to be + * committed + */ + void commit() throws TransactionException; + + /** + * Rolls back the current transaction. + */ + void rollback(); +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/TransactionBuilder.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/TransactionBuilder.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/TransactionBuilder.java new file mode 100644 index 0000000..2d2ef82 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/TransactionBuilder.java @@ -0,0 +1,25 @@ +/* + * 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.transaction; + +/** + * + */ +public interface TransactionBuilder { + + Transaction start() throws TransactionException; +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/TransactionException.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/TransactionException.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/TransactionException.java new file mode 100644 index 0000000..924e01f --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/TransactionException.java @@ -0,0 +1,40 @@ +/* + * 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.transaction; + +/** + * Exception to indicate that the user account is disabled. + */ +public class TransactionException extends RuntimeException { + + public TransactionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public TransactionException(Throwable cause) { + super(cause); + } + + public TransactionException(String message, Throwable cause) { + super(message, cause); + } + + public TransactionException(String message) { + super(message); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/impl/StandardTransaction.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/impl/StandardTransaction.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/impl/StandardTransaction.java new file mode 100644 index 0000000..a3cfb5e --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/impl/StandardTransaction.java @@ -0,0 +1,93 @@ +/* + * 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.transaction.impl; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; +import org.apache.nifi.admin.RepositoryUtils; +import org.apache.nifi.admin.dao.DAOFactory; +import org.apache.nifi.admin.dao.impl.DAOFactoryImpl; +import org.apache.nifi.admin.service.action.AdministrationAction; +import org.apache.nifi.admin.service.transaction.TransactionException; +import org.apache.nifi.admin.service.transaction.Transaction; +import org.apache.nifi.authorization.AuthorityProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Transaction implementation that uses the specified SQL Connection and + * AuthorityProvider. + */ +public class StandardTransaction implements Transaction { + + private static final Logger logger = LoggerFactory.getLogger(StandardTransaction.class); + + private final AuthorityProvider authorityProvider; + private Connection connection; + + public StandardTransaction(AuthorityProvider authorityProvider, Connection connection) { + this.authorityProvider = authorityProvider; + this.connection = connection; + } + + @Override + public <T> T execute(AdministrationAction<T> action) { + // ensure the transaction has been started + if (connection == null) { + throw new IllegalStateException("This transaction is not active."); + } + + // create a dao factory + DAOFactory daoFactory = new DAOFactoryImpl(connection); + + // execute the specified action + return action.execute(daoFactory, authorityProvider); + } + + @Override + public void commit() throws TransactionException { + // ensure there is an active transaction + if (connection == null) { + throw new IllegalStateException("No active transaction."); + } + + try { + // commit the transaction + connection.commit(); + } catch (SQLException sqle) { + throw new TransactionException(sqle.getMessage()); + } + } + + @Override + public void rollback() { + // ensure there is an active transaction + if (connection != null) { + // rollback the transaction + RepositoryUtils.rollback(connection, logger); + } + } + + @Override + public void close() throws IOException { + if (connection != null) { + RepositoryUtils.closeQuietly(connection); + connection = null; + } + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/impl/StandardTransactionBuilder.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/impl/StandardTransactionBuilder.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/impl/StandardTransactionBuilder.java new file mode 100644 index 0000000..b6e5a30 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/transaction/impl/StandardTransactionBuilder.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.admin.service.transaction.impl; + +import java.sql.Connection; +import java.sql.SQLException; +import javax.sql.DataSource; +import org.apache.nifi.admin.service.transaction.Transaction; +import org.apache.nifi.admin.service.transaction.TransactionBuilder; +import org.apache.nifi.admin.service.transaction.TransactionException; +import org.apache.nifi.authorization.AuthorityProvider; + +/** + * + */ +public class StandardTransactionBuilder implements TransactionBuilder { + + private DataSource dataSource; + private AuthorityProvider authorityProvider; + + @Override + public Transaction start() throws TransactionException { + try { + // get a new connection + Connection connection = dataSource.getConnection(); + connection.setAutoCommit(false); + + // create a new transaction + return new StandardTransaction(authorityProvider, connection); + } catch (SQLException sqle) { + throw new TransactionException(sqle.getMessage()); + } + } + + /* setters */ + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + public void setAuthorityProvider(AuthorityProvider authorityProvider) { + this.authorityProvider = authorityProvider; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/AuthorityProviderFactoryBean.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/AuthorityProviderFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/AuthorityProviderFactoryBean.java new file mode 100644 index 0000000..b3e9547 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/AuthorityProviderFactoryBean.java @@ -0,0 +1,490 @@ +/* + * 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.authorization; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.xml.XMLConstants; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +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.authorization.generated.AuthorityProviders; +import org.apache.nifi.authorization.generated.Property; +import org.apache.nifi.authorization.generated.Provider; +import org.apache.nifi.nar.ExtensionManager; +import org.apache.nifi.nar.NarCloseable; +import org.apache.nifi.util.NiFiProperties; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.xml.sax.SAXException; + +/** + * Factory bean for loading the configured authority provider. + */ +public class AuthorityProviderFactoryBean implements FactoryBean, ApplicationContextAware, DisposableBean, AuthorityProviderLookup { + + private static final Logger logger = LoggerFactory.getLogger(AuthorityProviderFactoryBean.class); + private static final String AUTHORITY_PROVIDERS_XSD = "/authority-providers.xsd"; + private static final String JAXB_GENERATED_PATH = "org.apache.nifi.authorization.generated"; + private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext(); + + /** + * Load the JAXBContext. + */ + private static JAXBContext initializeJaxbContext() { + try { + return JAXBContext.newInstance(JAXB_GENERATED_PATH, AuthorityProviderFactoryBean.class.getClassLoader()); + } catch (JAXBException e) { + throw new RuntimeException("Unable to create JAXBContext."); + } + } + + private ApplicationContext applicationContext; + private AuthorityProvider authorityProvider; + private NiFiProperties properties; + private final Map<String, AuthorityProvider> authorityProviders = new HashMap<>(); + + @Override + public AuthorityProvider getAuthorityProvider(String identifier) { + return authorityProviders.get(identifier); + } + + @Override + public Object getObject() throws Exception { + if (authorityProvider == null) { + // look up the authority provider to use + final String authorityProviderIdentifier = properties.getProperty(NiFiProperties.SECURITY_USER_AUTHORITY_PROVIDER); + + // ensure the authority provider class name was specified + if (StringUtils.isBlank(authorityProviderIdentifier)) { + // if configured for ssl, the authority provider must be specified + if (properties.getSslPort() != null) { + throw new Exception("When running securely, the authority provider identifier must be specified in the nifi properties file."); + } + + // use a default provider... only allowable when running not securely + authorityProvider = createDefaultProvider(); + } else { + final AuthorityProviders authorityProviderConfiguration = loadAuthorityProvidersConfiguration(); + + // create each authority provider + for (final Provider provider : authorityProviderConfiguration.getProvider()) { + authorityProviders.put(provider.getIdentifier(), createAuthorityProvider(provider.getIdentifier(), provider.getClazz())); + } + + // configure each authority provider + for (final Provider provider : authorityProviderConfiguration.getProvider()) { + final AuthorityProvider instance = authorityProviders.get(provider.getIdentifier()); + instance.onConfigured(loadAuthorityProviderConfiguration(provider)); + } + + // get the authority provider instance + authorityProvider = getAuthorityProvider(authorityProviderIdentifier); + + // ensure it was found + if (authorityProvider == null) { + throw new Exception(String.format("The specified authority provider '%s' could not be found.", authorityProviderIdentifier)); + } + } + } + + return authorityProvider; + } + + private AuthorityProviders loadAuthorityProvidersConfiguration() throws Exception { + final File authorityProvidersConfigurationFile = properties.getAuthorityProviderConfiguraitonFile(); + + // load the users from the specified file + if (authorityProvidersConfigurationFile.exists()) { + try { + // find the schema + final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + final Schema schema = schemaFactory.newSchema(AuthorityProviders.class.getResource(AUTHORITY_PROVIDERS_XSD)); + + // attempt to unmarshal + final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller(); + unmarshaller.setSchema(schema); + final JAXBElement<AuthorityProviders> element = unmarshaller.unmarshal(new StreamSource(authorityProvidersConfigurationFile), AuthorityProviders.class); + return element.getValue(); + } catch (SAXException | JAXBException e) { + throw new Exception("Unable to load the authority provider configuration file at: " + authorityProvidersConfigurationFile.getAbsolutePath()); + } + } else { + throw new Exception("Unable to find the authority provider configuration file at " + authorityProvidersConfigurationFile.getAbsolutePath()); + } + } + + private AuthorityProvider createAuthorityProvider(final String identifier, final String authorityProviderClassName) throws Exception { + // get the classloader for the specified authority provider + final ClassLoader authorityProviderClassLoader = ExtensionManager.getClassLoader(authorityProviderClassName); + if (authorityProviderClassLoader == null) { + throw new Exception(String.format("The specified authority provider class '%s' is not known to this nifi.", authorityProviderClassName)); + } + + // get the current context classloader + final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); + + final AuthorityProvider instance; + try { + // set the appropriate class loader + Thread.currentThread().setContextClassLoader(authorityProviderClassLoader); + + // attempt to load the class + Class<?> rawAuthorityProviderClass = Class.forName(authorityProviderClassName, true, authorityProviderClassLoader); + Class<? extends AuthorityProvider> authorityProviderClass = rawAuthorityProviderClass.asSubclass(AuthorityProvider.class); + + // otherwise create a new instance + Constructor constructor = authorityProviderClass.getConstructor(); + instance = (AuthorityProvider) constructor.newInstance(); + + // method injection + performMethodInjection(instance, authorityProviderClass); + + // field injection + performFieldInjection(instance, authorityProviderClass); + + // call post construction lifecycle event + instance.initialize(new StandardAuthorityProviderInitializationContext(identifier, this)); + } finally { + if (currentClassLoader != null) { + Thread.currentThread().setContextClassLoader(currentClassLoader); + } + } + + return withNarLoader(instance); + } + + private AuthorityProviderConfigurationContext loadAuthorityProviderConfiguration(final Provider provider) { + final Map<String, String> providerProperties = new HashMap<>(); + + for (final Property property : provider.getProperty()) { + providerProperties.put(property.getName(), property.getValue()); + } + + return new StandardAuthorityProviderConfigurationContext(provider.getIdentifier(), providerProperties); + } + + private void performMethodInjection(final AuthorityProvider instance, final Class authorityProviderClass) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + for (final Method method : authorityProviderClass.getMethods()) { + if (method.isAnnotationPresent(AuthorityProviderContext.class)) { + // make the method accessible + final boolean isAccessible = method.isAccessible(); + method.setAccessible(true); + + try { + final Class<?>[] argumentTypes = method.getParameterTypes(); + + // look for setters (single argument) + if (argumentTypes.length == 1) { + final Class<?> argumentType = argumentTypes[0]; + + // look for well known types + if (NiFiProperties.class.isAssignableFrom(argumentType)) { + // nifi properties injection + method.invoke(instance, properties); + } else if (ApplicationContext.class.isAssignableFrom(argumentType)) { + // spring application context injection + method.invoke(instance, applicationContext); + } + } + } finally { + method.setAccessible(isAccessible); + } + } + } + + final Class parentClass = authorityProviderClass.getSuperclass(); + if (parentClass != null && AuthorityProvider.class.isAssignableFrom(parentClass)) { + performMethodInjection(instance, parentClass); + } + } + + private void performFieldInjection(final AuthorityProvider instance, final Class authorityProviderClass) throws IllegalArgumentException, IllegalAccessException { + for (final Field field : authorityProviderClass.getDeclaredFields()) { + if (field.isAnnotationPresent(AuthorityProviderContext.class)) { + // make the method accessible + final boolean isAccessible = field.isAccessible(); + field.setAccessible(true); + + try { + // get the type + final Class<?> fieldType = field.getType(); + + // only consider this field if it isn't set yet + if (field.get(instance) == null) { + // look for well known types + if (NiFiProperties.class.isAssignableFrom(fieldType)) { + // nifi properties injection + field.set(instance, properties); + } else if (ApplicationContext.class.isAssignableFrom(fieldType)) { + // spring application context injection + field.set(instance, applicationContext); + } + } + + } finally { + field.setAccessible(isAccessible); + } + } + } + + final Class parentClass = authorityProviderClass.getSuperclass(); + if (parentClass != null && AuthorityProvider.class.isAssignableFrom(parentClass)) { + performFieldInjection(instance, parentClass); + } + } + + /** + * @return a default provider to use when running unsecurely with no + * provider configured + */ + private AuthorityProvider createDefaultProvider() { + return new AuthorityProvider() { + @Override + public boolean doesDnExist(String dn) throws AuthorityAccessException { + return false; + } + + @Override + public Set<Authority> getAuthorities(String dn) throws UnknownIdentityException, AuthorityAccessException { + return EnumSet.noneOf(Authority.class); + } + + @Override + public void setAuthorities(String dn, Set<Authority> authorities) throws UnknownIdentityException, AuthorityAccessException { + } + + @Override + public Set<String> getUsers(Authority authority) throws AuthorityAccessException { + return new HashSet<>(); + } + + @Override + public void revokeUser(String dn) throws UnknownIdentityException, AuthorityAccessException { + } + + @Override + public void addUser(String dn, String group) throws IdentityAlreadyExistsException, AuthorityAccessException { + } + + @Override + public String getGroupForUser(String dn) throws UnknownIdentityException, AuthorityAccessException { + return null; + } + + @Override + public void revokeGroup(String group) throws UnknownIdentityException, AuthorityAccessException { + } + + @Override + public void setUsersGroup(Set<String> dn, String group) throws UnknownIdentityException, AuthorityAccessException { + } + + @Override + public void ungroupUser(String dn) throws UnknownIdentityException, AuthorityAccessException { + } + + @Override + public void ungroup(String group) throws AuthorityAccessException { + } + + @Override + public DownloadAuthorization authorizeDownload(List<String> dnChain, Map<String, String> attributes) throws UnknownIdentityException, AuthorityAccessException { + return DownloadAuthorization.approved(); + } + + @Override + public void initialize(AuthorityProviderInitializationContext initializationContext) throws ProviderCreationException { + } + + @Override + public void onConfigured(AuthorityProviderConfigurationContext configurationContext) throws ProviderCreationException { + } + + @Override + public void preDestruction() throws ProviderDestructionException { + } + }; + } + + /** + * Decorates the base provider to ensure the nar context classloader is used + * when invoking the underlying methods. + * + * @param baseProvider base provider + * @return provider + */ + public AuthorityProvider withNarLoader(final AuthorityProvider baseProvider) { + return new AuthorityProvider() { + @Override + public boolean doesDnExist(String dn) throws AuthorityAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + return baseProvider.doesDnExist(dn); + } + } + + @Override + public Set<Authority> getAuthorities(String dn) throws UnknownIdentityException, AuthorityAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + return baseProvider.getAuthorities(dn); + } + } + + @Override + public void setAuthorities(String dn, Set<Authority> authorities) throws UnknownIdentityException, AuthorityAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseProvider.setAuthorities(dn, authorities); + } + } + + @Override + public Set<String> getUsers(Authority authority) throws AuthorityAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + return baseProvider.getUsers(authority); + } + } + + @Override + public void revokeUser(String dn) throws UnknownIdentityException, AuthorityAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseProvider.revokeUser(dn); + } + } + + @Override + public void addUser(String dn, String group) throws IdentityAlreadyExistsException, AuthorityAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseProvider.addUser(dn, group); + } + } + + @Override + public String getGroupForUser(String dn) throws UnknownIdentityException, AuthorityAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + return baseProvider.getGroupForUser(dn); + } + } + + @Override + public void revokeGroup(String group) throws UnknownIdentityException, AuthorityAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseProvider.revokeGroup(group); + } + } + + @Override + public void setUsersGroup(Set<String> dns, String group) throws UnknownIdentityException, AuthorityAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseProvider.setUsersGroup(dns, group); + } + } + + @Override + public void ungroupUser(String dn) throws UnknownIdentityException, AuthorityAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseProvider.ungroupUser(dn); + } + } + + @Override + public void ungroup(String group) throws AuthorityAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseProvider.ungroup(group); + } + } + + @Override + public DownloadAuthorization authorizeDownload(List<String> dnChain, Map<String, String> attributes) throws UnknownIdentityException, AuthorityAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + return baseProvider.authorizeDownload(dnChain, attributes); + } + } + + @Override + public void initialize(AuthorityProviderInitializationContext initializationContext) throws ProviderCreationException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseProvider.initialize(initializationContext); + } + } + + @Override + public void onConfigured(AuthorityProviderConfigurationContext configurationContext) throws ProviderCreationException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseProvider.onConfigured(configurationContext); + } + } + + @Override + public void preDestruction() throws ProviderDestructionException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseProvider.preDestruction(); + } + } + }; + } + + @Override + public Class getObjectType() { + return AuthorityProvider.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public void destroy() throws Exception { + if (authorityProvider != null) { + authorityProvider.preDestruction(); + } + } + + public void setProperties(NiFiProperties properties) { + this.properties = properties; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/StandardAuthorityProviderConfigurationContext.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/StandardAuthorityProviderConfigurationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/StandardAuthorityProviderConfigurationContext.java new file mode 100644 index 0000000..0535e27 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/StandardAuthorityProviderConfigurationContext.java @@ -0,0 +1,50 @@ +/* + * 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.authorization; + +import java.util.Collections; +import java.util.Map; + +/** + * + */ +public class StandardAuthorityProviderConfigurationContext implements AuthorityProviderConfigurationContext { + + private final String identifier; + private final Map<String, String> properties; + + public StandardAuthorityProviderConfigurationContext(String identifier, Map<String, String> properties) { + this.identifier = identifier; + this.properties = properties; + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public Map<String, String> getProperties() { + return Collections.unmodifiableMap(properties); + } + + @Override + public String getProperty(String property) { + return properties.get(property); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/StandardAuthorityProviderInitializationContext.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/StandardAuthorityProviderInitializationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/StandardAuthorityProviderInitializationContext.java new file mode 100644 index 0000000..e4b16c4 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/authorization/StandardAuthorityProviderInitializationContext.java @@ -0,0 +1,42 @@ +/* + * 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.authorization; + +/** + * + */ +public class StandardAuthorityProviderInitializationContext implements AuthorityProviderInitializationContext { + + private final String identifier; + private final AuthorityProviderLookup authorityProviderLookup; + + public StandardAuthorityProviderInitializationContext(String identifier, AuthorityProviderLookup authorityProviderLookup) { + this.identifier = identifier; + this.authorityProviderLookup = authorityProviderLookup; + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public AuthorityProviderLookup getAuthorityProviderLookup() { + return authorityProviderLookup; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/History.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/History.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/History.java new file mode 100644 index 0000000..8536871 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/History.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.history; + +import java.util.Collection; +import java.util.Date; +import org.apache.nifi.action.Action; + +/** + * The result of running an action query. + */ +public class History { + + private Integer total; + private Date lastRefreshed; + private Collection<Action> actions; + + public Collection<Action> getActions() { + return actions; + } + + public void setActions(Collection<Action> actions) { + this.actions = actions; + } + + public Integer getTotal() { + return total; + } + + public void setTotal(Integer totalRecordCount) { + this.total = totalRecordCount; + } + + public Date getLastRefreshed() { + return lastRefreshed; + } + + public void setLastRefreshed(Date lastRefreshed) { + this.lastRefreshed = lastRefreshed; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/HistoryQuery.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/HistoryQuery.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/HistoryQuery.java new file mode 100644 index 0000000..53cc13c --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/HistoryQuery.java @@ -0,0 +1,99 @@ +/* + * 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.history; + +import java.util.Date; + +/** + * + */ +public class HistoryQuery { + + private String userName; + private String sourceId; + private Date startDate; + private Date endDate; + private Integer offset; + private Integer count; + private String sortColumn; + private String sortOrder; + + public Date getEndDate() { + return endDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + + public String getSourceId() { + return sourceId; + } + + public void setSourceId(String sourceId) { + this.sourceId = sourceId; + } + + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public Integer getOffset() { + return offset; + } + + public void setOffset(Integer offset) { + this.offset = offset; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public String getSortColumn() { + return sortColumn; + } + + public void setSortColumn(String sortColumn) { + this.sortColumn = sortColumn; + } + + public String getSortOrder() { + return sortOrder; + } + + public void setSortOrder(String sortOrder) { + this.sortOrder = sortOrder; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/PreviousValue.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/PreviousValue.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/PreviousValue.java new file mode 100644 index 0000000..6ece5cf --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/history/PreviousValue.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.history; + +import java.util.Date; + +/** + * + */ +public class PreviousValue { + + private String previousValue; + private Date timestamp; + private String userName; + + public String getPreviousValue() { + return previousValue; + } + + public void setPreviousValue(String previousValue) { + this.previousValue = previousValue; + } + + public Date getTimestamp() { + return timestamp; + } + + public void setTimestamp(Date timestamp) { + this.timestamp = timestamp; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/AccountStatus.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/AccountStatus.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/AccountStatus.java new file mode 100644 index 0000000..d7becf1 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/AccountStatus.java @@ -0,0 +1,47 @@ +/* + * 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.user; + +/** + * Represents the status of a user's account. + */ +public enum AccountStatus { + + ACTIVE, + PENDING, + DISABLED; + + /** + * Returns the matching status or null if the specified status does not + * match any statuses. + * + * @param rawStatus string form of status + * @return account status object + */ + public static AccountStatus valueOfStatus(String rawStatus) { + AccountStatus desiredStatus = null; + + for (AccountStatus status : values()) { + if (status.toString().equals(rawStatus)) { + desiredStatus = status; + break; + } + } + + return desiredStatus; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUser.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUser.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUser.java new file mode 100644 index 0000000..a47bde9 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUser.java @@ -0,0 +1,164 @@ +/* + * 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.user; + +import java.io.Serializable; +import java.util.Date; +import java.util.EnumSet; +import java.util.Objects; +import java.util.Set; +import org.apache.nifi.authorization.Authority; +import org.apache.commons.lang3.StringUtils; + +/** + * An NiFiUser. + */ +public class NiFiUser implements Serializable { + + public static final String ANONYMOUS_USER_DN = "anonymous"; + + private String id; + private String dn; + private String userName; + private String userGroup; + private String justification; + + private Date creation; + private Date lastVerified; + private Date lastAccessed; + + private AccountStatus status; + private EnumSet<Authority> authorities; + + private NiFiUser chain; + + /* getters / setters */ + public Date getCreation() { + return creation; + } + + public void setCreation(Date creation) { + this.creation = creation; + } + + public String getDn() { + return dn; + } + + public void setDn(String dn) { + this.dn = dn; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getUserGroup() { + return userGroup; + } + + public void setUserGroup(String userGroup) { + this.userGroup = userGroup; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getJustification() { + return justification; + } + + public void setJustification(String justification) { + this.justification = justification; + } + + public AccountStatus getStatus() { + return status; + } + + public void setStatus(AccountStatus status) { + this.status = status; + } + + public Date getLastVerified() { + return lastVerified; + } + + public void setLastVerified(Date lastVerified) { + this.lastVerified = lastVerified; + } + + public Date getLastAccessed() { + return lastAccessed; + } + + public void setLastAccessed(Date lastAccessed) { + this.lastAccessed = lastAccessed; + } + + public NiFiUser getChain() { + return chain; + } + + public void setChain(NiFiUser chain) { + this.chain = chain; + } + + public Set<Authority> getAuthorities() { + if (authorities == null) { + authorities = EnumSet.noneOf(Authority.class); + } + return authorities; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final NiFiUser other = (NiFiUser) obj; + if (!Objects.equals(this.dn, other.dn)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 53 * hash + Objects.hashCode(this.dn); + return hash; + } + + @Override + public String toString() { + return String.format("dn[%s], userName[%s], justification[%s], authorities[%s]", getDn(), getUserName(), getJustification(), StringUtils.join(getAuthorities(), ", ")); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUserGroup.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUserGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUserGroup.java new file mode 100644 index 0000000..7586fd1 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/user/NiFiUserGroup.java @@ -0,0 +1,45 @@ +/* + * 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.user; + +import java.util.Set; + +/** + * + */ +public class NiFiUserGroup { + + private String group; + private Set<NiFiUser> users; + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public Set<NiFiUser> getUsers() { + return users; + } + + public void setUsers(Set<NiFiUser> users) { + this.users = users; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml new file mode 100644 index 0000000..a36619f --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/resources/nifi-administration-context.xml @@ -0,0 +1,62 @@ +<?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. +--> +<beans default-lazy-init="true" + xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xmlns:aop="http://www.springframework.org/schema/aop" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd + http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"> + + <!-- user authority provider --> + <bean id="authorityProvider" class="org.apache.nifi.authorization.AuthorityProviderFactoryBean" depends-on="clusterManager flowController"> + <property name="properties" ref="nifiProperties"/> + </bean> + + <!-- initialize the user data source --> + <bean id="userDataSource" class="org.apache.nifi.admin.UserDataSourceFactoryBean" destroy-method="shutdown"> + <property name="properties" ref="nifiProperties"/> + </bean> + + <!-- initialize the data source --> + <bean id="auditDataSource" class="org.apache.nifi.admin.AuditDataSourceFactoryBean" destroy-method="shutdown" depends-on="userDataSource"> + <property name="properties" ref="nifiProperties"/> + </bean> + + <!-- initialize the user transaction builder --> + <bean id="userTransactionBuilder" class="org.apache.nifi.admin.service.transaction.impl.StandardTransactionBuilder"> + <property name="authorityProvider" ref="authorityProvider"/> + <property name="dataSource" ref="userDataSource"/> + </bean> + + <!-- initialize the audit transaction builder --> + <bean id="auditTransactionBuilder" class="org.apache.nifi.admin.service.transaction.impl.StandardTransactionBuilder"> + <property name="authorityProvider" ref="authorityProvider"/> + <property name="dataSource" ref="auditDataSource"/> + </bean> + + <!-- administration service --> + <bean id="userService" class="org.apache.nifi.admin.service.impl.StandardUserService" init-method="seedUserAccounts"> + <property name="transactionBuilder" ref="userTransactionBuilder"/> + <property name="properties" ref="nifiProperties"/> + </bean> + + <!-- audit service --> + <bean id="auditService" class="org.apache.nifi.admin.service.impl.StandardAuditService"> + <property name="transactionBuilder" ref="auditTransactionBuilder"/> + </bean> +</beans> http://git-wip-us.apache.org/repos/asf/nifi/blob/aa998847/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/xsd/authority-providers.xsd ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/xsd/authority-providers.xsd b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/xsd/authority-providers.xsd new file mode 100644 index 0000000..122fa2c --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/xsd/authority-providers.xsd @@ -0,0 +1,49 @@ +<?xml version="1.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. +--> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <!-- role --> + <xs:complexType name="Provider"> + <xs:sequence> + <xs:element name="identifier" type="NonEmptyStringType"/> + <xs:element name="class" type="NonEmptyStringType"/> + <xs:element name="property" type="Property" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + + <!-- Name/Value properties--> + <xs:complexType name="Property"> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute name="name" type="NonEmptyStringType"></xs:attribute> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <xs:simpleType name="NonEmptyStringType"> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + </xs:restriction> + </xs:simpleType> + + <!-- users --> + <xs:element name="authorityProviders"> + <xs:complexType> + <xs:sequence> + <xs:element name="provider" type="Provider" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> +</xs:schema> \ No newline at end of file