natural commented on a change in pull request #3495: NIFI-5973 Adds 
ShellUserGroupProvider
URL: https://github.com/apache/nifi/pull/3495#discussion_r294057185
 
 

 ##########
 File path: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-shell-authorizer/src/main/java/org/apache/nifi/authorization/ShellUserGroupProvider.java
 ##########
 @@ -0,0 +1,438 @@
+/*
+ * 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 org.apache.nifi.authorization.exception.AuthorizationAccessException;
+import org.apache.nifi.authorization.exception.AuthorizerCreationException;
+import org.apache.nifi.authorization.exception.AuthorizerDestructionException;
+import org.apache.nifi.authorization.util.ShellRunner;
+
+import org.apache.nifi.components.PropertyValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+
+/*
+ * ShellUserGroupProvider implements UserGroupProvider by way of shell 
commands.
+ */
+public class ShellUserGroupProvider implements UserGroupProvider {
+
+    private final static Logger logger = 
LoggerFactory.getLogger(ShellUserGroupProvider.class);
+
+    private final static String OS_TYPE_ERROR = "Unsupported operating 
system.";
+    private final static String SYS_CHECK_ERROR = "System check failed - 
cannot provide users and groups.";
+    private final static Map<String, User> usersById = new HashMap<>();   // 
id == identifier
+    private final static Map<String, User> usersByName = new HashMap<>(); // 
name == identity
+    private final static Map<String, Group> groupsById = new HashMap<>();
+
+    public static final String INITIAL_REFRESH_DELAY_PROPERTY = "Initial 
Refresh Delay";
+    public static final String REFRESH_DELAY_PROPERTY = "Refresh Delay";
+
+    private int initialDelay;
+    private int fixedDelay;
+
+    // Our scheduler has one thread for users, one for groups:
+    private final ScheduledExecutorService scheduler = 
Executors.newScheduledThreadPool(2);
+
+    // Our shell timeout, in seconds:
+    @SuppressWarnings("FieldCanBeLocal")
+    private final Integer shellTimeout = 10;
+
+    // Commands selected during initialization:
+    private ShellCommandsProvider selectedShellCommands;
+
+    // Start of the UserGroupProvider implementation.  Javadoc strings
+    // copied from the interface definition for reference.
+
+    /**
+     * Retrieves all users. Must be non null
+     *
+     * @return a list of users
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    @Override
+    public Set<User> getUsers() throws AuthorizationAccessException {
+        synchronized (usersById) {
+            logger.debug("getUsers has user set of size: " + usersById.size());
+            return new HashSet<>(usersById.values());
+        }
+    }
+
+    /**
+     * Retrieves the user with the given identifier.
+     *
+     * @param identifier the id of the user to retrieve
+     * @return the user with the given id, or null if no matching user was 
found
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    @Override
+    public User getUser(String identifier) throws AuthorizationAccessException 
{
+        synchronized (usersById) {
+            User user = usersById.get(identifier);
+            if (user == null) {
+                logger.debug("getUser user not found: " + identifier);
+            } else {
+                logger.debug("getUser has user: " + user);
+            }
+            return user;
+        }
+    }
+
+    /**
+     * Retrieves the user with the given identity.
+     *
+     * @param identity the identity of the user to retrieve
+     * @return the user with the given identity, or null if no matching user 
was found
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    @Override
+    public User getUserByIdentity(String identity) throws 
AuthorizationAccessException {
+        synchronized (usersByName) {
+            User user = usersByName.get(identity);
+            logger.debug("getUserByIdentity has user: " + user);
+            return user;
+        }
+    }
+
+    /**
+     * Retrieves all groups. Must be non null
+     *
+     * @return a list of groups
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    @Override
+    public Set<Group> getGroups() throws AuthorizationAccessException {
+        synchronized (groupsById) {
+            logger.debug("getGroups has group set of size: " + 
groupsById.size());
+            return new HashSet<>(groupsById.values());
+        }
+    }
+
+    /**
+     * Retrieves a Group by id.
+     *
+     * @param identifier the identifier of the Group to retrieve
+     * @return the Group with the given identifier, or null if no matching 
group was found
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    @Override
+    public Group getGroup(String identifier) throws 
AuthorizationAccessException {
+        synchronized (groupsById) {
+            Group group = groupsById.get(identifier);
+            logger.debug("getGroup has group: " + group);
+            return group;
+        }
+    }
+
+    /**
+     * Gets a user and their groups. Must be non null. If the user is not 
known the UserAndGroups.getUser() and
+     * UserAndGroups.getGroups() should return null
+     *
+     * @return the UserAndGroups for the specified identity
+     * @throws AuthorizationAccessException if there was an unexpected error 
performing the operation
+     */
+    @Override
+    public UserAndGroups getUserAndGroups(String identity) throws 
AuthorizationAccessException {
+        User user = getUser(identity);
+        Set<Group> groups = new HashSet<>();
+
+        for (Group g: getGroups()) {
+            if (user != null && g.getUsers().contains(user.getIdentity())) {
+                groups.add(g);
+            }
+        }
+
+        return new UserAndGroups() {
+            @Override
+            public User getUser() {
+                return user;
+            }
+
+            @Override
+            public Set<Group> getGroups() {
+                return groups;
+            }
+        };
+    }
+
+    /**
+     * Called immediately after instance creation for implementers to perform 
additional setup
+     *
+     * @param initializationContext in which to initialize
+     */
+    @Override
+    public void initialize(UserGroupProviderInitializationContext 
initializationContext) throws AuthorizerCreationException {
+    }
+
+    /**
+     * Called to configure the Authorizer.
+     *
+     * @param configurationContext at the time of configuration
+     * @throws AuthorizerCreationException for any issues configuring the 
provider
+     */
+    @Override
+    public void onConfigured(AuthorizerConfigurationContext 
configurationContext) throws AuthorizerCreationException {
+        // Our first init step is to select the command set based on the
+        // operating system name:
+        ShellCommandsProvider commands = getCommands();
+
+        if (commands == null) {
+            commands = getShellCommandsProvider(null);
+            setCommandsProvider(commands);
+        }
+
+        // Our second init step is to run the system check from that
+        // command set to determine if the other commands will work on
+        // this host or not.
+        try {
+            ShellRunner.runShell(commands.getSystemCheck());
+        } catch (final IOException ioexc) {
+            logger.error("initialize exception: " + ioexc + " system check 
command: " + commands.getSystemCheck());
+            throw new AuthorizerCreationException(SYS_CHECK_ERROR, 
ioexc.getCause());
+        }
+
+        // With our command set selected, and our system check passed,
+        // we can pull in the users and groups:
+        refreshUsersAndGroups();
+
+        // Our last init step is to fire off the refresh threads:
+        initialDelay = getIntegerPropertyWithDefault(configurationContext, 
INITIAL_REFRESH_DELAY_PROPERTY, 30);
+        fixedDelay = getIntegerPropertyWithDefault(configurationContext, 
REFRESH_DELAY_PROPERTY, 30);
+
+        scheduler.scheduleWithFixedDelay(this::refreshUsersAndGroups, 
initialDelay, fixedDelay, TimeUnit.SECONDS);
+    }
+
+    private static ShellCommandsProvider getShellCommandsProvider(String 
osName) {
+        if (osName == null) {
+            osName = System.getProperty("os.name");
+        }
+
+        ShellCommandsProvider commands;
+        if (osName.startsWith("Linux")) {
+            logger.debug("Selected Linux command set.");
+            commands = new NssShellCommands();
+        } else if (osName.startsWith("Mac OS X")) {
+            logger.debug("Selected OSX command set.");
+            commands = new OsxShellCommands();
+        } else {
+            throw new AuthorizerCreationException(OS_TYPE_ERROR);
+        }
+        return commands;
+    }
+
+    private int getIntegerPropertyWithDefault(AuthorizerConfigurationContext 
context, String name, int defaultValue) {
+        final PropertyValue property = context.getProperty(name);
+        if (property != null) {
+            return property.asInteger();
+        } else {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Called immediately before instance destruction for implementers to 
release resources.
+     *
+     * @throws AuthorizerDestructionException If pre-destruction fails.
+     */
+    @Override
+    public void preDestruction() throws AuthorizerDestructionException {
+        try {
+            scheduler.shutdownNow();
+        } catch (final Exception ignored) {
+        }
+    }
+
+    public ShellCommandsProvider getCommands() {
 
 Review comment:
   That's a good catch.  I've renamed the method in question to 
`getCommandsProvider`.  I've also renamed the OS commands selector method to 
`getCommandsProviderFromName`.

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to