NIFIREG-9: Initial Auth Implementation - #14
Conversation
Authentication and authorization enforcement for web API, largely based on NiFi. This commit adds interfaces, framework, and file-based authorizer providers (file access policy provider, file user group provider). Authentication of identities is currently based on certificates in two-way SSL (HTTPS). Alternative identity strategies (user&pass, JWT) will be added later building upon the foundation in this commit. As part of this feature, some changes were made to the RegistryService interface and the providers it utilizes.
|
Reviewing... |
bbende
left a comment
There was a problem hiding this comment.
Overall looks great! Was able to secure the app and verify the REST endpoints blocked access when my user wasn't added to a policy. Just a few minor comments/suggestions.
| // call post construction lifecycle event | ||
| instance.initialize(new StandardAuthorizerInitializationContext(identifier, this, this, this)); | ||
|
|
||
| // if (StringUtils.isNotEmpty(classpathResources)) { |
There was a problem hiding this comment.
Should be fine to leave this out for now, but we may need to revisit this if/when we add support for Ranger... this was done so someone can configure additional libraries to be on the classpath of the authorizer, specifically for Ranger to know where the Hadoop client JARs were if wanting to send audit logs to HDFS.
There was a problem hiding this comment.
Yeah hadn't gotten that far :)
Its a good point. I'll capture the outstanding work in a JIRA ticket and some in-line TODOs.
| verifyAccessPolicyProviderIsConfigurable(); | ||
|
|
||
| // Don't allow changing action or resource of existing policy (should only be adding/removing users/groups) | ||
| org.apache.nifi.registry.authorization.AccessPolicy currentAccessPolicy = |
There was a problem hiding this comment.
Should this be within the write lock?
I'm thinking if two people tried to update at the same time, they could both retrieve the initial policy, then assuming user1 obtains the write lock and makes changes, now user2 gets the write lock but is making changes to the initial version and doesn't have user1's changes.
There was a problem hiding this comment.
The idea here is that no user can change the accessPolicy identifier, resource, or action fields on update. They can only change groups or users that have an access policy. To change resource or action, they would have to delete and create a new access policy.
So in your example, user1 and user2 both get the initial version, but those should be guaranteed to match for the fields we want to read. Unless I'm making a mistake in my logic...
That said, I think I'll move it to inside the write lock anyway, as it won't hurt and would protect this code block from future changes that break the expectation I had.
|
|
||
| readLock.lock(); | ||
| try { | ||
| final Set<BucketEntity> filterBuckets = bucketIdentifiers.stream() |
There was a problem hiding this comment.
Should we create a method in metadataService to retrieve all the buckets for the set of ids in one call?
I'm just slightly concerned making an individual DB retrieval for each id if there are a lot of ids passed in.
There was a problem hiding this comment.
Yeah I agree that would be a better approach. Will make that change, thanks.
| <version>4.12</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> |
There was a problem hiding this comment.
I think we can get rid of this, doing a quick test the build still passed without it
| </goals> | ||
| <configuration> | ||
| <packageName>org.apache.nifi.registry.user.generated</packageName> | ||
| <sources> |
There was a problem hiding this comment.
I noticed this file showed errors in my IDE, even though technically it built fine... seems like bumping the jaxb2-maven-plugin to 2.3.1 in the root pom pluginManagement cleared this up
There was a problem hiding this comment.
Good find, will update that plugin version.
| /** | ||
| * Defines a policy for a set of userIdentifiers to perform a set of actions on a given resource. | ||
| */ | ||
| public class AccessPolicy { |
There was a problem hiding this comment.
I know this would be a tedious change, but what do you think about splitting out all of the interfaces/domain objects from nifi-registry-security into nifi-registry-security-api?
My thinking is that nifi-registry-security-api would live in the lib directory and be something that other people would implement against, where as nifi-registry-security would be bundled under the WAR for nifi-registry-web-api.
There was a problem hiding this comment.
It's a good suggestion. I wasn't thrilled with the current organization. Your approach both distinguishes interfaces from implementation and adds extensibility. Will make that change, thanks!
| <artifactId>spring-boot-starter-web</artifactId> | ||
| <version>${spring.boot.version}</version> | ||
| </dependency> | ||
| <dependency> |
There was a problem hiding this comment.
If we make the change in one of the previous comments where we use spring-boot-starter-security in the nifi-registry-security pom, then we probably don't need to directly reference it here because it will be brought in transitively
| @@ -0,0 +1,47 @@ | |||
| # Licensed to the Apache Software Foundation (ASF) under one or more | |||
There was a problem hiding this comment.
I think this was maybe left-over from testing and not meant to be checked in?
There was a problem hiding this comment.
yeah this was a test file. will remove
| <nifi.registry.security.truststorePasswd /> | ||
| <nifi.registry.security.needClientAuth /> | ||
| <nifi.registry.security.authorized.users>./conf/authorized-users.xml</nifi.registry.security.authorized.users> | ||
| <nifi.security.user.authorizer>managed-authorizer</nifi.security.user.authorizer> |
There was a problem hiding this comment.
Is the nifi.registry.security.authorized.users still needed?
There was a problem hiding this comment.
nope. neither is login.identity.provider. will remove unused properties and fix the prefixes to be consistent. good catch!
| nifi.registry.security.truststorePasswd=${nifi.registry.security.truststorePasswd} | ||
| nifi.registry.security.needClientAuth=${nifi.registry.security.needClientAuth} | ||
| nifi.registry.security.authorized.users=${nifi.registry.security.authorized.users} | ||
| nifi.security.user.authorizer=${nifi.security.user.authorizer} |
There was a problem hiding this comment.
Is nifi.registry.security.authorized.users still needed?
There was a problem hiding this comment.
same as above. removed in the next update
|
Thanks a lot for the review @bbende! Great suggestions. Will make those changes and update. |
|
@kevdoran With this PR when I try to GET http://localhost:8080/nifi-registry-api/items I am getting a "400 Bad Request: Bucket Identifiers cannot be null or empty"... |
|
Good catch @scottyaslan - will update with a fix. |
|
@bbende I restructured the modules based on your suggestion. I now have the following modules: Let me know if you agree with this or if it is overkill for what we need. Technically |
…r review feedback
|
Thanks for the update! Everything is looking good. I'm thinking maybe we can fold |
|
+1 Latest update looks good, going to merge shortly, thanks! |
This is a rather large PR, including:
For reviewers, a good place to start is AuthorizationService.java. That is called by Resources (web api endpoints) for authorizing requests. It makes use of authorizer providers (access policy provider and user group provider), which are interfaces and implementations heavily based in NiFi.
To run this, see the instructions in included authorizers.xml file. Configure that with your initial admin identity (must be certificate based) and configure your properties file to use your configured authorizers.
This functionality has been verified through manual testing. Documentation on proper configuration (aside what is in the provided in comment ins the included authorizers.xml example file) is in progress. Automated tests cases (unit, integration, etc.) are in progress and will likely be added to the PR during the time it is reviewed, but opening it now to get initial feedback, especially where it defines/modifies key interfaces.
As part of this feature, some changes were made to the RegistryService interface and the providers it utilizes. @bbende please review these changes when you get a change to make sure they are consistent with your design and intentions for the service and provider layers. Thanks!