mirror of https://github.com/apache/nifi.git
NIFI-3653: - Introducing UserGroup and Policy provider interfaces.
- Introducing FileUserGroupProvider and FileAccessPolicyProvider. - Refactoring FileAuthorizer to utilize the file based implementations. - Introducing the StandardManagedAuthorizer. - Decorating the configured ManagedAuthorizer to ensure integrity checks are still performed. - Loading user groups if possible to use during access decisions. - Merging responses for requests for AccessPolicies, Users, and UserGroups. - Adding unit tests as appropriate. - Adding methods to the User, Group, and AccessPolicy builder that more easily supports generating UUIDs. - Fixing typo when seeding policies during startup. - Fixing type in documentation and error messages. This closes #1897. Signed-off-by: Bryan Bende <bbende@apache.org>
This commit is contained in:
parent
f447fc73fa
commit
4ed7511bee
|
@ -385,7 +385,32 @@ Authorizers are configured using two properties in the 'nifi.properties' file:
|
|||
Authorizers.xml Setup
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The 'authorizers.xml' file is used to define and configure available authorizers. The default authorizer is the FileAuthorizer, however, you can develop additional authorizers as extensions. The FileAuthorizer has the following properties:
|
||||
The 'authorizers.xml' file is used to define and configure available authorizers. The default authorizer is the StandardManagedAuthorizer. The managed authorizer is comprised of a UserGroupProvider
|
||||
and a AccessPolicyProvider. The users, group, and access policies will be loaded and optionally configured through these providers. The managed authorizer will make all access decisions based on
|
||||
these provided users, groups, and access policies.
|
||||
|
||||
The default UserGroupProvider is the FileUserGroupProvider, however, you can develop additional UserGroupProviders as extensions. The FileUserGroupProvider has the following properties:
|
||||
|
||||
* Users File - The file where the FileUserGroupProvider stores users and groups. By default, the 'users.xml' in the 'conf' directory is chosen.
|
||||
* Legacy Authorized Users File - The full path to an existing authorized-users.xml that will be automatically be used to load the users and groups into the Users File.
|
||||
* Initial User Identity - The identity of a users and systems to seed the Users File. The name of each property must be unique, for example: "Initial User Identity A", "Initial User Identity B", "Initial User Identity C" or "Initial User Identity 1", "Initial User Identity 2", "Initial User Identity 3"
|
||||
|
||||
The default AccessPolicyProvider is the FileAccessPolicyProvider, however, you can develop additional AccessPolicyProvider as extensions. The FileAccessPolicyProvider has the following properties:
|
||||
|
||||
* User Group Provider - The identifier for an User Group Provider defined above that will be used to access users and groups for use in the managed access policies.
|
||||
* Authorizations File - The file where the FileAccessPolicyProvider will store policies.
|
||||
* Initial Admin Identity - The identity of an initial admin user that will be granted access to the UI and given the ability to create additional users, groups, and policies. The value of this property could be a DN when using certificates or LDAP, or a Kerberos principal. This property will only be used when there are no other policies defined. If this property is specified then a Legacy Authorized Users File can not be specified.
|
||||
* Legacy Authorized Users File - The full path to an existing authorized-users.xml that will be automatically converted to the new authorizations model. If this property is specified then an Initial Admin Identity can not be specified, and this property will only be used when there are no other users, groups, and policies defined.
|
||||
* Node Identity - The identity of a NiFi cluster node. When clustered, a property for each node should be defined, so that every node knows about every other node. If not clustered these properties can be ignored. The name of each property must be unique, for example for a three node cluster: "Node Identity A", "Node Identity B", "Node Identity C" or "Node Identity 1", "Node Identity 2", "Node Identity 3"
|
||||
|
||||
The identities configured in the Initial Admin Identity, the Node Identity properties, or discovered in a Legacy Authorized Users File must be available in the configured User Group Provider.
|
||||
|
||||
The default authorizer is the StandardManagedAuthorizer, however, you can develop additional authorizers as extensions. The StandardManagedAuthorizer has the following properties:
|
||||
|
||||
* Access Policy Provider - The identifier for an Access Policy Provider defined above.
|
||||
|
||||
The FileAuthorizer has been replaced with the more granular StandardManagedAuthorizer approach described above. However, it is still available for backwards compatibility reasons. The
|
||||
FileAuthorizer has the following properties.
|
||||
|
||||
* Authorizations File - The file where the FileAuthorizer stores policies. By default, the 'authorizations.xml' in the 'conf' directory is chosen.
|
||||
* Users File - The file where the FileAuthorizer stores users and groups. By default, the 'users.xml' in the 'conf' directory is chosen.
|
||||
|
@ -402,17 +427,29 @@ If you are setting up a secured NiFi instance for the first time, you must manua
|
|||
Here is an example LDAP entry using the name John Smith:
|
||||
|
||||
----
|
||||
<authorizer>
|
||||
<identifier>file-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileAuthorizer</class>
|
||||
<property name="Authorizations File">./conf/authorizations.xml</property>
|
||||
<authorizers>
|
||||
<userGroupProvider>
|
||||
<identifier>file-user-group-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileUserGroupProvider</class>
|
||||
<property name="Users File">./conf/users.xml</property>
|
||||
<property name="Legacy Authorized Users File"></property>
|
||||
|
||||
<property name="Initial User Identity 1">cn=John Smith,ou=people,dc=example,dc=com</property>
|
||||
</userGroupProvider>
|
||||
<accessPolicyProvider>
|
||||
<identifier>file-access-policy-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileAccessPolicyProvider</class>
|
||||
<property name="User Group Provider">file-user-group-provider</property>
|
||||
<property name="Authorizations File">./conf/authorizations.xml</property>
|
||||
<property name="Initial Admin Identity">cn=John Smith,ou=people,dc=example,dc=com</property>
|
||||
<property name="Legacy Authorized Users File"></property>
|
||||
<!--
|
||||
|
||||
<property name="Node Identity 1"></property>
|
||||
<property name="Node Identity 2"></property>
|
||||
-->
|
||||
</accessPolicyProvider>
|
||||
<authorizer>
|
||||
<identifier>managed-authorizer</identifier>
|
||||
<class>org.apache.nifi.authorization.StandardManagedAuthorizer</class>
|
||||
<property name="Access Policy Provider">file-access-policy-provider</property>
|
||||
</authorizer>
|
||||
</authorizers>
|
||||
----
|
||||
|
@ -420,17 +457,29 @@ Here is an example LDAP entry using the name John Smith:
|
|||
Here is an example Kerberos entry using the name John Smith and realm `NIFI.APACHE.ORG`:
|
||||
|
||||
----
|
||||
<authorizer>
|
||||
<identifier>file-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileAuthorizer</class>
|
||||
<property name="Authorizations File">./conf/authorizations.xml</property>
|
||||
<authorizers>
|
||||
<userGroupProvider>
|
||||
<identifier>file-user-group-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileUserGroupProvider</class>
|
||||
<property name="Users File">./conf/users.xml</property>
|
||||
<property name="Legacy Authorized Users File"></property>
|
||||
|
||||
<property name="Initial User Identity 1">johnsmith@NIFI.APACHE.ORG</property>
|
||||
</userGroupProvider>
|
||||
<accessPolicyProvider>
|
||||
<identifier>file-access-policy-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileAccessPolicyProvider</class>
|
||||
<property name="User Group Provider">file-user-group-provider</property>
|
||||
<property name="Authorizations File">./conf/authorizations.xml</property>
|
||||
<property name="Initial Admin Identity">johnsmith@NIFI.APACHE.ORG</property>
|
||||
<property name="Legacy Authorized Users File"></property>
|
||||
<!--
|
||||
|
||||
<property name="Node Identity 1"></property>
|
||||
<property name="Node Identity 2"></property>
|
||||
-->
|
||||
</accessPolicyProvider>
|
||||
<authorizer>
|
||||
<identifier>managed-authorizer</identifier>
|
||||
<class>org.apache.nifi.authorization.StandardManagedAuthorizer</class>
|
||||
<property name="Access Policy Provider">file-access-policy-provider</property>
|
||||
</authorizer>
|
||||
</authorizers>
|
||||
----
|
||||
|
@ -449,13 +498,28 @@ Here is an example entry:
|
|||
|
||||
----
|
||||
<authorizers>
|
||||
<authorizer>
|
||||
<identifier>file-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileAuthorizer</class>
|
||||
<property name="Authorizations File">./conf/authorizations.xml</property>
|
||||
<userGroupProvider>
|
||||
<identifier>file-user-group-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileUserGroupProvider</class>
|
||||
<property name="Users File">./conf/users.xml</property>
|
||||
<property name="Legacy Authorized Users File">/Users/johnsmith/config_files/authorized-users.xml</property>
|
||||
|
||||
<property name="Initial User Identity 1"></property>
|
||||
</userGroupProvider>
|
||||
<accessPolicyProvider>
|
||||
<identifier>file-access-policy-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileAccessPolicyProvider</class>
|
||||
<property name="User Group Provider">file-user-group-provider</property>
|
||||
<property name="Authorizations File">./conf/authorizations.xml</property>
|
||||
<property name="Initial Admin Identity"></property>
|
||||
<property name="Legacy Authorized Users File">/Users/johnsmith/config_files/authorized-users.xml</property>
|
||||
|
||||
<property name="Node Identity 1"></property>
|
||||
</accessPolicyProvider>
|
||||
<authorizer>
|
||||
<identifier>managed-authorizer</identifier>
|
||||
<class>org.apache.nifi.authorization.StandardManagedAuthorizer</class>
|
||||
<property name="Access Policy Provider">file-access-policy-provider</property>
|
||||
</authorizer>
|
||||
</authorizers>
|
||||
----
|
||||
|
@ -514,15 +578,32 @@ cn=nifi-2,ou=people,dc=example,dc=com
|
|||
----
|
||||
|
||||
----
|
||||
<authorizer>
|
||||
<identifier>file-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileAuthorizer</class>
|
||||
<property name="Authorizations File">./conf/authorizations.xml</property>
|
||||
<authorizers>
|
||||
<userGroupProvider>
|
||||
<identifier>file-user-group-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileUserGroupProvider</class>
|
||||
<property name="Users File">./conf/users.xml</property>
|
||||
<property name="Legacy Authorized Users File"></property>
|
||||
|
||||
<property name="Initial User Identity 1">johnsmith@NIFI.APACHE.ORG</property>
|
||||
<property name="Initial User Identity 2">cn=nifi-1,ou=people,dc=example,dc=com</property>
|
||||
<property name="Initial User Identity 3">cn=nifi-2,ou=people,dc=example,dc=com</property>
|
||||
</userGroupProvider>
|
||||
<accessPolicyProvider>
|
||||
<identifier>file-access-policy-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileAccessPolicyProvider</class>
|
||||
<property name="User Group Provider">file-user-group-provider</property>
|
||||
<property name="Authorizations File">./conf/authorizations.xml</property>
|
||||
<property name="Initial Admin Identity">johnsmith@NIFI.APACHE.ORG</property>
|
||||
<property name="Legacy Authorized Users File"></property>
|
||||
|
||||
<property name="Node Identity 1">cn=nifi-1,ou=people,dc=example,dc=com</property>
|
||||
<property name="Node Identity 2">cn=nifi-2,ou=people,dc=example,dc=com</property>
|
||||
</accessPolicyProvider>
|
||||
<authorizer>
|
||||
<identifier>managed-authorizer</identifier>
|
||||
<class>org.apache.nifi.authorization.StandardManagedAuthorizer</class>
|
||||
<property name="Access Policy Provider">file-access-policy-provider</property>
|
||||
</authorizer>
|
||||
</authorizers>
|
||||
----
|
||||
|
@ -535,7 +616,11 @@ Now that initial authorizations have been created, additional users, groups and
|
|||
Configuring Users & Access Policies
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This section describes:
|
||||
Depending on the capabilities of the configured UserGroupProvider and AccessPolicyProvider the users, groups, and policies will be configurable in the UI. If the extensions are not configurable the
|
||||
users, groups, and policies will read-only in the UI. If the configured authorizer does not use UserGroupProvider and AccessPolicyProvider the users and policies may or may not be visible and
|
||||
configurable in the UI based on the underlying implementation.
|
||||
|
||||
This section assumes the users, groups, and policies are configurable in the UI and describes:
|
||||
|
||||
* How to create users and groups
|
||||
* How access policies are used to define authorizations
|
||||
|
|
|
@ -18,6 +18,8 @@ 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.exception.UninheritableAuthorizationsException;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
@ -43,7 +45,7 @@ import java.util.Set;
|
|||
/**
|
||||
* An Authorizer that provides management of users, groups, and policies.
|
||||
*/
|
||||
public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
||||
public abstract class AbstractPolicyBasedAuthorizer implements ManagedAuthorizer {
|
||||
|
||||
static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
|
||||
static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();
|
||||
|
@ -60,32 +62,9 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
static final String RESOURCE_ATTR = "resource";
|
||||
static final String ACTIONS_ATTR = "actions";
|
||||
|
||||
public static final String EMPTY_FINGERPRINT = "EMPTY";
|
||||
|
||||
@Override
|
||||
public final void onConfigured(final AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
doOnConfigured(configurationContext);
|
||||
|
||||
// ensure that only one policy per resource-action exists
|
||||
for (AccessPolicy accessPolicy : getAccessPolicies()) {
|
||||
if (policyExists(accessPolicy)) {
|
||||
throw new AuthorizerCreationException(String.format("Found multiple policies for '%s' with '%s'.", accessPolicy.getResource(), accessPolicy.getAction()));
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that only one group exists per identity
|
||||
for (User user : getUsers()) {
|
||||
if (tenantExists(user.getIdentifier(), user.getIdentity())) {
|
||||
throw new AuthorizerCreationException(String.format("Found multiple users/user groups with identity '%s'.", user.getIdentity()));
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that only one group exists per identity
|
||||
for (Group group : getGroups()) {
|
||||
if (tenantExists(group.getIdentifier(), group.getName())) {
|
||||
throw new AuthorizerCreationException(String.format("Found multiple users/user groups with name '%s'.", group.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,48 +75,6 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
*/
|
||||
protected abstract void doOnConfigured(final AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException;
|
||||
|
||||
/**
|
||||
* Checks if another policy exists with the same resource and action as the given policy.
|
||||
*
|
||||
* @param checkAccessPolicy an access policy being checked
|
||||
* @return true if another access policy exists with the same resource and action, false otherwise
|
||||
*/
|
||||
private boolean policyExists(final AccessPolicy checkAccessPolicy) {
|
||||
for (AccessPolicy accessPolicy : getAccessPolicies()) {
|
||||
if (!accessPolicy.getIdentifier().equals(checkAccessPolicy.getIdentifier())
|
||||
&& accessPolicy.getResource().equals(checkAccessPolicy.getResource())
|
||||
&& accessPolicy.getAction().equals(checkAccessPolicy.getAction())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if another user exists with the same identity.
|
||||
*
|
||||
* @param identifier identity of the user
|
||||
* @param identity identity of the user
|
||||
* @return true if another user exists with the same identity, false otherwise
|
||||
*/
|
||||
private boolean tenantExists(final String identifier, final String identity) {
|
||||
for (User user : getUsers()) {
|
||||
if (!user.getIdentifier().equals(identifier)
|
||||
&& user.getIdentity().equals(identity)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (Group group : getGroups()) {
|
||||
if (!group.getIdentifier().equals(identifier)
|
||||
&& group.getName().equals(identity)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException {
|
||||
final UsersAndAccessPolicies usersAndAccessPolicies = getUsersAndAccessPolicies();
|
||||
|
@ -191,9 +128,6 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
* @throws IllegalStateException if a group with the same name already exists
|
||||
*/
|
||||
public final synchronized Group addGroup(Group group) throws AuthorizationAccessException {
|
||||
if (tenantExists(group.getIdentifier(), group.getName())) {
|
||||
throw new IllegalStateException(String.format("User/user group already exists with the identity '%s'.", group.getName()));
|
||||
}
|
||||
return doAddGroup(group);
|
||||
}
|
||||
|
||||
|
@ -224,9 +158,6 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
* @throws IllegalStateException if there is already a group with the same name
|
||||
*/
|
||||
public final synchronized Group updateGroup(Group group) throws AuthorizationAccessException {
|
||||
if (tenantExists(group.getIdentifier(), group.getName())) {
|
||||
throw new IllegalStateException(String.format("User/user group already exists with the identity '%s'.", group.getName()));
|
||||
}
|
||||
return doUpdateGroup(group);
|
||||
}
|
||||
|
||||
|
@ -266,9 +197,6 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
* @throws IllegalStateException if there is already a user with the same identity
|
||||
*/
|
||||
public final synchronized User addUser(User user) throws AuthorizationAccessException {
|
||||
if (tenantExists(user.getIdentifier(), user.getIdentity())) {
|
||||
throw new IllegalStateException(String.format("User/user group already exists with the identity '%s'.", user.getIdentity()));
|
||||
}
|
||||
return doAddUser(user);
|
||||
}
|
||||
|
||||
|
@ -308,9 +236,6 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
* @throws IllegalStateException if there is already a user with the same identity
|
||||
*/
|
||||
public final synchronized User updateUser(final User user) throws AuthorizationAccessException {
|
||||
if (tenantExists(user.getIdentifier(), user.getIdentity())) {
|
||||
throw new IllegalStateException(String.format("User/user group already exists with the identity '%s'.", user.getIdentity()));
|
||||
}
|
||||
return doUpdateUser(user);
|
||||
}
|
||||
|
||||
|
@ -348,9 +273,6 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
public final synchronized AccessPolicy addAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
if (policyExists(accessPolicy)) {
|
||||
throw new IllegalStateException(String.format("Found multiple policies for '%s' with '%s'.", accessPolicy.getResource(), accessPolicy.getAction()));
|
||||
}
|
||||
return doAddAccessPolicy(accessPolicy);
|
||||
}
|
||||
|
||||
|
@ -406,18 +328,55 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
*/
|
||||
public abstract UsersAndAccessPolicies getUsersAndAccessPolicies() throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Returns whether the proposed fingerprint is inheritable.
|
||||
*
|
||||
* @param proposedFingerprint the proposed fingerprint
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
* @throws UninheritableAuthorizationsException if the proposed fingerprint was uninheritable
|
||||
*/
|
||||
@Override
|
||||
public final void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
|
||||
try {
|
||||
// ensure we understand the proposed fingerprint
|
||||
parsePoliciesUsersAndGroups(proposedFingerprint);
|
||||
} catch (final AuthorizationAccessException e) {
|
||||
throw new UninheritableAuthorizationsException("Unable to parse proposed fingerprint: " + e);
|
||||
}
|
||||
|
||||
final List<User> users = getSortedUsers();
|
||||
final List<Group> groups = getSortedGroups();
|
||||
final List<AccessPolicy> accessPolicies = getSortedAccessPolicies();
|
||||
|
||||
// ensure we're in a state to inherit
|
||||
if (!users.isEmpty() || !groups.isEmpty() || !accessPolicies.isEmpty()) {
|
||||
throw new UninheritableAuthorizationsException("Proposed fingerprint is not inheritable because the current Authorizations is not empty..");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the fingerprint and adds any users, groups, and policies to the current Authorizer.
|
||||
*
|
||||
* @param fingerprint the fingerprint that was obtained from calling getFingerprint() on another Authorizer.
|
||||
*/
|
||||
@Override
|
||||
public final void inheritFingerprint(final String fingerprint) throws AuthorizationAccessException {
|
||||
if (fingerprint == null || fingerprint.trim().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
|
||||
final PoliciesUsersAndGroups policiesUsersAndGroups = parsePoliciesUsersAndGroups(fingerprint);
|
||||
policiesUsersAndGroups.getUsers().forEach(user -> addUser(user));
|
||||
policiesUsersAndGroups.getGroups().forEach(group -> addGroup(group));
|
||||
policiesUsersAndGroups.getAccessPolicies().forEach(policy -> addAccessPolicy(policy));
|
||||
}
|
||||
|
||||
private PoliciesUsersAndGroups parsePoliciesUsersAndGroups(final String fingerprint) {
|
||||
final List<AccessPolicy> accessPolicies = new ArrayList<>();
|
||||
final List<User> users = new ArrayList<>();
|
||||
final List<Group> groups = new ArrayList<>();
|
||||
|
||||
final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
|
||||
try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) {
|
||||
final DocumentBuilder docBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
|
||||
final Document document = docBuilder.parse(in);
|
||||
|
@ -427,29 +386,27 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
NodeList userNodes = rootElement.getElementsByTagName(USER_ELEMENT);
|
||||
for (int i=0; i < userNodes.getLength(); i++) {
|
||||
Node userNode = userNodes.item(i);
|
||||
User user = parseUser((Element) userNode);
|
||||
addUser(user);
|
||||
users.add(parseUser((Element) userNode));
|
||||
}
|
||||
|
||||
// parse all the groups and add them to the current authorizer
|
||||
NodeList groupNodes = rootElement.getElementsByTagName(GROUP_ELEMENT);
|
||||
for (int i=0; i < groupNodes.getLength(); i++) {
|
||||
Node groupNode = groupNodes.item(i);
|
||||
Group group = parseGroup((Element) groupNode);
|
||||
addGroup(group);
|
||||
groups.add(parseGroup((Element) groupNode));
|
||||
}
|
||||
|
||||
// parse all the policies and add them to the current authorizer
|
||||
NodeList policyNodes = rootElement.getElementsByTagName(POLICY_ELEMENT);
|
||||
for (int i=0; i < policyNodes.getLength(); i++) {
|
||||
Node policyNode = policyNodes.item(i);
|
||||
AccessPolicy policy = parsePolicy((Element) policyNode);
|
||||
addAccessPolicy(policy);
|
||||
accessPolicies.add(parsePolicy((Element) policyNode));
|
||||
}
|
||||
|
||||
} catch (SAXException | ParserConfigurationException | IOException e) {
|
||||
throw new AuthorizationAccessException("Unable to parse fingerprint", e);
|
||||
}
|
||||
|
||||
return new PoliciesUsersAndGroups(accessPolicies, users, groups);
|
||||
}
|
||||
|
||||
private User parseUser(final Element element) {
|
||||
|
@ -503,6 +460,181 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AccessPolicyProvider getAccessPolicyProvider() {
|
||||
return new ConfigurableAccessPolicyProvider() {
|
||||
@Override
|
||||
public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.getAccessPolicies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.getAccessPolicy(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy addAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.addAccessPolicy(accessPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.updateAccessPolicy(accessPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy deleteAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.deleteAccessPolicy(accessPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException {
|
||||
final UsersAndAccessPolicies usersAndAccessPolicies = AbstractPolicyBasedAuthorizer.this.getUsersAndAccessPolicies();
|
||||
return usersAndAccessPolicies.getAccessPolicy(resourceIdentifier, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFingerprint() throws AuthorizationAccessException {
|
||||
// fingerprint is managed by the encapsulating class
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
|
||||
// fingerprint is managed by the encapsulating class
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
|
||||
// fingerprint is managed by the encapsulating class
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserGroupProvider getUserGroupProvider() {
|
||||
return new ConfigurableUserGroupProvider() {
|
||||
@Override
|
||||
public User addUser(User user) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.addUser(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User updateUser(User user) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.updateUser(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User deleteUser(User user) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.deleteUser(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group addGroup(Group group) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.addGroup(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group updateGroup(Group group) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.updateGroup(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group deleteGroup(Group group) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.deleteGroup(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<User> getUsers() throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.getUsers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(String identifier) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.getUser(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByIdentity(String identity) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.getUserByIdentity(identity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.getGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getGroup(String identifier) throws AuthorizationAccessException {
|
||||
return AbstractPolicyBasedAuthorizer.this.getGroup(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException {
|
||||
final UsersAndAccessPolicies usersAndAccessPolicies = AbstractPolicyBasedAuthorizer.this.getUsersAndAccessPolicies();
|
||||
final User user = usersAndAccessPolicies.getUser(identity);
|
||||
final Set<Group> groups = usersAndAccessPolicies.getGroups(identity);
|
||||
|
||||
return new UserAndGroups() {
|
||||
@Override
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFingerprint() throws AuthorizationAccessException {
|
||||
// fingerprint is managed by the encapsulating class
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
|
||||
// fingerprint is managed by the encapsulating class
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
|
||||
// fingerprint is managed by the encapsulating class
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a fingerprint representing the authorizations managed by this authorizer. The fingerprint will be
|
||||
* used for comparison to determine if two policy-based authorizers represent a compatible set of users,
|
||||
|
@ -510,17 +642,12 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
*
|
||||
* @return the fingerprint for this Authorizer
|
||||
*/
|
||||
@Override
|
||||
public final String getFingerprint() throws AuthorizationAccessException {
|
||||
final List<User> users = getSortedUsers();
|
||||
final List<Group> groups = getSortedGroups();
|
||||
final List<AccessPolicy> policies = getSortedAccessPolicies();
|
||||
|
||||
// when there are no users, groups, policies we want to always return a simple indicator so
|
||||
// it can easily be determined when comparing fingerprints
|
||||
if (users.isEmpty() && groups.isEmpty() && policies.isEmpty()) {
|
||||
return EMPTY_FINGERPRINT;
|
||||
}
|
||||
|
||||
XMLStreamWriter writer = null;
|
||||
final StringWriter out = new StringWriter();
|
||||
try {
|
||||
|
@ -611,38 +738,43 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer {
|
|||
|
||||
private List<AccessPolicy> getSortedAccessPolicies() {
|
||||
final List<AccessPolicy> policies = new ArrayList<>(getAccessPolicies());
|
||||
|
||||
Collections.sort(policies, new Comparator<AccessPolicy>() {
|
||||
@Override
|
||||
public int compare(AccessPolicy p1, AccessPolicy p2) {
|
||||
return p1.getIdentifier().compareTo(p2.getIdentifier());
|
||||
}
|
||||
});
|
||||
Collections.sort(policies, Comparator.comparing(AccessPolicy::getIdentifier));
|
||||
return policies;
|
||||
}
|
||||
|
||||
private List<Group> getSortedGroups() {
|
||||
final List<Group> groups = new ArrayList<>(getGroups());
|
||||
|
||||
Collections.sort(groups, new Comparator<Group>() {
|
||||
@Override
|
||||
public int compare(Group g1, Group g2) {
|
||||
return g1.getIdentifier().compareTo(g2.getIdentifier());
|
||||
}
|
||||
});
|
||||
Collections.sort(groups, Comparator.comparing(Group::getIdentifier));
|
||||
return groups;
|
||||
}
|
||||
|
||||
private List<User> getSortedUsers() {
|
||||
final List<User> users = new ArrayList<>(getUsers());
|
||||
|
||||
Collections.sort(users, new Comparator<User>() {
|
||||
@Override
|
||||
public int compare(User u1, User u2) {
|
||||
return u1.getIdentifier().compareTo(u2.getIdentifier());
|
||||
}
|
||||
});
|
||||
Collections.sort(users, Comparator.comparing(User::getIdentifier));
|
||||
return users;
|
||||
}
|
||||
|
||||
private static class PoliciesUsersAndGroups {
|
||||
final List<AccessPolicy> accessPolicies;
|
||||
final List<User> users;
|
||||
final List<Group> groups;
|
||||
|
||||
public PoliciesUsersAndGroups(List<AccessPolicy> accessPolicies, List<User> users, List<Group> groups) {
|
||||
this.accessPolicies = accessPolicies;
|
||||
this.users = users;
|
||||
this.groups = groups;
|
||||
}
|
||||
|
||||
public List<AccessPolicy> getAccessPolicies() {
|
||||
return accessPolicies;
|
||||
}
|
||||
|
||||
public List<User> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public List<Group> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
*/
|
||||
package org.apache.nifi.authorization;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Defines a policy for a set of userIdentifiers to perform a set of actions on a given resource.
|
||||
|
@ -173,6 +175,41 @@ public class AccessPolicy {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier of the builder to a random UUID.
|
||||
*
|
||||
* @return the builder
|
||||
* @throws IllegalStateException if this method is called when this builder was constructed from an existing Policy
|
||||
*/
|
||||
public Builder identifierGenerateRandom() {
|
||||
if (fromPolicy) {
|
||||
throw new IllegalStateException(
|
||||
"Identifier can not be changed when initialized from an existing policy");
|
||||
}
|
||||
|
||||
this.identifier = UUID.randomUUID().toString();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier of the builder with a UUID generated from the specified seed string.
|
||||
*
|
||||
* @return the builder
|
||||
* @throws IllegalStateException if this method is called when this builder was constructed from an existing Policy
|
||||
*/
|
||||
public Builder identifierGenerateFromSeed(final String seed) {
|
||||
if (fromPolicy) {
|
||||
throw new IllegalStateException(
|
||||
"Identifier can not be changed when initialized from an existing policy");
|
||||
}
|
||||
if (seed == null) {
|
||||
throw new IllegalArgumentException("Cannot seed the policy identifier with a null value.");
|
||||
}
|
||||
|
||||
this.identifier = UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)).toString();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource of the builder.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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 java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides access to AccessPolicies and the configured UserGroupProvider.
|
||||
*
|
||||
* NOTE: Extensions will be called often and frequently. Because of this, if the underlying implementation needs to
|
||||
* make remote calls or expensive calculations those should probably be done asynchronously and/or cache the results.
|
||||
*
|
||||
* Additionally, extensions need to be thread safe.
|
||||
*/
|
||||
public interface AccessPolicyProvider {
|
||||
|
||||
/**
|
||||
* Retrieves all access policies. Must be non null
|
||||
*
|
||||
* @return a list of policies
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Retrieves the policy with the given identifier.
|
||||
*
|
||||
* @param identifier the id of the policy to retrieve
|
||||
* @return the policy with the given id, or null if no matching policy exists
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Gets the access policies for the specified resource identifier and request action.
|
||||
*
|
||||
* @param resourceIdentifier the resource identifier
|
||||
* @param action the request action
|
||||
* @return the policy matching the resouce and action, or null if no matching policy exists
|
||||
* @throws AuthorizationAccessException if there was any unexpected error performing the operation
|
||||
*/
|
||||
AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Returns the UserGroupProvider for this managed Authorizer. Must be non null
|
||||
*
|
||||
* @return the UserGroupProvider
|
||||
*/
|
||||
UserGroupProvider getUserGroupProvider();
|
||||
|
||||
/**
|
||||
* Called immediately after instance creation for implementers to perform additional setup
|
||||
*
|
||||
* @param initializationContext in which to initialize
|
||||
*/
|
||||
void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException;
|
||||
|
||||
/**
|
||||
* Called to configure the Authorizer.
|
||||
*
|
||||
* @param configurationContext at the time of configuration
|
||||
* @throws AuthorizerCreationException for any issues configuring the provider
|
||||
*/
|
||||
void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException;
|
||||
|
||||
/**
|
||||
* Called immediately before instance destruction for implementers to release resources.
|
||||
*
|
||||
* @throws AuthorizerDestructionException If pre-destruction fails.
|
||||
*/
|
||||
void preDestruction() throws AuthorizerDestructionException;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Initialization content for AccessPolicyProviders.
|
||||
*/
|
||||
public interface AccessPolicyProviderInitializationContext extends UserGroupProviderInitializationContext {
|
||||
|
||||
/**
|
||||
* The lookup for accessing other configured AccessPolicyProviders.
|
||||
*
|
||||
* @return The AccessPolicyProvider lookup
|
||||
*/
|
||||
AccessPolicyProviderLookup getAccessPolicyProviderLookup();
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 interface AccessPolicyProviderLookup {
|
||||
|
||||
/**
|
||||
* Looks up the AccessPolicyProvider with the specified identifier
|
||||
*
|
||||
* @param identifier The identifier of the AccessPolicyProvider
|
||||
* @return The AccessPolicyProvider
|
||||
*/
|
||||
AccessPolicyProvider getAccessPolicyProvider(String identifier);
|
||||
}
|
|
@ -20,6 +20,7 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
|
@ -31,6 +32,7 @@ public class AuthorizationRequest {
|
|||
|
||||
private final Resource resource;
|
||||
private final String identity;
|
||||
private final Set<String> groups;
|
||||
private final RequestAction action;
|
||||
private final boolean isAccessAttempt;
|
||||
private final boolean isAnonymous;
|
||||
|
@ -46,6 +48,7 @@ public class AuthorizationRequest {
|
|||
|
||||
this.resource = builder.resource;
|
||||
this.identity = builder.identity;
|
||||
this.groups = builder.groups == null ? null : Collections.unmodifiableSet(builder.groups);
|
||||
this.action = builder.action;
|
||||
this.isAccessAttempt = builder.isAccessAttempt;
|
||||
this.isAnonymous = builder.isAnonymous;
|
||||
|
@ -81,6 +84,16 @@ public class AuthorizationRequest {
|
|||
return identity;
|
||||
}
|
||||
|
||||
/**
|
||||
* The groups the user making this request belongs to. May be null if this NiFi is not configured to load user
|
||||
* groups or empty if the user has no groups
|
||||
*
|
||||
* @return The groups
|
||||
*/
|
||||
public Set<String> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this is a direct access attempt of the Resource if if it's being checked as part of another response.
|
||||
*
|
||||
|
@ -142,6 +155,7 @@ public class AuthorizationRequest {
|
|||
|
||||
private Resource resource;
|
||||
private String identity;
|
||||
private Set<String> groups;
|
||||
private Boolean isAnonymous;
|
||||
private Boolean isAccessAttempt;
|
||||
private RequestAction action;
|
||||
|
@ -159,6 +173,11 @@ public class AuthorizationRequest {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder groups(final Set<String> groups) {
|
||||
this.groups = groups;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder anonymous(final Boolean isAnonymous) {
|
||||
this.isAnonymous = isAnonymous;
|
||||
return this;
|
||||
|
|
|
@ -27,6 +27,10 @@ public interface Authorizer {
|
|||
|
||||
/**
|
||||
* Determines if the specified user/entity is authorized to access the specified resource within the given context.
|
||||
* These details are all contained in the AuthorizationRequest.
|
||||
*
|
||||
* NOTE: This method will be called often and frequently. Because of this, if the underlying implementation needs to
|
||||
* make remote calls or expensive calculations those should probably be done asynchronously and/or cache the results.
|
||||
*
|
||||
* @param request The authorization request
|
||||
* @return the authorization result
|
||||
|
|
|
@ -42,9 +42,7 @@ public interface AuthorizerConfigurationContext {
|
|||
|
||||
/**
|
||||
* @param property to lookup the descriptor and value of
|
||||
* @return the value the component currently understands for the given
|
||||
* PropertyDescriptor. This method does not substitute default
|
||||
* PropertyDescriptor values, so the value returned will be null if not set
|
||||
* @return the value the component currently understands for the given PropertyDescriptor
|
||||
*/
|
||||
PropertyValue getProperty(String property);
|
||||
}
|
||||
|
|
|
@ -19,19 +19,12 @@ package org.apache.nifi.authorization;
|
|||
/**
|
||||
* Initialization content for Authorizers.
|
||||
*/
|
||||
public interface AuthorizerInitializationContext {
|
||||
|
||||
/**
|
||||
* The identifier of the Authorizer.
|
||||
*
|
||||
* @return The identifier
|
||||
*/
|
||||
public String getIdentifier();
|
||||
public interface AuthorizerInitializationContext extends AccessPolicyProviderInitializationContext {
|
||||
|
||||
/**
|
||||
* The lookup for accessing other configured Authorizers.
|
||||
*
|
||||
* @return The Authorizer lookup
|
||||
*/
|
||||
public AuthorizerLookup getAuthorizerLookup();
|
||||
AuthorizerLookup getAuthorizerLookup();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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.UninheritableAuthorizationsException;
|
||||
|
||||
/**
|
||||
* Provides support for configuring AccessPolicies.
|
||||
*
|
||||
* NOTE: Extensions will be called often and frequently. Because of this, if the underlying implementation needs to
|
||||
* make remote calls or expensive calculations those should probably be done asynchronously and/or cache the results.
|
||||
*
|
||||
* Additionally, extensions need to be thread safe.
|
||||
*/
|
||||
public interface ConfigurableAccessPolicyProvider extends AccessPolicyProvider {
|
||||
|
||||
/**
|
||||
* Returns a fingerprint representing the authorizations managed by this authorizer. The fingerprint will be
|
||||
* used for comparison to determine if two policy-based authorizers represent a compatible set of policies.
|
||||
*
|
||||
* @return the fingerprint for this Authorizer
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
String getFingerprint() throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Parses the fingerprint and adds any policies to the current AccessPolicyProvider.
|
||||
*
|
||||
* @param fingerprint the fingerprint that was obtained from calling getFingerprint() on another Authorizer.
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
void inheritFingerprint(final String fingerprint) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* When the fingerprints are not equal, this method will check if the proposed fingerprint is inheritable.
|
||||
* If the fingerprint is an exact match, this method will not be invoked as there is nothing to inherit.
|
||||
*
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
* @throws UninheritableAuthorizationsException if the proposed fingerprint was uninheritable
|
||||
*/
|
||||
void checkInheritability(final String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException;
|
||||
|
||||
/**
|
||||
* Adds the given policy ensuring that multiple policies can not be added for the same resource and action.
|
||||
*
|
||||
* @param accessPolicy the policy to add
|
||||
* @return the policy that was added
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
AccessPolicy addAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* The policy represented by the provided instance will be updated based on the provided instance.
|
||||
*
|
||||
* @param accessPolicy an updated policy
|
||||
* @return the updated policy, or null if no matching policy was found
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Deletes the given policy.
|
||||
*
|
||||
* @param accessPolicy the policy to delete
|
||||
* @return the deleted policy, or null if no matching policy was found
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
AccessPolicy deleteAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException;
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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.UninheritableAuthorizationsException;
|
||||
|
||||
/**
|
||||
* Provides support for configuring Users and Groups.
|
||||
*
|
||||
* NOTE: Extensions will be called often and frequently. Because of this, if the underlying implementation needs to
|
||||
* make remote calls or expensive calculations those should probably be done asynchronously and/or cache the results.
|
||||
*
|
||||
* Additionally, extensions need to be thread safe.
|
||||
*/
|
||||
public interface ConfigurableUserGroupProvider extends UserGroupProvider {
|
||||
|
||||
/**
|
||||
* Returns a fingerprint representing the authorizations managed by this authorizer. The fingerprint will be
|
||||
* used for comparison to determine if two policy-based authorizers represent a compatible set of users and/or groups.
|
||||
*
|
||||
* @return the fingerprint for this Authorizer
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
String getFingerprint() throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Parses the fingerprint and adds any users and groups to the current Authorizer.
|
||||
*
|
||||
* @param fingerprint the fingerprint that was obtained from calling getFingerprint() on another Authorizer.
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
void inheritFingerprint(final String fingerprint) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* When the fingerprints are not equal, this method will check if the proposed fingerprint is inheritable.
|
||||
* If the fingerprint is an exact match, this method will not be invoked as there is nothing to inherit.
|
||||
*
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
* @throws UninheritableAuthorizationsException if the proposed fingerprint was uninheritable
|
||||
*/
|
||||
void checkInheritability(final String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException;
|
||||
|
||||
/**
|
||||
* Adds the given user.
|
||||
*
|
||||
* @param user the user to add
|
||||
* @return the user that was added
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
* @throws IllegalStateException if there is already a user with the same identity
|
||||
*/
|
||||
User addUser(User user) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* The user represented by the provided instance will be updated based on the provided instance.
|
||||
*
|
||||
* @param user an updated user instance
|
||||
* @return the updated user instance, or null if no matching user was found
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
* @throws IllegalStateException if there is already a user with the same identity
|
||||
*/
|
||||
User updateUser(final User user) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Deletes the given user.
|
||||
*
|
||||
* @param user the user to delete
|
||||
* @return the user that was deleted, or null if no matching user was found
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
User deleteUser(User user) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Adds a new group.
|
||||
*
|
||||
* @param group the Group to add
|
||||
* @return the added Group
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
* @throws IllegalStateException if a group with the same name already exists
|
||||
*/
|
||||
Group addGroup(Group group) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* The group represented by the provided instance will be updated based on the provided instance.
|
||||
*
|
||||
* @param group an updated group instance
|
||||
* @return the updated group instance, or null if no matching group was found
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
* @throws IllegalStateException if there is already a group with the same name
|
||||
*/
|
||||
Group updateGroup(Group group) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Deletes the given group.
|
||||
*
|
||||
* @param group the group to delete
|
||||
* @return the deleted group, or null if no matching group was found
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
Group deleteGroup(Group group) throws AuthorizationAccessException;
|
||||
}
|
|
@ -16,10 +16,12 @@
|
|||
*/
|
||||
package org.apache.nifi.authorization;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A group that users can belong to.
|
||||
|
@ -141,6 +143,41 @@ public class Group { // TODO rename to UserGroup
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier of the builder to a random UUID.
|
||||
*
|
||||
* @return the builder
|
||||
* @throws IllegalStateException if this method is called when this builder was constructed from an existing Group
|
||||
*/
|
||||
public Builder identifierGenerateRandom() {
|
||||
if (fromGroup) {
|
||||
throw new IllegalStateException(
|
||||
"Identifier can not be changed when initialized from an existing group");
|
||||
}
|
||||
|
||||
this.identifier = UUID.randomUUID().toString();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier of the builder with a UUID generated from the specified seed string.
|
||||
*
|
||||
* @return the builder
|
||||
* @throws IllegalStateException if this method is called when this builder was constructed from an existing Group
|
||||
*/
|
||||
public Builder identifierGenerateFromSeed(final String seed) {
|
||||
if (fromGroup) {
|
||||
throw new IllegalStateException(
|
||||
"Identifier can not be changed when initialized from an existing group");
|
||||
}
|
||||
if (seed == null) {
|
||||
throw new IllegalArgumentException("Cannot seed the group identifier with a null value.");
|
||||
}
|
||||
|
||||
this.identifier = UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)).toString();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the builder.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.UninheritableAuthorizationsException;
|
||||
|
||||
public interface ManagedAuthorizer extends Authorizer {
|
||||
|
||||
/**
|
||||
* Returns a fingerprint representing the authorizations managed by this authorizer. The fingerprint will be
|
||||
* used for comparison to determine if two managed authorizers represent a compatible set of users,
|
||||
* groups, and/or policies. Must be non null
|
||||
*
|
||||
* @return the fingerprint for this Authorizer
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
String getFingerprint() throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Parses the fingerprint and adds any users, groups, and policies to the current Authorizer.
|
||||
*
|
||||
* @param fingerprint the fingerprint that was obtained from calling getFingerprint() on another Authorizer.
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
void inheritFingerprint(final String fingerprint) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* When the fingerprints are not equal, this method will check if the proposed fingerprint is inheritable.
|
||||
* If the fingerprint is an exact match, this method will not be invoked as there is nothing to inherit.
|
||||
*
|
||||
* @param proposedFingerprint the proposed fingerprint
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
* @throws UninheritableAuthorizationsException if the proposed fingerprint was uninheritable
|
||||
*/
|
||||
void checkInheritability(final String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException;
|
||||
|
||||
/**
|
||||
* Returns the AccessPolicy provider for this managed Authorizer. Must be non null
|
||||
*
|
||||
* @return the AccessPolicy provider
|
||||
*/
|
||||
AccessPolicyProvider getAccessPolicyProvider();
|
||||
|
||||
}
|
|
@ -16,7 +16,9 @@
|
|||
*/
|
||||
package org.apache.nifi.authorization;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A user to create authorization policies for.
|
||||
|
@ -128,6 +130,41 @@ public class User {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier of the builder to a random UUID.
|
||||
*
|
||||
* @return the builder
|
||||
* @throws IllegalStateException if this method is called when this builder was constructed from an existing User
|
||||
*/
|
||||
public Builder identifierGenerateRandom() {
|
||||
if (fromUser) {
|
||||
throw new IllegalStateException(
|
||||
"Identifier can not be changed when initialized from an existing user");
|
||||
}
|
||||
|
||||
this.identifier = UUID.randomUUID().toString();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier of the builder with a UUID generated from the specified seed string.
|
||||
*
|
||||
* @return the builder
|
||||
* @throws IllegalStateException if this method is called when this builder was constructed from an existing User
|
||||
*/
|
||||
public Builder identifierGenerateFromSeed(final String seed) {
|
||||
if (fromUser) {
|
||||
throw new IllegalStateException(
|
||||
"Identifier can not be changed when initialized from an existing user");
|
||||
}
|
||||
if (seed == null) {
|
||||
throw new IllegalArgumentException("Cannot seed the user identifier with a null value.");
|
||||
}
|
||||
|
||||
this.identifier = UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)).toString();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identity of the builder.
|
||||
*
|
||||
|
|
|
@ -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.authorization;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A holder object to provide atomic access to a user and their groups.
|
||||
*/
|
||||
public interface UserAndGroups {
|
||||
|
||||
/**
|
||||
* Retrieves the user, or null if the user is unknown
|
||||
*
|
||||
* @return the user with the given identity
|
||||
*/
|
||||
User getUser();
|
||||
|
||||
/**
|
||||
* Retrieves the groups for the user, or null if the user is unknown or has no groups.
|
||||
*
|
||||
* @return the set of groups for the given user identity
|
||||
*/
|
||||
Set<Group> getGroups();
|
||||
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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 java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides access to Users and Groups.
|
||||
*
|
||||
* NOTE: Extensions will be called often and frequently. Because of this, if the underlying implementation needs to
|
||||
* make remote calls or expensive calculations those should probably be done asynchronously and/or cache the results.
|
||||
*
|
||||
* Additionally, extensions need to be thread safe.
|
||||
*/
|
||||
public interface UserGroupProvider {
|
||||
|
||||
/**
|
||||
* Retrieves all users. Must be non null
|
||||
*
|
||||
* @return a list of users
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
Set<User> getUsers() throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
User getUser(String identifier) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
User getUserByIdentity(String identity) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Retrieves all groups. Must be non null
|
||||
*
|
||||
* @return a list of groups
|
||||
* @throws AuthorizationAccessException if there was an unexpected error performing the operation
|
||||
*/
|
||||
Set<Group> getGroups() throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
Group getGroup(String identifier) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException;
|
||||
|
||||
/**
|
||||
* Called immediately after instance creation for implementers to perform additional setup
|
||||
*
|
||||
* @param initializationContext in which to initialize
|
||||
*/
|
||||
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
|
||||
*/
|
||||
void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException;
|
||||
|
||||
/**
|
||||
* Called immediately before instance destruction for implementers to release resources.
|
||||
*
|
||||
* @throws AuthorizerDestructionException If pre-destruction fails.
|
||||
*/
|
||||
void preDestruction() throws AuthorizerDestructionException;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Initialization content for UserGroupProviders.
|
||||
*/
|
||||
public interface UserGroupProviderInitializationContext {
|
||||
|
||||
/**
|
||||
* The identifier of the UserGroupProvider.
|
||||
*
|
||||
* @return The identifier
|
||||
*/
|
||||
String getIdentifier();
|
||||
|
||||
/**
|
||||
* The lookup for accessing other configured UserGroupProviders.
|
||||
*
|
||||
* @return The UserGroupProvider lookup
|
||||
*/
|
||||
UserGroupProviderLookup getUserGroupProviderLookup();
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 interface UserGroupProviderLookup {
|
||||
|
||||
/**
|
||||
* Looks up the UserGroupProvider with the specified identifier
|
||||
*
|
||||
* @param identifier The identifier of the UserGroupProvider
|
||||
* @return The UserGroupProvider
|
||||
*/
|
||||
UserGroupProvider getUserGroupProvider(String identifier);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.exception;
|
||||
|
||||
/**
|
||||
* Represents the case when the proposed authorizations are not inheritable.
|
||||
*/
|
||||
public class UninheritableAuthorizationsException extends RuntimeException {
|
||||
|
||||
public UninheritableAuthorizationsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
|
@ -84,6 +84,7 @@ public interface Authorizable {
|
|||
final Resource resource = getResource();
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.identity(user.getIdentity())
|
||||
.groups(user.getGroups())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(false)
|
||||
.action(action)
|
||||
|
@ -188,6 +189,7 @@ public interface Authorizable {
|
|||
final Resource resource = getResource();
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.identity(user.getIdentity())
|
||||
.groups(user.getGroups())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(action)
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package org.apache.nifi.authorization.user;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A representation of a NiFi user that has logged into the application
|
||||
*/
|
||||
|
@ -27,6 +29,11 @@ public interface NiFiUser {
|
|||
*/
|
||||
String getIdentity();
|
||||
|
||||
/**
|
||||
* @return the groups that this user belongs to if this nifi is configured to load user groups, null otherwise.
|
||||
*/
|
||||
Set<String> getGroups();
|
||||
|
||||
/**
|
||||
* @return the next user in the proxied entities chain, or <code>null</code> if no more users exist in the chain.
|
||||
*/
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
*/
|
||||
package org.apache.nifi.authorization;
|
||||
|
||||
import org.apache.nifi.authorization.exception.AuthorizerCreationException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
@ -251,10 +250,7 @@ public class TestAbstractPolicyBasedAuthorizer {
|
|||
when(authorizer2.getAccessPolicies()).thenReturn(policies2);
|
||||
|
||||
// compare the fingerprints
|
||||
|
||||
assertEquals(authorizer1.getFingerprint(), authorizer2.getFingerprint());
|
||||
|
||||
//System.out.println(authorizer1.getFingerprint());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -332,211 +328,4 @@ public class TestAbstractPolicyBasedAuthorizer {
|
|||
Assert.assertTrue(fingerprint.length() > 0);
|
||||
}
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testOnConfiguredWhenPoliciesWithSameResourceAndAction() {
|
||||
User user1 = new User.Builder().identifier("user-id-1").identity("user-1").build();
|
||||
|
||||
AccessPolicy policy1 = new AccessPolicy.Builder()
|
||||
.identifier("policy-id-1")
|
||||
.resource("resource1")
|
||||
.action(RequestAction.READ)
|
||||
.addUser(user1.getIdentifier())
|
||||
.build();
|
||||
|
||||
AccessPolicy policy2 = new AccessPolicy.Builder()
|
||||
.identifier("policy-id-2")
|
||||
.resource("resource1")
|
||||
.action(RequestAction.READ)
|
||||
.addUser(user1.getIdentifier())
|
||||
.build();
|
||||
|
||||
Set<AccessPolicy> policies = new LinkedHashSet<>();
|
||||
policies.add(policy1);
|
||||
policies.add(policy2);
|
||||
|
||||
Set<User> users = new LinkedHashSet<>();
|
||||
users.add(user1);
|
||||
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
AbstractPolicyBasedAuthorizer authorizer = new MockPolicyBasedAuthorizer(new HashSet<>(), users, policies);
|
||||
authorizer.onConfigured(context);
|
||||
}
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testOnConfiguredWhenUsersWithSameIdentity() {
|
||||
User user1 = new User.Builder().identifier("user-id-1").identity("user-1").build();
|
||||
User user2 = new User.Builder().identifier("user-id-2").identity("user-1").build();
|
||||
|
||||
Set<User> users = new LinkedHashSet<>();
|
||||
users.add(user1);
|
||||
users.add(user2);
|
||||
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
AbstractPolicyBasedAuthorizer authorizer = new MockPolicyBasedAuthorizer(new HashSet<>(), users, new HashSet<>());
|
||||
authorizer.onConfigured(context);
|
||||
}
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testOnConfiguredWhenGroupsWithSameName() {
|
||||
Group group1 = new Group.Builder().identifier("group-id-1").name("group-1").build();
|
||||
Group group2 = new Group.Builder().identifier("group-id-2").name("group-1").build();
|
||||
|
||||
Set<Group> groups = new LinkedHashSet<>();
|
||||
groups.add(group1);
|
||||
groups.add(group2);
|
||||
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
AbstractPolicyBasedAuthorizer authorizer = new MockPolicyBasedAuthorizer(groups, new HashSet<>(), new HashSet<>());
|
||||
authorizer.onConfigured(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddPoliciesWithSameResourceAndAction() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
AbstractPolicyBasedAuthorizer authorizer = new MockPolicyBasedAuthorizer();
|
||||
authorizer.onConfigured(context);
|
||||
|
||||
User user1 = new User.Builder().identifier("user-id-1").identity("user-1").build();
|
||||
authorizer.addUser(user1);
|
||||
|
||||
AccessPolicy policy1 = new AccessPolicy.Builder()
|
||||
.identifier("policy-id-1")
|
||||
.resource("resource1")
|
||||
.action(RequestAction.READ)
|
||||
.addUser(user1.getIdentifier())
|
||||
.build();
|
||||
authorizer.addAccessPolicy(policy1);
|
||||
|
||||
AccessPolicy policy2 = new AccessPolicy.Builder()
|
||||
.identifier("policy-id-2")
|
||||
.resource("resource1")
|
||||
.action(RequestAction.READ)
|
||||
.addUser(user1.getIdentifier())
|
||||
.build();
|
||||
|
||||
try {
|
||||
authorizer.addAccessPolicy(policy2);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddUsersWithSameIdentity() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
AbstractPolicyBasedAuthorizer authorizer = new MockPolicyBasedAuthorizer();
|
||||
authorizer.onConfigured(context);
|
||||
|
||||
User user1 = new User.Builder().identifier("user-id-1").identity("user-1").build();
|
||||
authorizer.addUser(user1);
|
||||
|
||||
User user2 = new User.Builder().identifier("user-id-2").identity("user-1").build();
|
||||
|
||||
try {
|
||||
authorizer.addUser(user2);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddGroupsWithSameName() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
AbstractPolicyBasedAuthorizer authorizer = new MockPolicyBasedAuthorizer();
|
||||
authorizer.onConfigured(context);
|
||||
|
||||
Group group1 = new Group.Builder().identifier("group-id-1").name("group-1").build();
|
||||
authorizer.addGroup(group1);
|
||||
|
||||
Group group2 = new Group.Builder().identifier("group-id-2").name("group-1").build();
|
||||
|
||||
try {
|
||||
authorizer.addGroup(group2);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddUsersWithSameIdentityAsGroupName() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
AbstractPolicyBasedAuthorizer authorizer = new MockPolicyBasedAuthorizer();
|
||||
authorizer.onConfigured(context);
|
||||
|
||||
Group group1 = new Group.Builder().identifier("group-id-1").name("abc").build();
|
||||
authorizer.addGroup(group1);
|
||||
|
||||
User user = new User.Builder().identifier("user-id-2").identity("abc").build();
|
||||
|
||||
try {
|
||||
authorizer.addUser(user);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddGroupWithSameNameAsUserIdentity() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
AbstractPolicyBasedAuthorizer authorizer = new MockPolicyBasedAuthorizer();
|
||||
authorizer.onConfigured(context);
|
||||
|
||||
User user = new User.Builder().identifier("user-id-2").identity("abc").build();
|
||||
authorizer.addUser(user);
|
||||
|
||||
Group group1 = new Group.Builder().identifier("group-id-1").name("abc").build();
|
||||
try {
|
||||
authorizer.addGroup(group1);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateUserWithSameIdentity() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
AbstractPolicyBasedAuthorizer authorizer = new MockPolicyBasedAuthorizer();
|
||||
authorizer.onConfigured(context);
|
||||
|
||||
User user1 = new User.Builder().identifier("user-id-1").identity("abc").build();
|
||||
authorizer.addUser(user1);
|
||||
|
||||
User user2 = new User.Builder().identifier("user-id-2").identity("xyz").build();
|
||||
authorizer.addUser(user2);
|
||||
|
||||
try {
|
||||
User user1Updated = new User.Builder().identifier("user-id-1").identity("xyz").build();
|
||||
authorizer.updateUser(user1Updated);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateGroupWithSameName() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
AbstractPolicyBasedAuthorizer authorizer = new MockPolicyBasedAuthorizer();
|
||||
authorizer.onConfigured(context);
|
||||
|
||||
Group group1 = new Group.Builder().identifier("group-id-1").name("abc").build();
|
||||
authorizer.addGroup(group1);
|
||||
|
||||
Group group2 = new Group.Builder().identifier("group-id-2").name("xyz").build();
|
||||
authorizer.addGroup(group2);
|
||||
|
||||
try {
|
||||
Group group1Updated = new Group.Builder().identifier("group-id-1").name("xyz").build();
|
||||
authorizer.updateGroup(group1Updated);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -63,10 +63,6 @@
|
|||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-nar-utils</artifactId>
|
||||
|
@ -75,6 +71,22 @@
|
|||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-properties</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-nar-utils</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-authorization</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* 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.exception.UninheritableAuthorizationsException;
|
||||
import org.apache.nifi.nar.NarCloseable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public final class AccessPolicyProviderFactory {
|
||||
|
||||
public static AccessPolicyProvider withNarLoader(final AccessPolicyProvider baseAccessPolicyProvider) {
|
||||
if (baseAccessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
|
||||
final ConfigurableAccessPolicyProvider baseConfigurableAccessPolicyProvider = (ConfigurableAccessPolicyProvider) baseAccessPolicyProvider;
|
||||
return new ConfigurableAccessPolicyProvider() {
|
||||
@Override
|
||||
public AccessPolicy addAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableAccessPolicyProvider.addAccessPolicy(accessPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableAccessPolicyProvider.updateAccessPolicy(accessPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy deleteAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableAccessPolicyProvider.deleteAccessPolicy(accessPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableAccessPolicyProvider.getAccessPolicies();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableAccessPolicyProvider.getAccessPolicy(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableAccessPolicyProvider.getAccessPolicy(resourceIdentifier, action);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserGroupProvider getUserGroupProvider() {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableAccessPolicyProvider.getUserGroupProvider();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseConfigurableAccessPolicyProvider.inheritFingerprint(fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseConfigurableAccessPolicyProvider.checkInheritability(proposedFingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFingerprint() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableAccessPolicyProvider.getFingerprint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseConfigurableAccessPolicyProvider.initialize(initializationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseConfigurableAccessPolicyProvider.onConfigured(configurationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseConfigurableAccessPolicyProvider.preDestruction();
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return new AccessPolicyProvider() {
|
||||
@Override
|
||||
public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseAccessPolicyProvider.getAccessPolicies();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseAccessPolicyProvider.getAccessPolicy(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseAccessPolicyProvider.getAccessPolicy(resourceIdentifier, action);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserGroupProvider getUserGroupProvider() {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseAccessPolicyProvider.getUserGroupProvider();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseAccessPolicyProvider.initialize(initializationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseAccessPolicyProvider.onConfigured(configurationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseAccessPolicyProvider.preDestruction();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private AccessPolicyProviderFactory() {}
|
||||
}
|
|
@ -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.authorization;
|
||||
|
||||
public final class AuthorizerCapabilityDetection {
|
||||
|
||||
public static boolean isManagedAuthorizer(final Authorizer authorizer) {
|
||||
return authorizer instanceof ManagedAuthorizer;
|
||||
}
|
||||
|
||||
public static boolean isConfigurableAccessPolicyProvider(final Authorizer authorizer) {
|
||||
if (!isManagedAuthorizer(authorizer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) authorizer;
|
||||
return managedAuthorizer.getAccessPolicyProvider() instanceof ConfigurableAccessPolicyProvider;
|
||||
}
|
||||
|
||||
public static boolean isConfigurableUserGroupProvider(final Authorizer authorizer) {
|
||||
if (!isManagedAuthorizer(authorizer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) authorizer;
|
||||
final AccessPolicyProvider accessPolicyProvider = managedAuthorizer.getAccessPolicyProvider();
|
||||
return accessPolicyProvider.getUserGroupProvider() instanceof ConfigurableUserGroupProvider;
|
||||
}
|
||||
|
||||
private AuthorizerCapabilityDetection() {}
|
||||
}
|
|
@ -0,0 +1,426 @@
|
|||
/*
|
||||
* 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.exception.UninheritableAuthorizationsException;
|
||||
import org.apache.nifi.nar.NarCloseable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public final class AuthorizerFactory {
|
||||
|
||||
/**
|
||||
* Checks if another policy exists with the same resource and action as the given policy.
|
||||
*
|
||||
* @param checkAccessPolicy an access policy being checked
|
||||
* @return true if another access policy exists with the same resource and action, false otherwise
|
||||
*/
|
||||
private static boolean policyExists(final AccessPolicyProvider accessPolicyProvider, final AccessPolicy checkAccessPolicy) {
|
||||
for (AccessPolicy accessPolicy : accessPolicyProvider.getAccessPolicies()) {
|
||||
if (!accessPolicy.getIdentifier().equals(checkAccessPolicy.getIdentifier())
|
||||
&& accessPolicy.getResource().equals(checkAccessPolicy.getResource())
|
||||
&& accessPolicy.getAction().equals(checkAccessPolicy.getAction())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if another user exists with the same identity.
|
||||
*
|
||||
* @param identifier identity of the user
|
||||
* @param identity identity of the user
|
||||
* @return true if another user exists with the same identity, false otherwise
|
||||
*/
|
||||
private static boolean tenantExists(final UserGroupProvider userGroupProvider, final String identifier, final String identity) {
|
||||
for (User user : userGroupProvider.getUsers()) {
|
||||
if (!user.getIdentifier().equals(identifier)
|
||||
&& user.getIdentity().equals(identity)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (Group group : userGroupProvider.getGroups()) {
|
||||
if (!group.getIdentifier().equals(identifier)
|
||||
&& group.getName().equals(identity)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Authorizer installIntegrityChecks(final Authorizer baseAuthorizer) {
|
||||
if (baseAuthorizer instanceof ManagedAuthorizer) {
|
||||
final ManagedAuthorizer baseManagedAuthorizer = (ManagedAuthorizer) baseAuthorizer;
|
||||
return new ManagedAuthorizer() {
|
||||
@Override
|
||||
public String getFingerprint() throws AuthorizationAccessException {
|
||||
return baseManagedAuthorizer.getFingerprint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
|
||||
baseManagedAuthorizer.inheritFingerprint(fingerprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
|
||||
baseManagedAuthorizer.checkInheritability(proposedFingerprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicyProvider getAccessPolicyProvider() {
|
||||
final AccessPolicyProvider baseAccessPolicyProvider = baseManagedAuthorizer.getAccessPolicyProvider();
|
||||
if (baseAccessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
|
||||
final ConfigurableAccessPolicyProvider baseConfigurableAccessPolicyProvider = (ConfigurableAccessPolicyProvider) baseAccessPolicyProvider;
|
||||
return new ConfigurableAccessPolicyProvider() {
|
||||
@Override
|
||||
public String getFingerprint() throws AuthorizationAccessException {
|
||||
return baseConfigurableAccessPolicyProvider.getFingerprint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
|
||||
baseConfigurableAccessPolicyProvider.inheritFingerprint(fingerprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
|
||||
baseConfigurableAccessPolicyProvider.checkInheritability(proposedFingerprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy addAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
if (policyExists(baseConfigurableAccessPolicyProvider, accessPolicy)) {
|
||||
throw new IllegalStateException(String.format("Found multiple policies for '%s' with '%s'.", accessPolicy.getResource(), accessPolicy.getAction()));
|
||||
}
|
||||
return baseConfigurableAccessPolicyProvider.addAccessPolicy(accessPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
return baseConfigurableAccessPolicyProvider.updateAccessPolicy(accessPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy deleteAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
return baseConfigurableAccessPolicyProvider.deleteAccessPolicy(accessPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException {
|
||||
return baseConfigurableAccessPolicyProvider.getAccessPolicies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException {
|
||||
return baseConfigurableAccessPolicyProvider.getAccessPolicy(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException {
|
||||
return baseConfigurableAccessPolicyProvider.getAccessPolicy(resourceIdentifier, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserGroupProvider getUserGroupProvider() {
|
||||
final UserGroupProvider baseUserGroupProvider = baseConfigurableAccessPolicyProvider.getUserGroupProvider();
|
||||
if (baseUserGroupProvider instanceof ConfigurableUserGroupProvider) {
|
||||
final ConfigurableUserGroupProvider baseConfigurableUserGroupProvider = (ConfigurableUserGroupProvider) baseUserGroupProvider;
|
||||
return new ConfigurableUserGroupProvider() {
|
||||
@Override
|
||||
public String getFingerprint() throws AuthorizationAccessException {
|
||||
return baseConfigurableUserGroupProvider.getFingerprint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
|
||||
baseConfigurableUserGroupProvider.inheritFingerprint(fingerprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
|
||||
baseConfigurableUserGroupProvider.checkInheritability(proposedFingerprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User addUser(User user) throws AuthorizationAccessException {
|
||||
if (tenantExists(baseConfigurableUserGroupProvider, user.getIdentifier(), user.getIdentity())) {
|
||||
throw new IllegalStateException(String.format("User/user group already exists with the identity '%s'.", user.getIdentity()));
|
||||
}
|
||||
return baseConfigurableUserGroupProvider.addUser(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User updateUser(User user) throws AuthorizationAccessException {
|
||||
if (tenantExists(baseConfigurableUserGroupProvider, user.getIdentifier(), user.getIdentity())) {
|
||||
throw new IllegalStateException(String.format("User/user group already exists with the identity '%s'.", user.getIdentity()));
|
||||
}
|
||||
return baseConfigurableUserGroupProvider.updateUser(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User deleteUser(User user) throws AuthorizationAccessException {
|
||||
return baseConfigurableUserGroupProvider.deleteUser(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group addGroup(Group group) throws AuthorizationAccessException {
|
||||
if (tenantExists(baseConfigurableUserGroupProvider, group.getIdentifier(), group.getName())) {
|
||||
throw new IllegalStateException(String.format("User/user group already exists with the identity '%s'.", group.getName()));
|
||||
}
|
||||
return baseConfigurableUserGroupProvider.addGroup(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group updateGroup(Group group) throws AuthorizationAccessException {
|
||||
if (tenantExists(baseConfigurableUserGroupProvider, group.getIdentifier(), group.getName())) {
|
||||
throw new IllegalStateException(String.format("User/user group already exists with the identity '%s'.", group.getName()));
|
||||
}
|
||||
return baseConfigurableUserGroupProvider.updateGroup(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group deleteGroup(Group group) throws AuthorizationAccessException {
|
||||
return baseConfigurableUserGroupProvider.deleteGroup(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<User> getUsers() throws AuthorizationAccessException {
|
||||
return baseConfigurableUserGroupProvider.getUsers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(String identifier) throws AuthorizationAccessException {
|
||||
return baseConfigurableUserGroupProvider.getUser(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByIdentity(String identity) throws AuthorizationAccessException {
|
||||
return baseConfigurableUserGroupProvider.getUserByIdentity(identity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() throws AuthorizationAccessException {
|
||||
return baseConfigurableUserGroupProvider.getGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getGroup(String identifier) throws AuthorizationAccessException {
|
||||
return baseConfigurableUserGroupProvider.getGroup(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException {
|
||||
return baseConfigurableUserGroupProvider.getUserAndGroups(identity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
baseConfigurableUserGroupProvider.initialize(initializationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
baseConfigurableUserGroupProvider.onConfigured(configurationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
baseConfigurableUserGroupProvider.preDestruction();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return baseUserGroupProvider;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
baseConfigurableAccessPolicyProvider.initialize(initializationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
baseConfigurableAccessPolicyProvider.onConfigured(configurationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
baseConfigurableAccessPolicyProvider.preDestruction();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return baseAccessPolicyProvider;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException {
|
||||
return baseManagedAuthorizer.authorize(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
baseManagedAuthorizer.initialize(initializationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
baseManagedAuthorizer.onConfigured(configurationContext);
|
||||
|
||||
final AccessPolicyProvider accessPolicyProvider = baseManagedAuthorizer.getAccessPolicyProvider();
|
||||
final UserGroupProvider userGroupProvider = accessPolicyProvider.getUserGroupProvider();
|
||||
|
||||
// ensure that only one policy per resource-action exists
|
||||
for (AccessPolicy accessPolicy : accessPolicyProvider.getAccessPolicies()) {
|
||||
if (policyExists(accessPolicyProvider, accessPolicy)) {
|
||||
throw new AuthorizerCreationException(String.format("Found multiple policies for '%s' with '%s'.", accessPolicy.getResource(), accessPolicy.getAction()));
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that only one group exists per identity
|
||||
for (User user : userGroupProvider.getUsers()) {
|
||||
if (tenantExists(userGroupProvider, user.getIdentifier(), user.getIdentity())) {
|
||||
throw new AuthorizerCreationException(String.format("Found multiple users/user groups with identity '%s'.", user.getIdentity()));
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that only one group exists per identity
|
||||
for (Group group : userGroupProvider.getGroups()) {
|
||||
if (tenantExists(userGroupProvider, group.getIdentifier(), group.getName())) {
|
||||
throw new AuthorizerCreationException(String.format("Found multiple users/user groups with name '%s'.", group.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
baseManagedAuthorizer.preDestruction();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return baseAuthorizer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorates the base authorizer to ensure the nar context classloader is used when invoking the underlying methods.
|
||||
*
|
||||
* @param baseAuthorizer base authorizer
|
||||
* @return authorizer
|
||||
*/
|
||||
public static Authorizer withNarLoader(final Authorizer baseAuthorizer) {
|
||||
if (baseAuthorizer instanceof ManagedAuthorizer) {
|
||||
final ManagedAuthorizer baseManagedAuthorizer = (ManagedAuthorizer) baseAuthorizer;
|
||||
return new ManagedAuthorizer() {
|
||||
@Override
|
||||
public String getFingerprint() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseManagedAuthorizer.getFingerprint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseManagedAuthorizer.inheritFingerprint(fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseManagedAuthorizer.checkInheritability(proposedFingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicyProvider getAccessPolicyProvider() {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseManagedAuthorizer.getAccessPolicyProvider();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseManagedAuthorizer.authorize(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseManagedAuthorizer.initialize(initializationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseManagedAuthorizer.onConfigured(configurationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseManagedAuthorizer.preDestruction();
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return new Authorizer() {
|
||||
@Override
|
||||
public AuthorizationResult authorize(final AuthorizationRequest request) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseAuthorizer.authorize(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseAuthorizer.initialize(initializationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseAuthorizer.onConfigured(configurationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseAuthorizer.preDestruction();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private AuthorizerFactory() {}
|
||||
}
|
|
@ -25,7 +25,6 @@ import org.apache.nifi.authorization.generated.Authorizers;
|
|||
import org.apache.nifi.authorization.generated.Property;
|
||||
import org.apache.nifi.bundle.Bundle;
|
||||
import org.apache.nifi.nar.ExtensionManager;
|
||||
import org.apache.nifi.nar.NarCloseable;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -49,12 +48,11 @@ import java.lang.reflect.Method;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Factory bean for loading the configured authorizer.
|
||||
*/
|
||||
public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, AuthorizerLookup {
|
||||
public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, UserGroupProviderLookup, AccessPolicyProviderLookup, AuthorizerLookup {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthorizerFactoryBean.class);
|
||||
private static final String AUTHORIZERS_XSD = "/authorizers.xsd";
|
||||
|
@ -74,8 +72,19 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, Autho
|
|||
|
||||
private Authorizer authorizer;
|
||||
private NiFiProperties properties;
|
||||
private final Map<String, UserGroupProvider> userGroupProviders = new HashMap<>();
|
||||
private final Map<String, AccessPolicyProvider> accessPolicyProviders = new HashMap<>();
|
||||
private final Map<String, Authorizer> authorizers = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public UserGroupProvider getUserGroupProvider(String identifier) {
|
||||
return userGroupProviders.get(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicyProvider getAccessPolicyProvider(String identifier) {
|
||||
return accessPolicyProviders.get(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authorizer getAuthorizer(String identifier) {
|
||||
|
@ -98,6 +107,28 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, Autho
|
|||
} else {
|
||||
final Authorizers authorizerConfiguration = loadAuthorizersConfiguration();
|
||||
|
||||
// create each user group provider
|
||||
for (final org.apache.nifi.authorization.generated.UserGroupProvider userGroupProvider : authorizerConfiguration.getUserGroupProvider()) {
|
||||
userGroupProviders.put(userGroupProvider.getIdentifier(), createUserGroupProvider(userGroupProvider.getIdentifier(), userGroupProvider.getClazz()));
|
||||
}
|
||||
|
||||
// configure each user group provider
|
||||
for (final org.apache.nifi.authorization.generated.UserGroupProvider provider : authorizerConfiguration.getUserGroupProvider()) {
|
||||
final UserGroupProvider instance = userGroupProviders.get(provider.getIdentifier());
|
||||
instance.onConfigured(loadAuthorizerConfiguration(provider.getIdentifier(), provider.getProperty()));
|
||||
}
|
||||
|
||||
// create each access policy provider
|
||||
for (final org.apache.nifi.authorization.generated.AccessPolicyProvider accessPolicyProvider : authorizerConfiguration.getAccessPolicyProvider()) {
|
||||
accessPolicyProviders.put(accessPolicyProvider.getIdentifier(), createAccessPolicyProvider(accessPolicyProvider.getIdentifier(), accessPolicyProvider.getClazz()));
|
||||
}
|
||||
|
||||
// configure each access policy provider
|
||||
for (final org.apache.nifi.authorization.generated.AccessPolicyProvider provider : authorizerConfiguration.getAccessPolicyProvider()) {
|
||||
final AccessPolicyProvider instance = accessPolicyProviders.get(provider.getIdentifier());
|
||||
instance.onConfigured(loadAuthorizerConfiguration(provider.getIdentifier(), provider.getProperty()));
|
||||
}
|
||||
|
||||
// create each authorizer
|
||||
for (final org.apache.nifi.authorization.generated.Authorizer authorizer : authorizerConfiguration.getAuthorizer()) {
|
||||
authorizers.put(authorizer.getIdentifier(), createAuthorizer(authorizer.getIdentifier(), authorizer.getClazz()));
|
||||
|
@ -106,7 +137,7 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, Autho
|
|||
// configure each authorizer
|
||||
for (final org.apache.nifi.authorization.generated.Authorizer provider : authorizerConfiguration.getAuthorizer()) {
|
||||
final Authorizer instance = authorizers.get(provider.getIdentifier());
|
||||
instance.onConfigured(loadAuthorizerConfiguration(provider));
|
||||
instance.onConfigured(loadAuthorizerConfiguration(provider.getIdentifier(), provider.getProperty()));
|
||||
}
|
||||
|
||||
// get the authorizer instance
|
||||
|
@ -146,6 +177,102 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, Autho
|
|||
}
|
||||
}
|
||||
|
||||
private UserGroupProvider createUserGroupProvider(final String identifier, final String userGroupProviderClassName) throws Exception {
|
||||
// get the classloader for the specified user group provider
|
||||
final List<Bundle> userGroupProviderBundles = ExtensionManager.getBundles(userGroupProviderClassName);
|
||||
|
||||
if (userGroupProviderBundles.size() == 0) {
|
||||
throw new Exception(String.format("The specified user group provider class '%s' is not known to this nifi.", userGroupProviderClassName));
|
||||
}
|
||||
|
||||
if (userGroupProviderBundles.size() > 1) {
|
||||
throw new Exception(String.format("Multiple bundles found for the specified user group provider class '%s', only one is allowed.", userGroupProviderClassName));
|
||||
}
|
||||
|
||||
final Bundle userGroupProviderBundle = userGroupProviderBundles.get(0);
|
||||
final ClassLoader userGroupProviderClassLoader = userGroupProviderBundle.getClassLoader();
|
||||
|
||||
// get the current context classloader
|
||||
final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
final UserGroupProvider instance;
|
||||
try {
|
||||
// set the appropriate class loader
|
||||
Thread.currentThread().setContextClassLoader(userGroupProviderClassLoader);
|
||||
|
||||
// attempt to load the class
|
||||
Class<?> rawUserGroupProviderClass = Class.forName(userGroupProviderClassName, true, userGroupProviderClassLoader);
|
||||
Class<? extends UserGroupProvider> userGroupProviderClass = rawUserGroupProviderClass.asSubclass(UserGroupProvider.class);
|
||||
|
||||
// otherwise create a new instance
|
||||
Constructor constructor = userGroupProviderClass.getConstructor();
|
||||
instance = (UserGroupProvider) constructor.newInstance();
|
||||
|
||||
// method injection
|
||||
performMethodInjection(instance, userGroupProviderClass);
|
||||
|
||||
// field injection
|
||||
performFieldInjection(instance, userGroupProviderClass);
|
||||
|
||||
// call post construction lifecycle event
|
||||
instance.initialize(new StandardAuthorizerInitializationContext(identifier, this, this, this));
|
||||
} finally {
|
||||
if (currentClassLoader != null) {
|
||||
Thread.currentThread().setContextClassLoader(currentClassLoader);
|
||||
}
|
||||
}
|
||||
|
||||
return UserGroupProviderFactory.withNarLoader(instance);
|
||||
}
|
||||
|
||||
private AccessPolicyProvider createAccessPolicyProvider(final String identifier, final String accessPolicyProviderClassName) throws Exception {
|
||||
// get the classloader for the specified access policy provider
|
||||
final List<Bundle> accessPolicyProviderBundles = ExtensionManager.getBundles(accessPolicyProviderClassName);
|
||||
|
||||
if (accessPolicyProviderBundles.size() == 0) {
|
||||
throw new Exception(String.format("The specified access policy provider class '%s' is not known to this nifi.", accessPolicyProviderClassName));
|
||||
}
|
||||
|
||||
if (accessPolicyProviderBundles.size() > 1) {
|
||||
throw new Exception(String.format("Multiple bundles found for the specified access policy provider class '%s', only one is allowed.", accessPolicyProviderClassName));
|
||||
}
|
||||
|
||||
final Bundle accessPolicyProviderBundle = accessPolicyProviderBundles.get(0);
|
||||
final ClassLoader accessPolicyProviderClassLoader = accessPolicyProviderBundle.getClassLoader();
|
||||
|
||||
// get the current context classloader
|
||||
final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
final AccessPolicyProvider instance;
|
||||
try {
|
||||
// set the appropriate class loader
|
||||
Thread.currentThread().setContextClassLoader(accessPolicyProviderClassLoader);
|
||||
|
||||
// attempt to load the class
|
||||
Class<?> rawAccessPolicyProviderClass = Class.forName(accessPolicyProviderClassName, true, accessPolicyProviderClassLoader);
|
||||
Class<? extends AccessPolicyProvider> accessPolicyClass = rawAccessPolicyProviderClass.asSubclass(AccessPolicyProvider.class);
|
||||
|
||||
// otherwise create a new instance
|
||||
Constructor constructor = accessPolicyClass.getConstructor();
|
||||
instance = (AccessPolicyProvider) constructor.newInstance();
|
||||
|
||||
// method injection
|
||||
performMethodInjection(instance, accessPolicyClass);
|
||||
|
||||
// field injection
|
||||
performFieldInjection(instance, accessPolicyClass);
|
||||
|
||||
// call post construction lifecycle event
|
||||
instance.initialize(new StandardAuthorizerInitializationContext(identifier, this, this, this));
|
||||
} finally {
|
||||
if (currentClassLoader != null) {
|
||||
Thread.currentThread().setContextClassLoader(currentClassLoader);
|
||||
}
|
||||
}
|
||||
|
||||
return AccessPolicyProviderFactory.withNarLoader(instance);
|
||||
}
|
||||
|
||||
private Authorizer createAuthorizer(final String identifier, final String authorizerClassName) throws Exception {
|
||||
// get the classloader for the specified authorizer
|
||||
final List<Bundle> authorizerBundles = ExtensionManager.getBundles(authorizerClassName);
|
||||
|
@ -184,26 +311,26 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, Autho
|
|||
performFieldInjection(instance, authorizerClass);
|
||||
|
||||
// call post construction lifecycle event
|
||||
instance.initialize(new StandardAuthorizerInitializationContext(identifier, this));
|
||||
instance.initialize(new StandardAuthorizerInitializationContext(identifier, this, this, this));
|
||||
} finally {
|
||||
if (currentClassLoader != null) {
|
||||
Thread.currentThread().setContextClassLoader(currentClassLoader);
|
||||
}
|
||||
}
|
||||
|
||||
return withNarLoader(instance);
|
||||
return AuthorizerFactory.installIntegrityChecks(AuthorizerFactory.withNarLoader(instance));
|
||||
}
|
||||
|
||||
private AuthorizerConfigurationContext loadAuthorizerConfiguration(final org.apache.nifi.authorization.generated.Authorizer authorizer) {
|
||||
private AuthorizerConfigurationContext loadAuthorizerConfiguration(final String identifier, final List<Property> properties) {
|
||||
final Map<String, String> authorizerProperties = new HashMap<>();
|
||||
|
||||
for (final Property property : authorizer.getProperty()) {
|
||||
for (final Property property : properties) {
|
||||
authorizerProperties.put(property.getName(), property.getValue());
|
||||
}
|
||||
return new StandardAuthorizerConfigurationContext(authorizer.getIdentifier(), authorizerProperties);
|
||||
return new StandardAuthorizerConfigurationContext(identifier, authorizerProperties);
|
||||
}
|
||||
|
||||
private void performMethodInjection(final Authorizer instance, final Class authorizerClass) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
private void performMethodInjection(final Object instance, final Class authorizerClass) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
for (final Method method : authorizerClass.getMethods()) {
|
||||
if (method.isAnnotationPresent(AuthorizerContext.class)) {
|
||||
// make the method accessible
|
||||
|
@ -235,7 +362,7 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, Autho
|
|||
}
|
||||
}
|
||||
|
||||
private void performFieldInjection(final Authorizer instance, final Class authorizerClass) throws IllegalArgumentException, IllegalAccessException {
|
||||
private void performFieldInjection(final Object instance, final Class authorizerClass) throws IllegalArgumentException, IllegalAccessException {
|
||||
for (final Field field : authorizerClass.getDeclaredFields()) {
|
||||
if (field.isAnnotationPresent(AuthorizerContext.class)) {
|
||||
// make the method accessible
|
||||
|
@ -291,189 +418,6 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, Autho
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorates the base authorizer to ensure the nar context classloader is used when invoking the underlying methods.
|
||||
*
|
||||
* @param baseAuthorizer base authorizer
|
||||
* @return authorizer
|
||||
*/
|
||||
public Authorizer withNarLoader(final Authorizer baseAuthorizer) {
|
||||
if (baseAuthorizer instanceof AbstractPolicyBasedAuthorizer) {
|
||||
AbstractPolicyBasedAuthorizer policyBasedAuthorizer = (AbstractPolicyBasedAuthorizer) baseAuthorizer;
|
||||
return new AbstractPolicyBasedAuthorizer() {
|
||||
@Override
|
||||
public Group doAddGroup(Group group) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.addGroup(group);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getGroup(String identifier) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.getGroup(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group doUpdateGroup(Group group) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.updateGroup(group);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group deleteGroup(Group group) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.deleteGroup(group);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.getGroups();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User doAddUser(User user) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.addUser(user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(String identifier) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.getUser(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByIdentity(String identity) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.getUserByIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User doUpdateUser(User user) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.updateUser(user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User deleteUser(User user) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.deleteUser(user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<User> getUsers() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.getUsers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy doAddAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.addAccessPolicy(accessPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.getAccessPolicy(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.updateAccessPolicy(accessPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy deleteAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.deleteAccessPolicy(accessPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.getAccessPolicies();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsersAndAccessPolicies getUsersAndAccessPolicies() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return policyBasedAuthorizer.getUsersAndAccessPolicies();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
policyBasedAuthorizer.initialize(initializationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doOnConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
policyBasedAuthorizer.onConfigured(configurationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseAuthorizer.preDestruction();
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return new Authorizer() {
|
||||
@Override
|
||||
public AuthorizationResult authorize(final AuthorizationRequest request) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseAuthorizer.authorize(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseAuthorizer.initialize(initializationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseAuthorizer.onConfigured(configurationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseAuthorizer.preDestruction();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class getObjectType() {
|
||||
return Authorizer.class;
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* 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.exception.UninheritableAuthorizationsException;
|
||||
import org.apache.nifi.nar.NarCloseable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public final class UserGroupProviderFactory {
|
||||
|
||||
public static UserGroupProvider withNarLoader(final UserGroupProvider baseUserGroupProvider) {
|
||||
if (baseUserGroupProvider instanceof ConfigurableUserGroupProvider) {
|
||||
final ConfigurableUserGroupProvider baseConfigurableUserGroupProvider = (ConfigurableUserGroupProvider) baseUserGroupProvider;
|
||||
return new ConfigurableUserGroupProvider() {
|
||||
@Override
|
||||
public User addUser(User user) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.addUser(user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User updateUser(User user) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.updateUser(user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User deleteUser(User user) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.deleteUser(user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group addGroup(Group group) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.addGroup(group);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group updateGroup(Group group) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.updateGroup(group);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group deleteGroup(Group group) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.deleteGroup(group);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<User> getUsers() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.getUsers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(String identifier) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.getUser(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByIdentity(String identity) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.getUserByIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.getGroups();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getGroup(String identifier) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.getGroup(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.getUserAndGroups(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseConfigurableUserGroupProvider.inheritFingerprint(fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseConfigurableUserGroupProvider.checkInheritability(proposedFingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFingerprint() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseConfigurableUserGroupProvider.getFingerprint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseConfigurableUserGroupProvider.initialize(initializationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseConfigurableUserGroupProvider.onConfigured(configurationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseConfigurableUserGroupProvider.preDestruction();
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return new UserGroupProvider() {
|
||||
@Override
|
||||
public Set<User> getUsers() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseUserGroupProvider.getUsers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(String identifier) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseUserGroupProvider.getUser(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByIdentity(String identity) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseUserGroupProvider.getUserByIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseUserGroupProvider.getGroups();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getGroup(String identifier) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseUserGroupProvider.getGroup(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
return baseUserGroupProvider.getUserAndGroups(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseUserGroupProvider.initialize(initializationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseUserGroupProvider.onConfigured(configurationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
|
||||
baseUserGroupProvider.preDestruction();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private UserGroupProviderFactory() {}
|
||||
}
|
|
@ -14,7 +14,25 @@
|
|||
limitations under the License.
|
||||
-->
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<!-- role -->
|
||||
<!-- user group providers type -->
|
||||
<xs:complexType name="UserGroupProvider">
|
||||
<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>
|
||||
|
||||
<!-- access policy provider type -->
|
||||
<xs:complexType name="AccessPolicyProvider">
|
||||
<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>
|
||||
|
||||
<!-- authorizers type -->
|
||||
<xs:complexType name="Authorizer">
|
||||
<xs:sequence>
|
||||
<xs:element name="identifier" type="NonEmptyStringType"/>
|
||||
|
@ -38,10 +56,12 @@
|
|||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<!-- users -->
|
||||
<!-- authorizers -->
|
||||
<xs:element name="authorizers">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="userGroupProvider" type="UserGroupProvider" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="accessPolicyProvider" type="AccessPolicyProvider" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="authorizer" type="Authorizer" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* 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.AuthorizerCreationException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class AuthorizerFactoryTest {
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testOnConfiguredWhenPoliciesWithSameResourceAndAction() {
|
||||
User user1 = new User.Builder().identifier("user-id-1").identity("user-1").build();
|
||||
|
||||
AccessPolicy policy1 = new AccessPolicy.Builder()
|
||||
.identifier("policy-id-1")
|
||||
.resource("resource1")
|
||||
.action(RequestAction.READ)
|
||||
.addUser(user1.getIdentifier())
|
||||
.build();
|
||||
|
||||
AccessPolicy policy2 = new AccessPolicy.Builder()
|
||||
.identifier("policy-id-2")
|
||||
.resource("resource1")
|
||||
.action(RequestAction.READ)
|
||||
.addUser(user1.getIdentifier())
|
||||
.build();
|
||||
|
||||
Set<AccessPolicy> policies = new LinkedHashSet<>();
|
||||
policies.add(policy1);
|
||||
policies.add(policy2);
|
||||
|
||||
Set<User> users = new LinkedHashSet<>();
|
||||
users.add(user1);
|
||||
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
Authorizer authorizer = AuthorizerFactory.installIntegrityChecks(new MockPolicyBasedAuthorizer(new HashSet<>(), users, policies));
|
||||
authorizer.onConfigured(context);
|
||||
}
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testOnConfiguredWhenUsersWithSameIdentity() {
|
||||
User user1 = new User.Builder().identifier("user-id-1").identity("user-1").build();
|
||||
User user2 = new User.Builder().identifier("user-id-2").identity("user-1").build();
|
||||
|
||||
Set<User> users = new LinkedHashSet<>();
|
||||
users.add(user1);
|
||||
users.add(user2);
|
||||
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
Authorizer authorizer = AuthorizerFactory.installIntegrityChecks(new MockPolicyBasedAuthorizer(new HashSet<>(), users, new HashSet<>()));
|
||||
authorizer.onConfigured(context);
|
||||
}
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testOnConfiguredWhenGroupsWithSameName() {
|
||||
Group group1 = new Group.Builder().identifier("group-id-1").name("group-1").build();
|
||||
Group group2 = new Group.Builder().identifier("group-id-2").name("group-1").build();
|
||||
|
||||
Set<Group> groups = new LinkedHashSet<>();
|
||||
groups.add(group1);
|
||||
groups.add(group2);
|
||||
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
Authorizer authorizer = AuthorizerFactory.installIntegrityChecks(new MockPolicyBasedAuthorizer(groups, new HashSet<>(), new HashSet<>()));
|
||||
authorizer.onConfigured(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddPoliciesWithSameResourceAndAction() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
|
||||
final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) AuthorizerFactory.installIntegrityChecks(new MockPolicyBasedAuthorizer());
|
||||
managedAuthorizer.onConfigured(context);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = (ConfigurableAccessPolicyProvider) managedAuthorizer.getAccessPolicyProvider();
|
||||
final ConfigurableUserGroupProvider userGroupProvider = (ConfigurableUserGroupProvider) accessPolicyProvider.getUserGroupProvider();
|
||||
|
||||
User user1 = new User.Builder().identifier("user-id-1").identity("user-1").build();
|
||||
userGroupProvider.addUser(user1);
|
||||
|
||||
AccessPolicy policy1 = new AccessPolicy.Builder()
|
||||
.identifier("policy-id-1")
|
||||
.resource("resource1")
|
||||
.action(RequestAction.READ)
|
||||
.addUser(user1.getIdentifier())
|
||||
.build();
|
||||
accessPolicyProvider.addAccessPolicy(policy1);
|
||||
|
||||
AccessPolicy policy2 = new AccessPolicy.Builder()
|
||||
.identifier("policy-id-2")
|
||||
.resource("resource1")
|
||||
.action(RequestAction.READ)
|
||||
.addUser(user1.getIdentifier())
|
||||
.build();
|
||||
|
||||
try {
|
||||
accessPolicyProvider.addAccessPolicy(policy2);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddUsersWithSameIdentity() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
|
||||
final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) AuthorizerFactory.installIntegrityChecks(new MockPolicyBasedAuthorizer());
|
||||
managedAuthorizer.onConfigured(context);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = (ConfigurableAccessPolicyProvider) managedAuthorizer.getAccessPolicyProvider();
|
||||
final ConfigurableUserGroupProvider userGroupProvider = (ConfigurableUserGroupProvider) accessPolicyProvider.getUserGroupProvider();
|
||||
|
||||
User user1 = new User.Builder().identifier("user-id-1").identity("user-1").build();
|
||||
userGroupProvider.addUser(user1);
|
||||
|
||||
User user2 = new User.Builder().identifier("user-id-2").identity("user-1").build();
|
||||
|
||||
try {
|
||||
userGroupProvider.addUser(user2);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddGroupsWithSameName() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
|
||||
final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) AuthorizerFactory.installIntegrityChecks(new MockPolicyBasedAuthorizer());
|
||||
managedAuthorizer.onConfigured(context);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = (ConfigurableAccessPolicyProvider) managedAuthorizer.getAccessPolicyProvider();
|
||||
final ConfigurableUserGroupProvider userGroupProvider = (ConfigurableUserGroupProvider) accessPolicyProvider.getUserGroupProvider();
|
||||
|
||||
Group group1 = new Group.Builder().identifier("group-id-1").name("group-1").build();
|
||||
userGroupProvider.addGroup(group1);
|
||||
|
||||
Group group2 = new Group.Builder().identifier("group-id-2").name("group-1").build();
|
||||
|
||||
try {
|
||||
userGroupProvider.addGroup(group2);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddUsersWithSameIdentityAsGroupName() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
|
||||
final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) AuthorizerFactory.installIntegrityChecks(new MockPolicyBasedAuthorizer());
|
||||
managedAuthorizer.onConfigured(context);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = (ConfigurableAccessPolicyProvider) managedAuthorizer.getAccessPolicyProvider();
|
||||
final ConfigurableUserGroupProvider userGroupProvider = (ConfigurableUserGroupProvider) accessPolicyProvider.getUserGroupProvider();
|
||||
|
||||
Group group1 = new Group.Builder().identifier("group-id-1").name("abc").build();
|
||||
userGroupProvider.addGroup(group1);
|
||||
|
||||
User user = new User.Builder().identifier("user-id-2").identity("abc").build();
|
||||
|
||||
try {
|
||||
userGroupProvider.addUser(user);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddGroupWithSameNameAsUserIdentity() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
|
||||
final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) AuthorizerFactory.installIntegrityChecks(new MockPolicyBasedAuthorizer());
|
||||
managedAuthorizer.onConfigured(context);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = (ConfigurableAccessPolicyProvider) managedAuthorizer.getAccessPolicyProvider();
|
||||
final ConfigurableUserGroupProvider userGroupProvider = (ConfigurableUserGroupProvider) accessPolicyProvider.getUserGroupProvider();
|
||||
|
||||
User user = new User.Builder().identifier("user-id-2").identity("abc").build();
|
||||
userGroupProvider.addUser(user);
|
||||
|
||||
Group group1 = new Group.Builder().identifier("group-id-1").name("abc").build();
|
||||
try {
|
||||
userGroupProvider.addGroup(group1);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateUserWithSameIdentity() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
|
||||
final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) AuthorizerFactory.installIntegrityChecks(new MockPolicyBasedAuthorizer());
|
||||
managedAuthorizer.onConfigured(context);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = (ConfigurableAccessPolicyProvider) managedAuthorizer.getAccessPolicyProvider();
|
||||
final ConfigurableUserGroupProvider userGroupProvider = (ConfigurableUserGroupProvider) accessPolicyProvider.getUserGroupProvider();
|
||||
|
||||
User user1 = new User.Builder().identifier("user-id-1").identity("abc").build();
|
||||
userGroupProvider.addUser(user1);
|
||||
|
||||
User user2 = new User.Builder().identifier("user-id-2").identity("xyz").build();
|
||||
userGroupProvider.addUser(user2);
|
||||
|
||||
try {
|
||||
User user1Updated = new User.Builder().identifier("user-id-1").identity("xyz").build();
|
||||
userGroupProvider.updateUser(user1Updated);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateGroupWithSameName() {
|
||||
AuthorizerConfigurationContext context = Mockito.mock(AuthorizerConfigurationContext.class);
|
||||
|
||||
final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) AuthorizerFactory.installIntegrityChecks(new MockPolicyBasedAuthorizer());
|
||||
managedAuthorizer.onConfigured(context);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = (ConfigurableAccessPolicyProvider) managedAuthorizer.getAccessPolicyProvider();
|
||||
final ConfigurableUserGroupProvider userGroupProvider = (ConfigurableUserGroupProvider) accessPolicyProvider.getUserGroupProvider();
|
||||
|
||||
Group group1 = new Group.Builder().identifier("group-id-1").name("abc").build();
|
||||
userGroupProvider.addGroup(group1);
|
||||
|
||||
Group group2 = new Group.Builder().identifier("group-id-2").name("xyz").build();
|
||||
userGroupProvider.addGroup(group2);
|
||||
|
||||
try {
|
||||
Group group1Updated = new Group.Builder().identifier("group-id-1").name("xyz").build();
|
||||
userGroupProvider.updateGroup(group1Updated);
|
||||
Assert.fail("Should have thrown exception");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* 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 java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Mock implementation of AbstractPolicyBasedAuthorizer.
|
||||
*/
|
||||
public class MockPolicyBasedAuthorizer extends AbstractPolicyBasedAuthorizer {
|
||||
|
||||
private Set<Group> groups = new HashSet<>();
|
||||
private Set<User> users = new HashSet<>();
|
||||
private Set<AccessPolicy> policies = new HashSet<>();
|
||||
|
||||
public MockPolicyBasedAuthorizer() {
|
||||
|
||||
}
|
||||
|
||||
public MockPolicyBasedAuthorizer(Set<Group> groups, Set<User> users, Set<AccessPolicy> policies) {
|
||||
if (groups != null) {
|
||||
this.groups.addAll(groups);
|
||||
}
|
||||
if (users != null) {
|
||||
this.users.addAll(users);
|
||||
}
|
||||
if (policies != null) {
|
||||
this.policies.addAll(policies);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group doAddGroup(Group group) throws AuthorizationAccessException {
|
||||
groups.add(group);
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getGroup(String identifier) throws AuthorizationAccessException {
|
||||
return groups.stream().filter(g -> g.getIdentifier().equals(identifier)).findFirst().get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group doUpdateGroup(Group group) throws AuthorizationAccessException {
|
||||
deleteGroup(group);
|
||||
return addGroup(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group deleteGroup(Group group) throws AuthorizationAccessException {
|
||||
groups.remove(group);
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() throws AuthorizationAccessException {
|
||||
return groups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User doAddUser(User user) throws AuthorizationAccessException {
|
||||
users.add(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(String identifier) throws AuthorizationAccessException {
|
||||
return users.stream().filter(u -> u.getIdentifier().equals(identifier)).findFirst().get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByIdentity(String identity) throws AuthorizationAccessException {
|
||||
return users.stream().filter(u -> u.getIdentity().equals(identity)).findFirst().get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public User doUpdateUser(User user) throws AuthorizationAccessException {
|
||||
deleteUser(user);
|
||||
return addUser(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User deleteUser(User user) throws AuthorizationAccessException {
|
||||
users.remove(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<User> getUsers() throws AuthorizationAccessException {
|
||||
return users;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AccessPolicy doAddAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
policies.add(accessPolicy);
|
||||
return accessPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException {
|
||||
return policies.stream().filter(p -> p.getIdentifier().equals(identifier)).findFirst().get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
deleteAccessPolicy(accessPolicy);
|
||||
return addAccessPolicy(accessPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy deleteAccessPolicy(AccessPolicy policy) throws AuthorizationAccessException {
|
||||
policies.remove(policy);
|
||||
return policy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException {
|
||||
return policies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsersAndAccessPolicies getUsersAndAccessPolicies() throws AuthorizationAccessException {
|
||||
return new UsersAndAccessPolicies() {
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(String identity) {
|
||||
return getUserByIdentity(identity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups(String userIdentity) {
|
||||
User user = getUserByIdentity(userIdentity);
|
||||
if (user == null) {
|
||||
return new HashSet<>();
|
||||
} else {
|
||||
return groups.stream()
|
||||
.filter(g -> g.getUsers().contains(user.getIdentifier()))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doOnConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -29,7 +29,9 @@ import java.util.Date;
|
|||
@XmlType(name = "flowConfiguration")
|
||||
public class FlowConfigurationDTO {
|
||||
|
||||
private Boolean supportsManagedAuthorizer;
|
||||
private Boolean supportsConfigurableAuthorizer;
|
||||
private Boolean supportsConfigurableUsersAndGroups;
|
||||
private Long autoRefreshIntervalSeconds;
|
||||
|
||||
private Date currentTime;
|
||||
|
@ -50,6 +52,37 @@ public class FlowConfigurationDTO {
|
|||
this.autoRefreshIntervalSeconds = autoRefreshIntervalSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this NiFi supports a managed authorizer. Managed authorizers can visualize users, groups,
|
||||
* and policies in the UI. This value is read only
|
||||
*/
|
||||
@ApiModelProperty(
|
||||
value = "Whether this NiFi supports a managed authorizer. Managed authorizers can visualize users, groups, and policies in the UI.",
|
||||
readOnly = true
|
||||
)
|
||||
public Boolean getSupportsManagedAuthorizer() {
|
||||
return supportsManagedAuthorizer;
|
||||
}
|
||||
|
||||
public void setSupportsManagedAuthorizer(Boolean supportsManagedAuthorizer) {
|
||||
this.supportsManagedAuthorizer = supportsManagedAuthorizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this NiFi supports configurable users and groups. This value is read only
|
||||
*/
|
||||
@ApiModelProperty(
|
||||
value = "Whether this NiFi supports configurable users and groups.",
|
||||
readOnly = true
|
||||
)
|
||||
public Boolean getSupportsConfigurableUsersAndGroups() {
|
||||
return supportsConfigurableUsersAndGroups;
|
||||
}
|
||||
|
||||
public void setSupportsConfigurableUsersAndGroups(Boolean supportsConfigurableUsersAndGroups) {
|
||||
this.supportsConfigurableUsersAndGroups = supportsConfigurableUsersAndGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this NiFi supports a configurable authorizer. This value is read only
|
||||
*/
|
||||
|
|
|
@ -28,7 +28,7 @@ import java.util.Date;
|
|||
* A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to an {@link AccessPolicyDTO}.
|
||||
*/
|
||||
@XmlRootElement(name = "accessPolicyEntity")
|
||||
public class AccessPolicyEntity extends ComponentEntity {
|
||||
public class AccessPolicyEntity extends ComponentEntity implements Permissible<AccessPolicyDTO> {
|
||||
|
||||
private Date generated;
|
||||
private AccessPolicyDTO component;
|
||||
|
|
|
@ -24,7 +24,7 @@ import java.util.Collection;
|
|||
* TenantEntity objects.
|
||||
*/
|
||||
@XmlRootElement(name = "tenantsEntity")
|
||||
public class TenantsEntity {
|
||||
public class TenantsEntity extends Entity {
|
||||
|
||||
private Collection<TenantEntity> users;
|
||||
private Collection<TenantEntity> userGroups;
|
||||
|
|
|
@ -24,7 +24,7 @@ import javax.xml.bind.annotation.XmlRootElement;
|
|||
* A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a UserDTO.
|
||||
*/
|
||||
@XmlRootElement(name = "userEntity")
|
||||
public class UserEntity extends ComponentEntity {
|
||||
public class UserEntity extends ComponentEntity implements Permissible<UserDTO> {
|
||||
|
||||
private UserDTO component;
|
||||
|
||||
|
|
|
@ -16,14 +16,15 @@
|
|||
*/
|
||||
package org.apache.nifi.web.api.entity;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import org.apache.nifi.web.api.dto.UserGroupDTO;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a UserGroupDTO.
|
||||
*/
|
||||
@XmlRootElement(name = "userGroupEntity")
|
||||
public class UserGroupEntity extends ComponentEntity {
|
||||
public class UserGroupEntity extends ComponentEntity implements Permissible<UserGroupDTO> {
|
||||
|
||||
private UserGroupDTO component;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import java.util.Collection;
|
|||
* UserGroupEntity objects.
|
||||
*/
|
||||
@XmlRootElement(name = "userGroupsEntity")
|
||||
public class UserGroupsEntity {
|
||||
public class UserGroupsEntity extends Entity {
|
||||
|
||||
private Collection<UserGroupEntity> userGroups;
|
||||
|
||||
|
|
|
@ -41,8 +41,9 @@
|
|||
<goal>xjc</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<schemaDirectory>src/main/xsd</schemaDirectory>
|
||||
<schemaFiles>authorizations.xsd</schemaFiles>
|
||||
<sources>
|
||||
<source>src/main/xsd/authorizations.xsd</source>
|
||||
</sources>
|
||||
<packageName>org.apache.nifi.authorization.file.generated</packageName>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
@ -52,8 +53,9 @@
|
|||
<goal>xjc</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<schemaDirectory>src/main/xsd</schemaDirectory>
|
||||
<schemaFiles>tenants.xsd</schemaFiles>
|
||||
<sources>
|
||||
<source>src/main/xsd/tenants.xsd</source>
|
||||
</sources>
|
||||
<packageName>org.apache.nifi.authorization.file.tenants.generated</packageName>
|
||||
<clearOutputDir>false</clearOutputDir>
|
||||
</configuration>
|
||||
|
@ -64,8 +66,9 @@
|
|||
<goal>xjc</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<schemaDirectory>src/main/xsd</schemaDirectory>
|
||||
<schemaFiles>legacy-users.xsd</schemaFiles>
|
||||
<sources>
|
||||
<source>src/main/xsd/legacy-users.xsd</source>
|
||||
</sources>
|
||||
<packageName>org.apache.nifi.user.generated</packageName>
|
||||
<clearOutputDir>false</clearOutputDir>
|
||||
</configuration>
|
||||
|
|
|
@ -19,9 +19,6 @@ package org.apache.nifi.authorization;
|
|||
|
||||
import org.apache.nifi.authorization.file.generated.Authorizations;
|
||||
import org.apache.nifi.authorization.file.generated.Policies;
|
||||
import org.apache.nifi.authorization.file.tenants.generated.Groups;
|
||||
import org.apache.nifi.authorization.file.tenants.generated.Tenants;
|
||||
import org.apache.nifi.authorization.file.tenants.generated.Users;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -32,56 +29,26 @@ import java.util.Set;
|
|||
/**
|
||||
* A holder to provide atomic access to data structures.
|
||||
*/
|
||||
public class AuthorizationsHolder implements UsersAndAccessPolicies {
|
||||
public class AuthorizationsHolder {
|
||||
|
||||
private final Tenants tenants;
|
||||
private final Authorizations authorizations;
|
||||
|
||||
private final Set<AccessPolicy> allPolicies;
|
||||
private final Map<String, Set<AccessPolicy>> policiesByResource;
|
||||
private final Map<String, AccessPolicy> policiesById;
|
||||
|
||||
private final Set<User> allUsers;
|
||||
private final Map<String,User> usersById;
|
||||
private final Map<String,User> usersByIdentity;
|
||||
|
||||
private final Set<Group> allGroups;
|
||||
private final Map<String,Group> groupsById;
|
||||
private final Map<String, Set<Group>> groupsByUserIdentity;
|
||||
|
||||
/**
|
||||
* Creates a new holder and populates all convenience data structures.
|
||||
* Creates a new holder and populates all convenience authorizations data structures.
|
||||
*
|
||||
* @param authorizations the current authorizations instance
|
||||
*/
|
||||
public AuthorizationsHolder(final Authorizations authorizations, final Tenants tenants) {
|
||||
public AuthorizationsHolder(final Authorizations authorizations) {
|
||||
this.authorizations = authorizations;
|
||||
this.tenants = tenants;
|
||||
|
||||
// load all users
|
||||
final Users users = tenants.getUsers();
|
||||
final Set<User> allUsers = Collections.unmodifiableSet(createUsers(users));
|
||||
|
||||
// load all groups
|
||||
final Groups groups = tenants.getGroups();
|
||||
final Set<Group> allGroups = Collections.unmodifiableSet(createGroups(groups, users));
|
||||
|
||||
// load all access policies
|
||||
final Policies policies = authorizations.getPolicies();
|
||||
final Set<AccessPolicy> allPolicies = Collections.unmodifiableSet(createAccessPolicies(policies));
|
||||
|
||||
// create a convenience map to retrieve a user by id
|
||||
final Map<String, User> userByIdMap = Collections.unmodifiableMap(createUserByIdMap(allUsers));
|
||||
|
||||
// create a convenience map to retrieve a user by identity
|
||||
final Map<String, User> userByIdentityMap = Collections.unmodifiableMap(createUserByIdentityMap(allUsers));
|
||||
|
||||
// create a convenience map to retrieve a group by id
|
||||
final Map<String, Group> groupByIdMap = Collections.unmodifiableMap(createGroupByIdMap(allGroups));
|
||||
|
||||
// create a convenience map to retrieve the groups for a user identity
|
||||
final Map<String, Set<Group>> groupsByUserIdentityMap = Collections.unmodifiableMap(createGroupsByUserIdentityMap(allGroups, allUsers));
|
||||
|
||||
// create a convenience map from resource id to policies
|
||||
final Map<String, Set<AccessPolicy>> policiesByResourceMap = Collections.unmodifiableMap(createResourcePolicyMap(allPolicies));
|
||||
|
||||
|
@ -89,13 +56,7 @@ public class AuthorizationsHolder implements UsersAndAccessPolicies {
|
|||
final Map<String, AccessPolicy> policiesByIdMap = Collections.unmodifiableMap(createPoliciesByIdMap(allPolicies));
|
||||
|
||||
// set all the holders
|
||||
this.allUsers = allUsers;
|
||||
this.allGroups = allGroups;
|
||||
this.allPolicies = allPolicies;
|
||||
this.usersById = userByIdMap;
|
||||
this.usersByIdentity = userByIdentityMap;
|
||||
this.groupsById = groupByIdMap;
|
||||
this.groupsByUserIdentity = groupsByUserIdentityMap;
|
||||
this.policiesByResource = policiesByResourceMap;
|
||||
this.policiesById = policiesByIdMap;
|
||||
}
|
||||
|
@ -134,9 +95,9 @@ public class AuthorizationsHolder implements UsersAndAccessPolicies {
|
|||
|
||||
// add the appropriate request actions
|
||||
final String authorizationCode = policy.getAction();
|
||||
if (authorizationCode.equals(FileAuthorizer.READ_CODE)) {
|
||||
if (authorizationCode.equals(FileAccessPolicyProvider.READ_CODE)) {
|
||||
builder.action(RequestAction.READ);
|
||||
} else if (authorizationCode.equals(FileAuthorizer.WRITE_CODE)){
|
||||
} else if (authorizationCode.equals(FileAccessPolicyProvider.WRITE_CODE)){
|
||||
builder.action(RequestAction.WRITE);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown Policy Action: " + authorizationCode);
|
||||
|
@ -149,57 +110,6 @@ public class AuthorizationsHolder implements UsersAndAccessPolicies {
|
|||
return allPolicies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a set of Users from the JAXB Users.
|
||||
*
|
||||
* @param users the JAXB Users
|
||||
* @return a set of API Users matching the provided JAXB Users
|
||||
*/
|
||||
private Set<User> createUsers(org.apache.nifi.authorization.file.tenants.generated.Users users) {
|
||||
Set<User> allUsers = new HashSet<>();
|
||||
if (users == null || users.getUser() == null) {
|
||||
return allUsers;
|
||||
}
|
||||
|
||||
for (org.apache.nifi.authorization.file.tenants.generated.User user : users.getUser()) {
|
||||
final User.Builder builder = new User.Builder()
|
||||
.identity(user.getIdentity())
|
||||
.identifier(user.getIdentifier());
|
||||
|
||||
allUsers.add(builder.build());
|
||||
}
|
||||
|
||||
return allUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a set of Groups from the JAXB Groups.
|
||||
*
|
||||
* @param groups the JAXB Groups
|
||||
* @return a set of API Groups matching the provided JAXB Groups
|
||||
*/
|
||||
private Set<Group> createGroups(org.apache.nifi.authorization.file.tenants.generated.Groups groups,
|
||||
org.apache.nifi.authorization.file.tenants.generated.Users users) {
|
||||
Set<Group> allGroups = new HashSet<>();
|
||||
if (groups == null || groups.getGroup() == null) {
|
||||
return allGroups;
|
||||
}
|
||||
|
||||
for (org.apache.nifi.authorization.file.tenants.generated.Group group : groups.getGroup()) {
|
||||
final Group.Builder builder = new Group.Builder()
|
||||
.identifier(group.getIdentifier())
|
||||
.name(group.getName());
|
||||
|
||||
for (org.apache.nifi.authorization.file.tenants.generated.Group.User groupUser : group.getUser()) {
|
||||
builder.addUser(groupUser.getIdentifier());
|
||||
}
|
||||
|
||||
allGroups.add(builder.build());
|
||||
}
|
||||
|
||||
return allGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a map from resource identifier to the set of policies for the given resource.
|
||||
*
|
||||
|
@ -221,74 +131,6 @@ public class AuthorizationsHolder implements UsersAndAccessPolicies {
|
|||
return resourcePolicies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Map from user identifier to User.
|
||||
*
|
||||
* @param users the set of all users
|
||||
* @return the Map from user identifier to User
|
||||
*/
|
||||
private Map<String,User> createUserByIdMap(final Set<User> users) {
|
||||
Map<String,User> usersMap = new HashMap<>();
|
||||
for (User user : users) {
|
||||
usersMap.put(user.getIdentifier(), user);
|
||||
}
|
||||
return usersMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Map from user identity to User.
|
||||
*
|
||||
* @param users the set of all users
|
||||
* @return the Map from user identity to User
|
||||
*/
|
||||
private Map<String,User> createUserByIdentityMap(final Set<User> users) {
|
||||
Map<String,User> usersMap = new HashMap<>();
|
||||
for (User user : users) {
|
||||
usersMap.put(user.getIdentity(), user);
|
||||
}
|
||||
return usersMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Map from group identifier to Group.
|
||||
*
|
||||
* @param groups the set of all groups
|
||||
* @return the Map from group identifier to Group
|
||||
*/
|
||||
private Map<String,Group> createGroupByIdMap(final Set<Group> groups) {
|
||||
Map<String,Group> groupsMap = new HashMap<>();
|
||||
for (Group group : groups) {
|
||||
groupsMap.put(group.getIdentifier(), group);
|
||||
}
|
||||
return groupsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Map from user identity to the set of Groups for that identity.
|
||||
*
|
||||
* @param groups all groups
|
||||
* @param users all users
|
||||
* @return a Map from User identity to the set of Groups for that identity
|
||||
*/
|
||||
private Map<String, Set<Group>> createGroupsByUserIdentityMap(final Set<Group> groups, final Set<User> users) {
|
||||
Map<String, Set<Group>> groupsByUserIdentity = new HashMap<>();
|
||||
|
||||
for (User user : users) {
|
||||
Set<Group> userGroups = new HashSet<>();
|
||||
for (Group group : groups) {
|
||||
for (String groupUser : group.getUsers()) {
|
||||
if (groupUser.equals(user.getIdentifier())) {
|
||||
userGroups.add(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
groupsByUserIdentity.put(user.getIdentity(), userGroups);
|
||||
}
|
||||
|
||||
return groupsByUserIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Map from policy identifier to AccessPolicy.
|
||||
*
|
||||
|
@ -307,10 +149,6 @@ public class AuthorizationsHolder implements UsersAndAccessPolicies {
|
|||
return authorizations;
|
||||
}
|
||||
|
||||
public Tenants getTenants() {
|
||||
return tenants;
|
||||
}
|
||||
|
||||
public Set<AccessPolicy> getAllPolicies() {
|
||||
return allPolicies;
|
||||
}
|
||||
|
@ -323,27 +161,6 @@ public class AuthorizationsHolder implements UsersAndAccessPolicies {
|
|||
return policiesById;
|
||||
}
|
||||
|
||||
public Set<User> getAllUsers() {
|
||||
return allUsers;
|
||||
}
|
||||
|
||||
public Map<String, User> getUsersById() {
|
||||
return usersById;
|
||||
}
|
||||
|
||||
public Map<String, User> getUsersByIdentity() {
|
||||
return usersByIdentity;
|
||||
}
|
||||
|
||||
public Set<Group> getAllGroups() {
|
||||
return allGroups;
|
||||
}
|
||||
|
||||
public Map<String, Group> getGroupsById() {
|
||||
return groupsById;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(final String resourceIdentifier, final RequestAction action) {
|
||||
if (resourceIdentifier == null) {
|
||||
throw new IllegalArgumentException("Resource Identifier cannot be null");
|
||||
|
@ -363,20 +180,4 @@ public class AuthorizationsHolder implements UsersAndAccessPolicies {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(String identity) {
|
||||
if (identity == null) {
|
||||
throw new IllegalArgumentException("Identity cannot be null");
|
||||
}
|
||||
return usersByIdentity.get(identity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups(String userIdentity) {
|
||||
if (userIdentity == null) {
|
||||
throw new IllegalArgumentException("User Identity cannot be null");
|
||||
}
|
||||
return groupsByUserIdentity.get(userIdentity);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,947 @@
|
|||
/*
|
||||
* 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.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authorization.annotation.AuthorizerContext;
|
||||
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.exception.UninheritableAuthorizationsException;
|
||||
import org.apache.nifi.authorization.file.generated.Authorizations;
|
||||
import org.apache.nifi.authorization.file.generated.Policies;
|
||||
import org.apache.nifi.authorization.file.generated.Policy;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.resource.ResourceType;
|
||||
import org.apache.nifi.authorization.util.IdentityMapping;
|
||||
import org.apache.nifi.authorization.util.IdentityMappingUtil;
|
||||
import org.apache.nifi.components.PropertyValue;
|
||||
import org.apache.nifi.user.generated.Users;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.util.file.FileUtils;
|
||||
import org.apache.nifi.web.api.dto.PortDTO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBElement;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FileAccessPolicyProvider implements ConfigurableAccessPolicyProvider {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileAccessPolicyProvider.class);
|
||||
|
||||
private static final String AUTHORIZATIONS_XSD = "/authorizations.xsd";
|
||||
private static final String JAXB_AUTHORIZATIONS_PATH = "org.apache.nifi.authorization.file.generated";
|
||||
|
||||
private static final String USERS_XSD = "/legacy-users.xsd";
|
||||
private static final String JAXB_USERS_PATH = "org.apache.nifi.user.generated";
|
||||
|
||||
private static final JAXBContext JAXB_AUTHORIZATIONS_CONTEXT = initializeJaxbContext(JAXB_AUTHORIZATIONS_PATH);
|
||||
private static final JAXBContext JAXB_USERS_CONTEXT = initializeJaxbContext(JAXB_USERS_PATH);
|
||||
|
||||
/**
|
||||
* Load the JAXBContext.
|
||||
*/
|
||||
private static JAXBContext initializeJaxbContext(final String contextPath) {
|
||||
try {
|
||||
return JAXBContext.newInstance(contextPath, FileAuthorizer.class.getClassLoader());
|
||||
} catch (JAXBException e) {
|
||||
throw new RuntimeException("Unable to create JAXBContext.");
|
||||
}
|
||||
}
|
||||
|
||||
private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
|
||||
private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();
|
||||
|
||||
private static final String POLICY_ELEMENT = "policy";
|
||||
private static final String POLICY_USER_ELEMENT = "policyUser";
|
||||
private static final String POLICY_GROUP_ELEMENT = "policyGroup";
|
||||
private static final String IDENTIFIER_ATTR = "identifier";
|
||||
private static final String RESOURCE_ATTR = "resource";
|
||||
private static final String ACTIONS_ATTR = "actions";
|
||||
|
||||
static final String READ_CODE = "R";
|
||||
static final String WRITE_CODE = "W";
|
||||
|
||||
static final String PROP_NODE_IDENTITY_PREFIX = "Node Identity ";
|
||||
static final String PROP_USER_GROUP_PROVIDER = "User Group Provider";
|
||||
static final String PROP_AUTHORIZATIONS_FILE = "Authorizations File";
|
||||
static final String PROP_INITIAL_ADMIN_IDENTITY = "Initial Admin Identity";
|
||||
static final Pattern NODE_IDENTITY_PATTERN = Pattern.compile(PROP_NODE_IDENTITY_PREFIX + "\\S+");
|
||||
|
||||
private Schema usersSchema;
|
||||
private Schema authorizationsSchema;
|
||||
private NiFiProperties properties;
|
||||
private File authorizationsFile;
|
||||
private File restoreAuthorizationsFile;
|
||||
private String rootGroupId;
|
||||
private String initialAdminIdentity;
|
||||
private String legacyAuthorizedUsersFile;
|
||||
private Set<String> nodeIdentities;
|
||||
private List<PortDTO> ports = new ArrayList<>();
|
||||
private List<IdentityMapping> identityMappings;
|
||||
|
||||
private UserGroupProvider userGroupProvider;
|
||||
private UserGroupProviderLookup userGroupProviderLookup;
|
||||
private final AtomicReference<AuthorizationsHolder> authorizationsHolder = new AtomicReference<>();
|
||||
|
||||
@Override
|
||||
public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
userGroupProviderLookup = initializationContext.getUserGroupProviderLookup();
|
||||
|
||||
try {
|
||||
final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
authorizationsSchema = schemaFactory.newSchema(FileAuthorizer.class.getResource(AUTHORIZATIONS_XSD));
|
||||
usersSchema = schemaFactory.newSchema(FileAuthorizer.class.getResource(USERS_XSD));
|
||||
} catch (Exception e) {
|
||||
throw new AuthorizerCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
try {
|
||||
final PropertyValue userGroupProviderIdentifier = configurationContext.getProperty(PROP_USER_GROUP_PROVIDER);
|
||||
if (!userGroupProviderIdentifier.isSet()) {
|
||||
throw new AuthorizerCreationException("The user group provider must be specified.");
|
||||
}
|
||||
|
||||
userGroupProvider = userGroupProviderLookup.getUserGroupProvider(userGroupProviderIdentifier.getValue());
|
||||
if (userGroupProvider == null) {
|
||||
throw new AuthorizerCreationException("Unable to locate user group provider with identifier " + userGroupProviderIdentifier.getValue());
|
||||
}
|
||||
|
||||
final PropertyValue authorizationsPath = configurationContext.getProperty(PROP_AUTHORIZATIONS_FILE);
|
||||
if (StringUtils.isBlank(authorizationsPath.getValue())) {
|
||||
throw new AuthorizerCreationException("The authorizations file must be specified.");
|
||||
}
|
||||
|
||||
// get the authorizations file and ensure it exists
|
||||
authorizationsFile = new File(authorizationsPath.getValue());
|
||||
if (!authorizationsFile.exists()) {
|
||||
logger.info("Creating new authorizations file at {}", new Object[] {authorizationsFile.getAbsolutePath()});
|
||||
saveAuthorizations(new Authorizations());
|
||||
}
|
||||
|
||||
final File authorizationsFileDirectory = authorizationsFile.getAbsoluteFile().getParentFile();
|
||||
|
||||
// the restore directory is optional and may be null
|
||||
final File restoreDirectory = properties.getRestoreDirectory();
|
||||
if (restoreDirectory != null) {
|
||||
// sanity check that restore directory is a directory, creating it if necessary
|
||||
FileUtils.ensureDirectoryExistAndCanAccess(restoreDirectory);
|
||||
|
||||
// check that restore directory is not the same as the authorizations directory
|
||||
if (authorizationsFileDirectory.getAbsolutePath().equals(restoreDirectory.getAbsolutePath())) {
|
||||
throw new AuthorizerCreationException(String.format("Authorizations file directory '%s' is the same as restore directory '%s' ",
|
||||
authorizationsFileDirectory.getAbsolutePath(), restoreDirectory.getAbsolutePath()));
|
||||
}
|
||||
|
||||
// the restore copy will have same file name, but reside in a different directory
|
||||
restoreAuthorizationsFile = new File(restoreDirectory, authorizationsFile.getName());
|
||||
|
||||
try {
|
||||
// sync the primary copy with the restore copy
|
||||
FileUtils.syncWithRestore(authorizationsFile, restoreAuthorizationsFile, logger);
|
||||
} catch (final IOException | IllegalStateException ioe) {
|
||||
throw new AuthorizerCreationException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
// extract the identity mappings from nifi.properties if any are provided
|
||||
identityMappings = Collections.unmodifiableList(IdentityMappingUtil.getIdentityMappings(properties));
|
||||
|
||||
// get the value of the initial admin identity
|
||||
final PropertyValue initialAdminIdentityProp = configurationContext.getProperty(PROP_INITIAL_ADMIN_IDENTITY);
|
||||
initialAdminIdentity = initialAdminIdentityProp.isSet() ? IdentityMappingUtil.mapIdentity(initialAdminIdentityProp.getValue(), identityMappings) : null;
|
||||
|
||||
// get the value of the legacy authorized users file
|
||||
final PropertyValue legacyAuthorizedUsersProp = configurationContext.getProperty(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE);
|
||||
legacyAuthorizedUsersFile = legacyAuthorizedUsersProp.isSet() ? legacyAuthorizedUsersProp.getValue() : null;
|
||||
|
||||
// extract any node identities
|
||||
nodeIdentities = new HashSet<>();
|
||||
for (Map.Entry<String,String> entry : configurationContext.getProperties().entrySet()) {
|
||||
Matcher matcher = NODE_IDENTITY_PATTERN.matcher(entry.getKey());
|
||||
if (matcher.matches() && !StringUtils.isBlank(entry.getValue())) {
|
||||
nodeIdentities.add(IdentityMappingUtil.mapIdentity(entry.getValue(), identityMappings));
|
||||
}
|
||||
}
|
||||
|
||||
// load the authorizations
|
||||
load();
|
||||
|
||||
// if we've copied the authorizations file to a restore directory synchronize it
|
||||
if (restoreAuthorizationsFile != null) {
|
||||
FileUtils.copyFile(authorizationsFile, restoreAuthorizationsFile, false, false, logger);
|
||||
}
|
||||
|
||||
logger.info(String.format("Authorizations file loaded at %s", new Date().toString()));
|
||||
} catch (IOException | AuthorizerCreationException | JAXBException | IllegalStateException | SAXException e) {
|
||||
throw new AuthorizerCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserGroupProvider getUserGroupProvider() {
|
||||
return userGroupProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException {
|
||||
return authorizationsHolder.get().getAllPolicies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AccessPolicy addAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
if (accessPolicy == null) {
|
||||
throw new IllegalArgumentException("AccessPolicy cannot be null");
|
||||
}
|
||||
|
||||
// create the new JAXB Policy
|
||||
final Policy policy = createJAXBPolicy(accessPolicy);
|
||||
|
||||
// add the new Policy to the top-level list of policies
|
||||
final AuthorizationsHolder holder = authorizationsHolder.get();
|
||||
final Authorizations authorizations = holder.getAuthorizations();
|
||||
authorizations.getPolicies().getPolicy().add(policy);
|
||||
|
||||
saveAndRefreshHolder(authorizations);
|
||||
|
||||
return authorizationsHolder.get().getPoliciesById().get(accessPolicy.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException {
|
||||
if (identifier == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final AuthorizationsHolder holder = authorizationsHolder.get();
|
||||
return holder.getPoliciesById().get(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException {
|
||||
return authorizationsHolder.get().getAccessPolicy(resourceIdentifier, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
if (accessPolicy == null) {
|
||||
throw new IllegalArgumentException("AccessPolicy cannot be null");
|
||||
}
|
||||
|
||||
final AuthorizationsHolder holder = this.authorizationsHolder.get();
|
||||
final Authorizations authorizations = holder.getAuthorizations();
|
||||
|
||||
// try to find an existing Authorization that matches the policy id
|
||||
Policy updatePolicy = null;
|
||||
for (Policy policy : authorizations.getPolicies().getPolicy()) {
|
||||
if (policy.getIdentifier().equals(accessPolicy.getIdentifier())) {
|
||||
updatePolicy = policy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no matching Policy so return null
|
||||
if (updatePolicy == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// update the Policy, save, reload, and return
|
||||
transferUsersAndGroups(accessPolicy, updatePolicy);
|
||||
saveAndRefreshHolder(authorizations);
|
||||
|
||||
return this.authorizationsHolder.get().getPoliciesById().get(accessPolicy.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AccessPolicy deleteAccessPolicy(AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
if (accessPolicy == null) {
|
||||
throw new IllegalArgumentException("AccessPolicy cannot be null");
|
||||
}
|
||||
|
||||
final AuthorizationsHolder holder = this.authorizationsHolder.get();
|
||||
final Authorizations authorizations = holder.getAuthorizations();
|
||||
|
||||
// find the matching Policy and remove it
|
||||
boolean deletedPolicy = false;
|
||||
Iterator<Policy> policyIter = authorizations.getPolicies().getPolicy().iterator();
|
||||
while (policyIter.hasNext()) {
|
||||
final Policy policy = policyIter.next();
|
||||
if (policy.getIdentifier().equals(accessPolicy.getIdentifier())) {
|
||||
policyIter.remove();
|
||||
deletedPolicy = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// never found a matching Policy so return null
|
||||
if (!deletedPolicy) {
|
||||
return null;
|
||||
}
|
||||
|
||||
saveAndRefreshHolder(authorizations);
|
||||
return accessPolicy;
|
||||
}
|
||||
|
||||
AuthorizationsHolder getAuthorizationsHolder() {
|
||||
return authorizationsHolder.get();
|
||||
}
|
||||
|
||||
@AuthorizerContext
|
||||
public void setNiFiProperties(NiFiProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
|
||||
parsePolicies(fingerprint).forEach(policy -> addAccessPolicy(policy));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
|
||||
try {
|
||||
// ensure we can understand the proposed fingerprint
|
||||
parsePolicies(proposedFingerprint);
|
||||
} catch (final AuthorizationAccessException e) {
|
||||
throw new UninheritableAuthorizationsException("Unable to parse the proposed fingerprint: " + e);
|
||||
}
|
||||
|
||||
// ensure we are in a proper state to inherit the fingerprint
|
||||
if (!getAccessPolicies().isEmpty()) {
|
||||
throw new UninheritableAuthorizationsException("Proposed fingerprint is not inheritable because the current access policies is not empty.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFingerprint() throws AuthorizationAccessException {
|
||||
final List<AccessPolicy> policies = new ArrayList<>(getAccessPolicies());
|
||||
Collections.sort(policies, Comparator.comparing(AccessPolicy::getIdentifier));
|
||||
|
||||
XMLStreamWriter writer = null;
|
||||
final StringWriter out = new StringWriter();
|
||||
try {
|
||||
writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
|
||||
writer.writeStartDocument();
|
||||
writer.writeStartElement("accessPolicies");
|
||||
|
||||
for (AccessPolicy policy : policies) {
|
||||
writePolicy(writer, policy);
|
||||
}
|
||||
|
||||
writer.writeEndElement();
|
||||
writer.writeEndDocument();
|
||||
writer.flush();
|
||||
} catch (XMLStreamException e) {
|
||||
throw new AuthorizationAccessException("Unable to generate fingerprint", e);
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (XMLStreamException e) {
|
||||
// nothing to do here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private List<AccessPolicy> parsePolicies(final String fingerprint) {
|
||||
final List<AccessPolicy> policies = new ArrayList<>();
|
||||
|
||||
final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
|
||||
try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) {
|
||||
final DocumentBuilder docBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
|
||||
final Document document = docBuilder.parse(in);
|
||||
final Element rootElement = document.getDocumentElement();
|
||||
|
||||
// parse all the policies and add them to the current access policy provider
|
||||
NodeList policyNodes = rootElement.getElementsByTagName(POLICY_ELEMENT);
|
||||
for (int i = 0; i < policyNodes.getLength(); i++) {
|
||||
Node policyNode = policyNodes.item(i);
|
||||
policies.add(parsePolicy((Element) policyNode));
|
||||
}
|
||||
} catch (SAXException | ParserConfigurationException | IOException e) {
|
||||
throw new AuthorizationAccessException("Unable to parse fingerprint", e);
|
||||
}
|
||||
|
||||
return policies;
|
||||
}
|
||||
|
||||
private AccessPolicy parsePolicy(final Element element) {
|
||||
final AccessPolicy.Builder builder = new AccessPolicy.Builder()
|
||||
.identifier(element.getAttribute(IDENTIFIER_ATTR))
|
||||
.resource(element.getAttribute(RESOURCE_ATTR));
|
||||
|
||||
final String actions = element.getAttribute(ACTIONS_ATTR);
|
||||
if (actions.equals(RequestAction.READ.name())) {
|
||||
builder.action(RequestAction.READ);
|
||||
} else if (actions.equals(RequestAction.WRITE.name())) {
|
||||
builder.action(RequestAction.WRITE);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown Policy Action: " + actions);
|
||||
}
|
||||
|
||||
NodeList policyUsers = element.getElementsByTagName(POLICY_USER_ELEMENT);
|
||||
for (int i=0; i < policyUsers.getLength(); i++) {
|
||||
Element policyUserNode = (Element) policyUsers.item(i);
|
||||
builder.addUser(policyUserNode.getAttribute(IDENTIFIER_ATTR));
|
||||
}
|
||||
|
||||
NodeList policyGroups = element.getElementsByTagName(POLICY_GROUP_ELEMENT);
|
||||
for (int i=0; i < policyGroups.getLength(); i++) {
|
||||
Element policyGroupNode = (Element) policyGroups.item(i);
|
||||
builder.addGroup(policyGroupNode.getAttribute(IDENTIFIER_ATTR));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void writePolicy(final XMLStreamWriter writer, final AccessPolicy policy) throws XMLStreamException {
|
||||
// sort the users for the policy
|
||||
List<String> policyUsers = new ArrayList<>(policy.getUsers());
|
||||
Collections.sort(policyUsers);
|
||||
|
||||
// sort the groups for this policy
|
||||
List<String> policyGroups = new ArrayList<>(policy.getGroups());
|
||||
Collections.sort(policyGroups);
|
||||
|
||||
writer.writeStartElement(POLICY_ELEMENT);
|
||||
writer.writeAttribute(IDENTIFIER_ATTR, policy.getIdentifier());
|
||||
writer.writeAttribute(RESOURCE_ATTR, policy.getResource());
|
||||
writer.writeAttribute(ACTIONS_ATTR, policy.getAction().name());
|
||||
|
||||
for (String policyUser : policyUsers) {
|
||||
writer.writeStartElement(POLICY_USER_ELEMENT);
|
||||
writer.writeAttribute(IDENTIFIER_ATTR, policyUser);
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
for (String policyGroup : policyGroups) {
|
||||
writer.writeStartElement(POLICY_GROUP_ELEMENT);
|
||||
writer.writeAttribute(IDENTIFIER_ATTR, policyGroup);
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the authorizations file and populates the AuthorizationsHolder, only called during start-up.
|
||||
*
|
||||
* @throws JAXBException Unable to reload the authorized users file
|
||||
* @throws IOException Unable to sync file with restore
|
||||
* @throws IllegalStateException Unable to sync file with restore
|
||||
*/
|
||||
private synchronized void load() throws JAXBException, IOException, IllegalStateException, SAXException {
|
||||
// attempt to unmarshal
|
||||
final Authorizations authorizations = unmarshallAuthorizations();
|
||||
if (authorizations.getPolicies() == null) {
|
||||
authorizations.setPolicies(new Policies());
|
||||
}
|
||||
|
||||
final AuthorizationsHolder authorizationsHolder = new AuthorizationsHolder(authorizations);
|
||||
final boolean emptyAuthorizations = authorizationsHolder.getAllPolicies().isEmpty();
|
||||
final boolean hasInitialAdminIdentity = (initialAdminIdentity != null && !StringUtils.isBlank(initialAdminIdentity));
|
||||
final boolean hasLegacyAuthorizedUsers = (legacyAuthorizedUsersFile != null && !StringUtils.isBlank(legacyAuthorizedUsersFile));
|
||||
|
||||
// if we are starting fresh then we might need to populate an initial admin or convert legacy users
|
||||
if (emptyAuthorizations) {
|
||||
parseFlow();
|
||||
|
||||
if (hasInitialAdminIdentity && hasLegacyAuthorizedUsers) {
|
||||
throw new AuthorizerCreationException("Cannot provide an Initial Admin Identity and a Legacy Authorized Users File");
|
||||
} else if (hasInitialAdminIdentity) {
|
||||
logger.info("Populating authorizations for Initial Admin: " + initialAdminIdentity);
|
||||
populateInitialAdmin(authorizations);
|
||||
} else if (hasLegacyAuthorizedUsers) {
|
||||
logger.info("Converting " + legacyAuthorizedUsersFile + " to new authorizations model");
|
||||
convertLegacyAuthorizedUsers(authorizations);
|
||||
}
|
||||
|
||||
populateNodes(authorizations);
|
||||
|
||||
// save any changes that were made and repopulate the holder
|
||||
saveAndRefreshHolder(authorizations);
|
||||
} else {
|
||||
this.authorizationsHolder.set(authorizationsHolder);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveAuthorizations(final Authorizations authorizations) throws JAXBException {
|
||||
final Marshaller marshaller = JAXB_AUTHORIZATIONS_CONTEXT.createMarshaller();
|
||||
marshaller.setSchema(authorizationsSchema);
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
|
||||
marshaller.marshal(authorizations, authorizationsFile);
|
||||
}
|
||||
|
||||
private Authorizations unmarshallAuthorizations() throws JAXBException {
|
||||
final Unmarshaller unmarshaller = JAXB_AUTHORIZATIONS_CONTEXT.createUnmarshaller();
|
||||
unmarshaller.setSchema(authorizationsSchema);
|
||||
|
||||
final JAXBElement<Authorizations> element = unmarshaller.unmarshal(new StreamSource(authorizationsFile), Authorizations.class);
|
||||
return element.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to parse the flow configuration file to extract the root group id and port information.
|
||||
*
|
||||
* @throws SAXException if an error occurs creating the schema
|
||||
*/
|
||||
private void parseFlow() throws SAXException {
|
||||
final FlowParser flowParser = new FlowParser();
|
||||
final FlowInfo flowInfo = flowParser.parse(properties.getFlowConfigurationFile());
|
||||
|
||||
if (flowInfo != null) {
|
||||
rootGroupId = flowInfo.getRootGroupId();
|
||||
ports = flowInfo.getPorts() == null ? new ArrayList<>() : flowInfo.getPorts();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the initial admin user and policies for access the flow and managing users and policies.
|
||||
*/
|
||||
private void populateInitialAdmin(final Authorizations authorizations) {
|
||||
final User initialAdmin = userGroupProvider.getUserByIdentity(initialAdminIdentity);
|
||||
if (initialAdmin == null) {
|
||||
throw new AuthorizerCreationException("Unable to locate initial admin " + initialAdminIdentity + " to seed policies");
|
||||
}
|
||||
|
||||
// grant the user read access to the /flow resource
|
||||
addUserToAccessPolicy(authorizations, ResourceType.Flow.getValue(), initialAdmin.getIdentifier(), READ_CODE);
|
||||
|
||||
// grant the user read access to the root process group resource
|
||||
if (rootGroupId != null) {
|
||||
addUserToAccessPolicy(authorizations, ResourceType.Data.getValue() + ResourceType.ProcessGroup.getValue() + "/" + rootGroupId, initialAdmin.getIdentifier(), READ_CODE);
|
||||
addUserToAccessPolicy(authorizations, ResourceType.Data.getValue() + ResourceType.ProcessGroup.getValue() + "/" + rootGroupId, initialAdmin.getIdentifier(), WRITE_CODE);
|
||||
|
||||
addUserToAccessPolicy(authorizations, ResourceType.ProcessGroup.getValue() + "/" + rootGroupId, initialAdmin.getIdentifier(), READ_CODE);
|
||||
addUserToAccessPolicy(authorizations, ResourceType.ProcessGroup.getValue() + "/" + rootGroupId, initialAdmin.getIdentifier(), WRITE_CODE);
|
||||
}
|
||||
|
||||
// grant the user write to restricted components
|
||||
addUserToAccessPolicy(authorizations, ResourceType.RestrictedComponents.getValue(), initialAdmin.getIdentifier(), WRITE_CODE);
|
||||
|
||||
// grant the user read/write access to the /tenants resource
|
||||
addUserToAccessPolicy(authorizations, ResourceType.Tenant.getValue(), initialAdmin.getIdentifier(), READ_CODE);
|
||||
addUserToAccessPolicy(authorizations, ResourceType.Tenant.getValue(), initialAdmin.getIdentifier(), WRITE_CODE);
|
||||
|
||||
// grant the user read/write access to the /policies resource
|
||||
addUserToAccessPolicy(authorizations, ResourceType.Policy.getValue(), initialAdmin.getIdentifier(), READ_CODE);
|
||||
addUserToAccessPolicy(authorizations, ResourceType.Policy.getValue(), initialAdmin.getIdentifier(), WRITE_CODE);
|
||||
|
||||
// grant the user read/write access to the /controller resource
|
||||
addUserToAccessPolicy(authorizations, ResourceType.Controller.getValue(), initialAdmin.getIdentifier(), READ_CODE);
|
||||
addUserToAccessPolicy(authorizations, ResourceType.Controller.getValue(), initialAdmin.getIdentifier(), WRITE_CODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a user for each node and gives the nodes write permission to /proxy.
|
||||
*
|
||||
* @param authorizations the overall authorizations
|
||||
*/
|
||||
private void populateNodes(Authorizations authorizations) {
|
||||
for (String nodeIdentity : nodeIdentities) {
|
||||
final User node = userGroupProvider.getUserByIdentity(nodeIdentity);
|
||||
if (node == null) {
|
||||
throw new AuthorizerCreationException("Unable to locate node " + nodeIdentity + " to seed policies.");
|
||||
}
|
||||
|
||||
// grant access to the proxy resource
|
||||
addUserToAccessPolicy(authorizations, ResourceType.Proxy.getValue(), node.getIdentifier(), WRITE_CODE);
|
||||
|
||||
// grant the user read/write access data of the root group
|
||||
if (rootGroupId != null) {
|
||||
addUserToAccessPolicy(authorizations, ResourceType.Data.getValue() + ResourceType.ProcessGroup.getValue() + "/" + rootGroupId, node.getIdentifier(), READ_CODE);
|
||||
addUserToAccessPolicy(authorizations, ResourceType.Data.getValue() + ResourceType.ProcessGroup.getValue() + "/" + rootGroupId, node.getIdentifier(), WRITE_CODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshalls an existing authorized-users.xml and converts the object model to the new model.
|
||||
*
|
||||
* @param authorizations the current Authorizations instance that policies will be added to
|
||||
* @throws AuthorizerCreationException if the legacy authorized users file that was provided does not exist
|
||||
* @throws JAXBException if the legacy authorized users file that was provided could not be unmarshalled
|
||||
*/
|
||||
private void convertLegacyAuthorizedUsers(final Authorizations authorizations) throws AuthorizerCreationException, JAXBException {
|
||||
final File authorizedUsersFile = new File(legacyAuthorizedUsersFile);
|
||||
if (!authorizedUsersFile.exists()) {
|
||||
throw new AuthorizerCreationException("Legacy Authorized Users File '" + legacyAuthorizedUsersFile + "' does not exists");
|
||||
}
|
||||
|
||||
final Unmarshaller unmarshaller = JAXB_USERS_CONTEXT.createUnmarshaller();
|
||||
unmarshaller.setSchema(usersSchema);
|
||||
|
||||
final JAXBElement<Users> element = unmarshaller.unmarshal(
|
||||
new StreamSource(authorizedUsersFile), org.apache.nifi.user.generated.Users.class);
|
||||
|
||||
final org.apache.nifi.user.generated.Users users = element.getValue();
|
||||
if (users.getUser().isEmpty()) {
|
||||
logger.info("Legacy Authorized Users File contained no users, nothing to convert");
|
||||
return;
|
||||
}
|
||||
|
||||
// get all the user DNs into a list
|
||||
List<String> userIdentities = new ArrayList<>();
|
||||
for (org.apache.nifi.user.generated.User legacyUser : users.getUser()) {
|
||||
userIdentities.add(IdentityMappingUtil.mapIdentity(legacyUser.getDn(), identityMappings));
|
||||
}
|
||||
|
||||
// sort the list and pull out the first identity
|
||||
Collections.sort(userIdentities);
|
||||
final String seedIdentity = userIdentities.get(0);
|
||||
|
||||
// create mapping from Role to access policies
|
||||
final Map<Role,Set<RoleAccessPolicy>> roleAccessPolicies = RoleAccessPolicy.getMappings(rootGroupId);
|
||||
|
||||
final List<Policy> allPolicies = new ArrayList<>();
|
||||
|
||||
for (org.apache.nifi.user.generated.User legacyUser : users.getUser()) {
|
||||
// create the identifier of the new user based on the DN
|
||||
final String legacyUserDn = IdentityMappingUtil.mapIdentity(legacyUser.getDn(), identityMappings);
|
||||
final User user = userGroupProvider.getUserByIdentity(legacyUserDn);
|
||||
if (user == null) {
|
||||
throw new AuthorizerCreationException("Unable to locate legacy user " + legacyUserDn + " to seed policies.");
|
||||
}
|
||||
|
||||
// create policies based on the given role
|
||||
for (org.apache.nifi.user.generated.Role jaxbRole : legacyUser.getRole()) {
|
||||
Role role = Role.valueOf(jaxbRole.getName());
|
||||
Set<RoleAccessPolicy> policies = roleAccessPolicies.get(role);
|
||||
|
||||
for (RoleAccessPolicy roleAccessPolicy : policies) {
|
||||
|
||||
// get the matching policy, or create a new one
|
||||
Policy policy = getOrCreatePolicy(
|
||||
allPolicies,
|
||||
seedIdentity,
|
||||
roleAccessPolicy.getResource(),
|
||||
roleAccessPolicy.getAction());
|
||||
|
||||
// add the user to the policy if it doesn't exist
|
||||
addUserToPolicy(user.getIdentifier(), policy);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// convert any access controls on ports to the appropriate policies
|
||||
for (PortDTO portDTO : ports) {
|
||||
final Resource resource;
|
||||
if (portDTO.getType() != null && portDTO.getType().equals("inputPort")) {
|
||||
resource = ResourceFactory.getDataTransferResource(ResourceFactory.getComponentResource(ResourceType.InputPort, portDTO.getId(), portDTO.getName()));
|
||||
} else {
|
||||
resource = ResourceFactory.getDataTransferResource(ResourceFactory.getComponentResource(ResourceType.OutputPort, portDTO.getId(), portDTO.getName()));
|
||||
}
|
||||
|
||||
if (portDTO.getUserAccessControl() != null) {
|
||||
for (String userAccessControl : portDTO.getUserAccessControl()) {
|
||||
// need to perform the identity mapping on the access control so it matches the identities in the User objects
|
||||
final String mappedUserAccessControl = IdentityMappingUtil.mapIdentity(userAccessControl, identityMappings);
|
||||
final User foundUser = userGroupProvider.getUserByIdentity(mappedUserAccessControl);
|
||||
|
||||
// couldn't find the user matching the access control so log a warning and skip
|
||||
if (foundUser == null) {
|
||||
logger.warn("Found port with user access control for {} but no user exists with this identity, skipping...",
|
||||
new Object[] {mappedUserAccessControl});
|
||||
continue;
|
||||
}
|
||||
|
||||
// we found the user so create the appropriate policy and add the user to it
|
||||
Policy policy = getOrCreatePolicy(
|
||||
allPolicies,
|
||||
seedIdentity,
|
||||
resource.getIdentifier(),
|
||||
WRITE_CODE);
|
||||
|
||||
addUserToPolicy(foundUser.getIdentifier(), policy);
|
||||
}
|
||||
}
|
||||
|
||||
if (portDTO.getGroupAccessControl() != null) {
|
||||
for (String groupAccessControl : portDTO.getGroupAccessControl()) {
|
||||
// find a group where the name is the groupAccessControl
|
||||
Group foundGroup = null;
|
||||
for (Group group : userGroupProvider.getGroups()) {
|
||||
if (group.getName().equals(groupAccessControl)) {
|
||||
foundGroup = group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// couldn't find the group matching the access control so log a warning and skip
|
||||
if (foundGroup == null) {
|
||||
logger.warn("Found port with group access control for {} but no group exists with this name, skipping...",
|
||||
new Object[] {groupAccessControl});
|
||||
continue;
|
||||
}
|
||||
|
||||
// we found the group so create the appropriate policy and add all the users to it
|
||||
Policy policy = getOrCreatePolicy(
|
||||
allPolicies,
|
||||
seedIdentity,
|
||||
resource.getIdentifier(),
|
||||
WRITE_CODE);
|
||||
|
||||
addGroupToPolicy(IdentifierUtil.getIdentifier(groupAccessControl), policy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
authorizations.getPolicies().getPolicy().addAll(allPolicies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and adds an access policy for the given resource, identity, and actions to the specified authorizations.
|
||||
*
|
||||
* @param authorizations the Authorizations instance to add the policy to
|
||||
* @param resource the resource for the policy
|
||||
* @param userIdentifier the identifier for the user to add to the policy
|
||||
* @param action the action for the policy
|
||||
*/
|
||||
private void addUserToAccessPolicy(final Authorizations authorizations, final String resource, final String userIdentifier, final String action) {
|
||||
// first try to find an existing policy for the given resource and action
|
||||
Policy foundPolicy = null;
|
||||
for (Policy policy : authorizations.getPolicies().getPolicy()) {
|
||||
if (policy.getResource().equals(resource) && policy.getAction().equals(action)) {
|
||||
foundPolicy = policy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundPolicy == null) {
|
||||
// if we didn't find an existing policy create a new one
|
||||
final String uuidSeed = resource + action;
|
||||
|
||||
final AccessPolicy.Builder builder = new AccessPolicy.Builder()
|
||||
.identifierGenerateFromSeed(uuidSeed)
|
||||
.resource(resource)
|
||||
.addUser(userIdentifier);
|
||||
|
||||
if (action.equals(READ_CODE)) {
|
||||
builder.action(RequestAction.READ);
|
||||
} else if (action.equals(WRITE_CODE)) {
|
||||
builder.action(RequestAction.WRITE);
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown Policy Action: " + action);
|
||||
}
|
||||
|
||||
final AccessPolicy accessPolicy = builder.build();
|
||||
final Policy jaxbPolicy = createJAXBPolicy(accessPolicy);
|
||||
authorizations.getPolicies().getPolicy().add(jaxbPolicy);
|
||||
} else {
|
||||
// otherwise add the user to the existing policy
|
||||
Policy.User policyUser = new Policy.User();
|
||||
policyUser.setIdentifier(userIdentifier);
|
||||
foundPolicy.getUser().add(policyUser);
|
||||
}
|
||||
}
|
||||
|
||||
private Policy createJAXBPolicy(final AccessPolicy accessPolicy) {
|
||||
final Policy policy = new Policy();
|
||||
policy.setIdentifier(accessPolicy.getIdentifier());
|
||||
policy.setResource(accessPolicy.getResource());
|
||||
|
||||
switch (accessPolicy.getAction()) {
|
||||
case READ:
|
||||
policy.setAction(READ_CODE);
|
||||
break;
|
||||
case WRITE:
|
||||
policy.setAction(WRITE_CODE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
transferUsersAndGroups(accessPolicy, policy);
|
||||
return policy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given Policy to the state of the provided AccessPolicy. Users and Groups will be cleared and
|
||||
* set to match the AccessPolicy, the resource and action will be set to match the AccessPolicy.
|
||||
*
|
||||
* Does not set the identifier.
|
||||
*
|
||||
* @param accessPolicy the AccessPolicy to transfer state from
|
||||
* @param policy the Policy to transfer state to
|
||||
*/
|
||||
private void transferUsersAndGroups(AccessPolicy accessPolicy, Policy policy) {
|
||||
// add users to the policy
|
||||
policy.getUser().clear();
|
||||
for (String userIdentifier : accessPolicy.getUsers()) {
|
||||
Policy.User policyUser = new Policy.User();
|
||||
policyUser.setIdentifier(userIdentifier);
|
||||
policy.getUser().add(policyUser);
|
||||
}
|
||||
|
||||
// add groups to the policy
|
||||
policy.getGroup().clear();
|
||||
for (String groupIdentifier : accessPolicy.getGroups()) {
|
||||
Policy.Group policyGroup = new Policy.Group();
|
||||
policyGroup.setIdentifier(groupIdentifier);
|
||||
policy.getGroup().add(policyGroup);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given user identifier to the policy if it doesn't already exist.
|
||||
*
|
||||
* @param userIdentifier a user identifier
|
||||
* @param policy a policy to add the user to
|
||||
*/
|
||||
private void addUserToPolicy(final String userIdentifier, final Policy policy) {
|
||||
// determine if the user already exists in the policy
|
||||
boolean userExists = false;
|
||||
for (Policy.User policyUser : policy.getUser()) {
|
||||
if (policyUser.getIdentifier().equals(userIdentifier)) {
|
||||
userExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add the user to the policy if doesn't already exist
|
||||
if (!userExists) {
|
||||
Policy.User policyUser = new Policy.User();
|
||||
policyUser.setIdentifier(userIdentifier);
|
||||
policy.getUser().add(policyUser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given group identifier to the policy if it doesn't already exist.
|
||||
*
|
||||
* @param groupIdentifier a group identifier
|
||||
* @param policy a policy to add the user to
|
||||
*/
|
||||
private void addGroupToPolicy(final String groupIdentifier, final Policy policy) {
|
||||
// determine if the group already exists in the policy
|
||||
boolean groupExists = false;
|
||||
for (Policy.Group policyGroup : policy.getGroup()) {
|
||||
if (policyGroup.getIdentifier().equals(groupIdentifier)) {
|
||||
groupExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add the group to the policy if doesn't already exist
|
||||
if (!groupExists) {
|
||||
Policy.Group policyGroup = new Policy.Group();
|
||||
policyGroup.setIdentifier(groupIdentifier);
|
||||
policy.getGroup().add(policyGroup);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the Policy matching the resource and action, or creates a new one and adds it to the list of policies.
|
||||
*
|
||||
* @param policies the policies to search through
|
||||
* @param seedIdentity the seedIdentity to use when creating identifiers for new policies
|
||||
* @param resource the resource for the policy
|
||||
* @param action the action string for the police (R or RW)
|
||||
* @return the matching policy or a new policy
|
||||
*/
|
||||
private Policy getOrCreatePolicy(final List<Policy> policies, final String seedIdentity, final String resource, final String action) {
|
||||
Policy foundPolicy = null;
|
||||
|
||||
// try to find a policy with the same resource and actions
|
||||
for (Policy policy : policies) {
|
||||
if (policy.getResource().equals(resource) && policy.getAction().equals(action)) {
|
||||
foundPolicy = policy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if a matching policy wasn't found then create one
|
||||
if (foundPolicy == null) {
|
||||
final String uuidSeed = resource + action + seedIdentity;
|
||||
final String policyIdentifier = IdentifierUtil.getIdentifier(uuidSeed);
|
||||
|
||||
foundPolicy = new Policy();
|
||||
foundPolicy.setIdentifier(policyIdentifier);
|
||||
foundPolicy.setResource(resource);
|
||||
foundPolicy.setAction(action);
|
||||
|
||||
policies.add(foundPolicy);
|
||||
}
|
||||
|
||||
return foundPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the Authorizations instance by marshalling to a file, then re-populates the
|
||||
* in-memory data structures and sets the new holder.
|
||||
*
|
||||
* Synchronized to ensure only one thread writes the file at a time.
|
||||
*
|
||||
* @param authorizations the authorizations to save and populate from
|
||||
* @throws AuthorizationAccessException if an error occurs saving the authorizations
|
||||
*/
|
||||
private synchronized void saveAndRefreshHolder(final Authorizations authorizations) throws AuthorizationAccessException {
|
||||
try {
|
||||
saveAuthorizations(authorizations);
|
||||
|
||||
this.authorizationsHolder.set(new AuthorizationsHolder(authorizations));
|
||||
} catch (JAXBException e) {
|
||||
throw new AuthorizationAccessException("Unable to save Authorizations", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,820 @@
|
|||
/*
|
||||
* 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.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authorization.annotation.AuthorizerContext;
|
||||
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.exception.UninheritableAuthorizationsException;
|
||||
import org.apache.nifi.authorization.file.tenants.generated.Groups;
|
||||
import org.apache.nifi.authorization.file.tenants.generated.Tenants;
|
||||
import org.apache.nifi.authorization.file.tenants.generated.Users;
|
||||
import org.apache.nifi.authorization.util.IdentityMapping;
|
||||
import org.apache.nifi.authorization.util.IdentityMappingUtil;
|
||||
import org.apache.nifi.components.PropertyValue;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.util.file.FileUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBElement;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FileUserGroupProvider implements ConfigurableUserGroupProvider {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileUserGroupProvider.class);
|
||||
|
||||
private static final String TENANTS_XSD = "/tenants.xsd";
|
||||
private static final String JAXB_TENANTS_PATH = "org.apache.nifi.authorization.file.tenants.generated";
|
||||
|
||||
private static final String USERS_XSD = "/legacy-users.xsd";
|
||||
private static final String JAXB_USERS_PATH = "org.apache.nifi.user.generated";
|
||||
|
||||
private static final JAXBContext JAXB_TENANTS_CONTEXT = initializeJaxbContext(JAXB_TENANTS_PATH);
|
||||
private static final JAXBContext JAXB_USERS_CONTEXT = initializeJaxbContext(JAXB_USERS_PATH);
|
||||
|
||||
/**
|
||||
* Load the JAXBContext.
|
||||
*/
|
||||
private static JAXBContext initializeJaxbContext(final String contextPath) {
|
||||
try {
|
||||
return JAXBContext.newInstance(contextPath, FileAuthorizer.class.getClassLoader());
|
||||
} catch (JAXBException e) {
|
||||
throw new RuntimeException("Unable to create JAXBContext.");
|
||||
}
|
||||
}
|
||||
|
||||
private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
|
||||
private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();
|
||||
|
||||
private static final String USER_ELEMENT = "user";
|
||||
private static final String GROUP_USER_ELEMENT = "groupUser";
|
||||
private static final String GROUP_ELEMENT = "group";
|
||||
private static final String IDENTIFIER_ATTR = "identifier";
|
||||
private static final String IDENTITY_ATTR = "identity";
|
||||
private static final String NAME_ATTR = "name";
|
||||
|
||||
static final String PROP_INITIAL_USER_IDENTITY_PREFIX = "Initial User Identity ";
|
||||
static final String PROP_TENANTS_FILE = "Users File";
|
||||
static final Pattern INITIAL_USER_IDENTITY_PATTERN = Pattern.compile(PROP_INITIAL_USER_IDENTITY_PREFIX + "\\S+");
|
||||
|
||||
private Schema usersSchema;
|
||||
private Schema tenantsSchema;
|
||||
private NiFiProperties properties;
|
||||
private File tenantsFile;
|
||||
private File restoreTenantsFile;
|
||||
private String legacyAuthorizedUsersFile;
|
||||
private Set<String> initialUserIdentities;
|
||||
private List<IdentityMapping> identityMappings;
|
||||
|
||||
private final AtomicReference<UserGroupHolder> userGroupHolder = new AtomicReference<>();
|
||||
|
||||
@Override
|
||||
public void initialize(UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
try {
|
||||
final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
tenantsSchema = schemaFactory.newSchema(FileAuthorizer.class.getResource(TENANTS_XSD));
|
||||
usersSchema = schemaFactory.newSchema(FileAuthorizer.class.getResource(USERS_XSD));
|
||||
} catch (Exception e) {
|
||||
throw new AuthorizerCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
try {
|
||||
final PropertyValue tenantsPath = configurationContext.getProperty(PROP_TENANTS_FILE);
|
||||
if (StringUtils.isBlank(tenantsPath.getValue())) {
|
||||
throw new AuthorizerCreationException("The users file must be specified.");
|
||||
}
|
||||
|
||||
// get the tenants file and ensure it exists
|
||||
tenantsFile = new File(tenantsPath.getValue());
|
||||
if (!tenantsFile.exists()) {
|
||||
logger.info("Creating new users file at {}", new Object[] {tenantsFile.getAbsolutePath()});
|
||||
saveTenants(new Tenants());
|
||||
}
|
||||
|
||||
final File tenantsFileDirectory = tenantsFile.getAbsoluteFile().getParentFile();
|
||||
|
||||
// the restore directory is optional and may be null
|
||||
final File restoreDirectory = properties.getRestoreDirectory();
|
||||
if (restoreDirectory != null) {
|
||||
// sanity check that restore directory is a directory, creating it if necessary
|
||||
FileUtils.ensureDirectoryExistAndCanAccess(restoreDirectory);
|
||||
|
||||
// check that restore directory is not the same as the user's directory
|
||||
if (tenantsFileDirectory.getAbsolutePath().equals(restoreDirectory.getAbsolutePath())) {
|
||||
throw new AuthorizerCreationException(String.format("Users file directory '%s' is the same as restore directory '%s' ",
|
||||
tenantsFileDirectory.getAbsolutePath(), restoreDirectory.getAbsolutePath()));
|
||||
}
|
||||
|
||||
// the restore copy will have same file name, but reside in a different directory
|
||||
restoreTenantsFile = new File(restoreDirectory, tenantsFile.getName());
|
||||
|
||||
try {
|
||||
// sync the primary copy with the restore copy
|
||||
FileUtils.syncWithRestore(tenantsFile, restoreTenantsFile, logger);
|
||||
} catch (final IOException | IllegalStateException ioe) {
|
||||
throw new AuthorizerCreationException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
// extract the identity mappings from nifi.properties if any are provided
|
||||
identityMappings = Collections.unmodifiableList(IdentityMappingUtil.getIdentityMappings(properties));
|
||||
|
||||
// get the value of the legacy authorized users file
|
||||
final PropertyValue legacyAuthorizedUsersProp = configurationContext.getProperty(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE);
|
||||
legacyAuthorizedUsersFile = legacyAuthorizedUsersProp.isSet() ? legacyAuthorizedUsersProp.getValue() : null;
|
||||
|
||||
// extract any node identities
|
||||
initialUserIdentities = new HashSet<>();
|
||||
for (Map.Entry<String,String> entry : configurationContext.getProperties().entrySet()) {
|
||||
Matcher matcher = INITIAL_USER_IDENTITY_PATTERN.matcher(entry.getKey());
|
||||
if (matcher.matches() && !StringUtils.isBlank(entry.getValue())) {
|
||||
initialUserIdentities.add(IdentityMappingUtil.mapIdentity(entry.getValue(), identityMappings));
|
||||
}
|
||||
}
|
||||
|
||||
load();
|
||||
|
||||
// if we've copied the authorizations file to a restore directory synchronize it
|
||||
if (restoreTenantsFile != null) {
|
||||
FileUtils.copyFile(tenantsFile, restoreTenantsFile, false, false, logger);
|
||||
}
|
||||
|
||||
logger.info(String.format("Users/Groups file loaded at %s", new Date().toString()));
|
||||
} catch (IOException | AuthorizerCreationException | JAXBException | IllegalStateException | SAXException e) {
|
||||
throw new AuthorizerCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<User> getUsers() throws AuthorizationAccessException {
|
||||
return userGroupHolder.get().getAllUsers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized User addUser(User user) throws AuthorizationAccessException {
|
||||
if (user == null) {
|
||||
throw new IllegalArgumentException("User cannot be null");
|
||||
}
|
||||
|
||||
final org.apache.nifi.authorization.file.tenants.generated.User jaxbUser = createJAXBUser(user);
|
||||
|
||||
final UserGroupHolder holder = userGroupHolder.get();
|
||||
final Tenants tenants = holder.getTenants();
|
||||
tenants.getUsers().getUser().add(jaxbUser);
|
||||
|
||||
saveAndRefreshHolder(tenants);
|
||||
|
||||
return userGroupHolder.get().getUsersById().get(user.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(String identifier) throws AuthorizationAccessException {
|
||||
if (identifier == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final UserGroupHolder holder = userGroupHolder.get();
|
||||
return holder.getUsersById().get(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized User updateUser(User user) throws AuthorizationAccessException {
|
||||
if (user == null) {
|
||||
throw new IllegalArgumentException("User cannot be null");
|
||||
}
|
||||
|
||||
final UserGroupHolder holder = userGroupHolder.get();
|
||||
final Tenants tenants = holder.getTenants();
|
||||
|
||||
final List<org.apache.nifi.authorization.file.tenants.generated.User> users = tenants.getUsers().getUser();
|
||||
|
||||
// fine the User that needs to be updated
|
||||
org.apache.nifi.authorization.file.tenants.generated.User updateUser = null;
|
||||
for (org.apache.nifi.authorization.file.tenants.generated.User jaxbUser : users) {
|
||||
if (user.getIdentifier().equals(jaxbUser.getIdentifier())) {
|
||||
updateUser = jaxbUser;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if user wasn't found return null, otherwise update the user and save changes
|
||||
if (updateUser == null) {
|
||||
return null;
|
||||
} else {
|
||||
updateUser.setIdentity(user.getIdentity());
|
||||
saveAndRefreshHolder(tenants);
|
||||
|
||||
return userGroupHolder.get().getUsersById().get(user.getIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByIdentity(String identity) throws AuthorizationAccessException {
|
||||
if (identity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final UserGroupHolder holder = userGroupHolder.get();
|
||||
return holder.getUsersByIdentity().get(identity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized User deleteUser(User user) throws AuthorizationAccessException {
|
||||
if (user == null) {
|
||||
throw new IllegalArgumentException("User cannot be null");
|
||||
}
|
||||
|
||||
final UserGroupHolder holder = userGroupHolder.get();
|
||||
final Tenants tenants = holder.getTenants();
|
||||
|
||||
final List<org.apache.nifi.authorization.file.tenants.generated.User> users = tenants.getUsers().getUser();
|
||||
|
||||
// for each group iterate over the user references and remove the user reference if it matches the user being deleted
|
||||
for (org.apache.nifi.authorization.file.tenants.generated.Group group : tenants.getGroups().getGroup()) {
|
||||
Iterator<org.apache.nifi.authorization.file.tenants.generated.Group.User> groupUserIter = group.getUser().iterator();
|
||||
while (groupUserIter.hasNext()) {
|
||||
org.apache.nifi.authorization.file.tenants.generated.Group.User groupUser = groupUserIter.next();
|
||||
if (groupUser.getIdentifier().equals(user.getIdentifier())) {
|
||||
groupUserIter.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove the actual user if it exists
|
||||
boolean removedUser = false;
|
||||
Iterator<org.apache.nifi.authorization.file.tenants.generated.User> iter = users.iterator();
|
||||
while (iter.hasNext()) {
|
||||
org.apache.nifi.authorization.file.tenants.generated.User jaxbUser = iter.next();
|
||||
if (user.getIdentifier().equals(jaxbUser.getIdentifier())) {
|
||||
iter.remove();
|
||||
removedUser = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedUser) {
|
||||
saveAndRefreshHolder(tenants);
|
||||
return user;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() throws AuthorizationAccessException {
|
||||
return userGroupHolder.get().getAllGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Group addGroup(Group group) throws AuthorizationAccessException {
|
||||
if (group == null) {
|
||||
throw new IllegalArgumentException("Group cannot be null");
|
||||
}
|
||||
|
||||
final UserGroupHolder holder = userGroupHolder.get();
|
||||
final Tenants tenants = holder.getTenants();
|
||||
|
||||
// determine that all users in the group exist before doing anything, throw an exception if they don't
|
||||
checkGroupUsers(group, tenants.getUsers().getUser());
|
||||
|
||||
// create a new JAXB Group based on the incoming Group
|
||||
final org.apache.nifi.authorization.file.tenants.generated.Group jaxbGroup = new org.apache.nifi.authorization.file.tenants.generated.Group();
|
||||
jaxbGroup.setIdentifier(group.getIdentifier());
|
||||
jaxbGroup.setName(group.getName());
|
||||
|
||||
// add each user to the group
|
||||
for (String groupUser : group.getUsers()) {
|
||||
org.apache.nifi.authorization.file.tenants.generated.Group.User jaxbGroupUser = new org.apache.nifi.authorization.file.tenants.generated.Group.User();
|
||||
jaxbGroupUser.setIdentifier(groupUser);
|
||||
jaxbGroup.getUser().add(jaxbGroupUser);
|
||||
}
|
||||
|
||||
tenants.getGroups().getGroup().add(jaxbGroup);
|
||||
saveAndRefreshHolder(tenants);
|
||||
|
||||
return userGroupHolder.get().getGroupsById().get(group.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getGroup(String identifier) throws AuthorizationAccessException {
|
||||
if (identifier == null) {
|
||||
return null;
|
||||
}
|
||||
return userGroupHolder.get().getGroupsById().get(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAndGroups getUserAndGroups(final String identity) throws AuthorizationAccessException {
|
||||
final UserGroupHolder holder = userGroupHolder.get();
|
||||
final User user = holder.getUser(identity);
|
||||
final Set<Group> groups = holder.getGroups(identity);
|
||||
|
||||
return new UserAndGroups() {
|
||||
@Override
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Group updateGroup(Group group) throws AuthorizationAccessException {
|
||||
if (group == null) {
|
||||
throw new IllegalArgumentException("Group cannot be null");
|
||||
}
|
||||
|
||||
final UserGroupHolder holder = userGroupHolder.get();
|
||||
final Tenants tenants = holder.getTenants();
|
||||
|
||||
// find the group that needs to be update
|
||||
org.apache.nifi.authorization.file.tenants.generated.Group updateGroup = null;
|
||||
for (org.apache.nifi.authorization.file.tenants.generated.Group jaxbGroup : tenants.getGroups().getGroup()) {
|
||||
if (jaxbGroup.getIdentifier().equals(group.getIdentifier())) {
|
||||
updateGroup = jaxbGroup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if the group wasn't found return null, otherwise update the group and save changes
|
||||
if (updateGroup == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// reset the list of users and add each user to the group
|
||||
updateGroup.getUser().clear();
|
||||
for (String groupUser : group.getUsers()) {
|
||||
org.apache.nifi.authorization.file.tenants.generated.Group.User jaxbGroupUser = new org.apache.nifi.authorization.file.tenants.generated.Group.User();
|
||||
jaxbGroupUser.setIdentifier(groupUser);
|
||||
updateGroup.getUser().add(jaxbGroupUser);
|
||||
}
|
||||
|
||||
updateGroup.setName(group.getName());
|
||||
saveAndRefreshHolder(tenants);
|
||||
|
||||
return userGroupHolder.get().getGroupsById().get(group.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Group deleteGroup(Group group) throws AuthorizationAccessException {
|
||||
final UserGroupHolder holder = userGroupHolder.get();
|
||||
final Tenants tenants = holder.getTenants();
|
||||
|
||||
final List<org.apache.nifi.authorization.file.tenants.generated.Group> groups = tenants.getGroups().getGroup();
|
||||
|
||||
// now remove the actual group from the top-level list of groups
|
||||
boolean removedGroup = false;
|
||||
Iterator<org.apache.nifi.authorization.file.tenants.generated.Group> iter = groups.iterator();
|
||||
while (iter.hasNext()) {
|
||||
org.apache.nifi.authorization.file.tenants.generated.Group jaxbGroup = iter.next();
|
||||
if (group.getIdentifier().equals(jaxbGroup.getIdentifier())) {
|
||||
iter.remove();
|
||||
removedGroup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedGroup) {
|
||||
saveAndRefreshHolder(tenants);
|
||||
return group;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
UserGroupHolder getUserGroupHolder() {
|
||||
return userGroupHolder.get();
|
||||
}
|
||||
|
||||
@AuthorizerContext
|
||||
public void setNiFiProperties(NiFiProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
|
||||
final UsersAndGroups usersAndGroups = parseUsersAndGroups(fingerprint);
|
||||
usersAndGroups.getUsers().forEach(user -> addUser(user));
|
||||
usersAndGroups.getGroups().forEach(group -> addGroup(group));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException {
|
||||
try {
|
||||
// ensure we understand the proposed fingerprint
|
||||
parseUsersAndGroups(proposedFingerprint);
|
||||
} catch (final AuthorizationAccessException e) {
|
||||
throw new UninheritableAuthorizationsException("Unable to parse the proposed fingerprint: " + e);
|
||||
}
|
||||
|
||||
final UserGroupHolder usersAndGroups = userGroupHolder.get();
|
||||
|
||||
// ensure we are in a proper state to inherit the fingerprint
|
||||
if (!usersAndGroups.getAllUsers().isEmpty() || !usersAndGroups.getAllGroups().isEmpty()) {
|
||||
throw new UninheritableAuthorizationsException("Proposed fingerprint is not inheritable because the current users and groups is not empty.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFingerprint() throws AuthorizationAccessException {
|
||||
final UserGroupHolder usersAndGroups = userGroupHolder.get();
|
||||
|
||||
final List<User> users = new ArrayList<>(usersAndGroups.getAllUsers());
|
||||
Collections.sort(users, Comparator.comparing(User::getIdentifier));
|
||||
|
||||
final List<Group> groups = new ArrayList<>(usersAndGroups.getAllGroups());
|
||||
Collections.sort(groups, Comparator.comparing(Group::getIdentifier));
|
||||
|
||||
XMLStreamWriter writer = null;
|
||||
final StringWriter out = new StringWriter();
|
||||
try {
|
||||
writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
|
||||
writer.writeStartDocument();
|
||||
writer.writeStartElement("tenants");
|
||||
|
||||
for (User user : users) {
|
||||
writeUser(writer, user);
|
||||
}
|
||||
for (Group group : groups) {
|
||||
writeGroup(writer, group);
|
||||
}
|
||||
|
||||
writer.writeEndElement();
|
||||
writer.writeEndDocument();
|
||||
writer.flush();
|
||||
} catch (XMLStreamException e) {
|
||||
throw new AuthorizationAccessException("Unable to generate fingerprint", e);
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (XMLStreamException e) {
|
||||
// nothing to do here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private UsersAndGroups parseUsersAndGroups(final String fingerprint) {
|
||||
final List<User> users = new ArrayList<>();
|
||||
final List<Group> groups = new ArrayList<>();
|
||||
|
||||
final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
|
||||
try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) {
|
||||
final DocumentBuilder docBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
|
||||
final Document document = docBuilder.parse(in);
|
||||
final Element rootElement = document.getDocumentElement();
|
||||
|
||||
// parse all the users and add them to the current user group provider
|
||||
NodeList userNodes = rootElement.getElementsByTagName(USER_ELEMENT);
|
||||
for (int i=0; i < userNodes.getLength(); i++) {
|
||||
Node userNode = userNodes.item(i);
|
||||
users.add(parseUser((Element) userNode));
|
||||
}
|
||||
|
||||
// parse all the groups and add them to the current user group provider
|
||||
NodeList groupNodes = rootElement.getElementsByTagName(GROUP_ELEMENT);
|
||||
for (int i=0; i < groupNodes.getLength(); i++) {
|
||||
Node groupNode = groupNodes.item(i);
|
||||
groups.add(parseGroup((Element) groupNode));
|
||||
}
|
||||
} catch (SAXException | ParserConfigurationException | IOException e) {
|
||||
throw new AuthorizationAccessException("Unable to parse fingerprint", e);
|
||||
}
|
||||
|
||||
return new UsersAndGroups(users, groups);
|
||||
}
|
||||
|
||||
private User parseUser(final Element element) {
|
||||
final User.Builder builder = new User.Builder()
|
||||
.identifier(element.getAttribute(IDENTIFIER_ATTR))
|
||||
.identity(element.getAttribute(IDENTITY_ATTR));
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Group parseGroup(final Element element) {
|
||||
final Group.Builder builder = new Group.Builder()
|
||||
.identifier(element.getAttribute(IDENTIFIER_ATTR))
|
||||
.name(element.getAttribute(NAME_ATTR));
|
||||
|
||||
NodeList groupUsers = element.getElementsByTagName(GROUP_USER_ELEMENT);
|
||||
for (int i=0; i < groupUsers.getLength(); i++) {
|
||||
Element groupUserNode = (Element) groupUsers.item(i);
|
||||
builder.addUser(groupUserNode.getAttribute(IDENTIFIER_ATTR));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void writeUser(final XMLStreamWriter writer, final User user) throws XMLStreamException {
|
||||
writer.writeStartElement(USER_ELEMENT);
|
||||
writer.writeAttribute(IDENTIFIER_ATTR, user.getIdentifier());
|
||||
writer.writeAttribute(IDENTITY_ATTR, user.getIdentity());
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
private void writeGroup(final XMLStreamWriter writer, final Group group) throws XMLStreamException {
|
||||
List<String> users = new ArrayList<>(group.getUsers());
|
||||
Collections.sort(users);
|
||||
|
||||
writer.writeStartElement(GROUP_ELEMENT);
|
||||
writer.writeAttribute(IDENTIFIER_ATTR, group.getIdentifier());
|
||||
writer.writeAttribute(NAME_ATTR, group.getName());
|
||||
|
||||
for (String user : users) {
|
||||
writer.writeStartElement(GROUP_USER_ELEMENT);
|
||||
writer.writeAttribute(IDENTIFIER_ATTR, user);
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
private org.apache.nifi.authorization.file.tenants.generated.User createJAXBUser(User user) {
|
||||
final org.apache.nifi.authorization.file.tenants.generated.User jaxbUser = new org.apache.nifi.authorization.file.tenants.generated.User();
|
||||
jaxbUser.setIdentifier(user.getIdentifier());
|
||||
jaxbUser.setIdentity(user.getIdentity());
|
||||
return jaxbUser;
|
||||
}
|
||||
|
||||
private Set<org.apache.nifi.authorization.file.tenants.generated.User> checkGroupUsers(final Group group, final List<org.apache.nifi.authorization.file.tenants.generated.User> users) {
|
||||
final Set<org.apache.nifi.authorization.file.tenants.generated.User> jaxbUsers = new HashSet<>();
|
||||
for (String groupUser : group.getUsers()) {
|
||||
boolean found = false;
|
||||
for (org.apache.nifi.authorization.file.tenants.generated.User jaxbUser : users) {
|
||||
if (jaxbUser.getIdentifier().equals(groupUser)) {
|
||||
jaxbUsers.add(jaxbUser);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
throw new IllegalStateException("Unable to add group because user " + groupUser + " does not exist");
|
||||
}
|
||||
}
|
||||
return jaxbUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the authorizations file and populates the AuthorizationsHolder, only called during start-up.
|
||||
*
|
||||
* @throws JAXBException Unable to reload the authorized users file
|
||||
* @throws IllegalStateException Unable to sync file with restore
|
||||
* @throws SAXException Unable to unmarshall tenants
|
||||
*/
|
||||
private synchronized void load() throws JAXBException, IllegalStateException, SAXException {
|
||||
final Tenants tenants = unmarshallTenants();
|
||||
if (tenants.getUsers() == null) {
|
||||
tenants.setUsers(new Users());
|
||||
}
|
||||
if (tenants.getGroups() == null) {
|
||||
tenants.setGroups(new Groups());
|
||||
}
|
||||
|
||||
final UserGroupHolder userGroupHolder = new UserGroupHolder(tenants);
|
||||
final boolean emptyTenants = userGroupHolder.getAllUsers().isEmpty() && userGroupHolder.getAllGroups().isEmpty();
|
||||
final boolean hasLegacyAuthorizedUsers = (legacyAuthorizedUsersFile != null && !StringUtils.isBlank(legacyAuthorizedUsersFile));
|
||||
|
||||
if (emptyTenants) {
|
||||
if (hasLegacyAuthorizedUsers) {
|
||||
logger.info("Loading users from legacy model " + legacyAuthorizedUsersFile + " into new users file.");
|
||||
convertLegacyAuthorizedUsers(tenants);
|
||||
}
|
||||
|
||||
populateInitialUsers(tenants);
|
||||
|
||||
// save any changes that were made and repopulate the holder
|
||||
saveAndRefreshHolder(tenants);
|
||||
} else {
|
||||
this.userGroupHolder.set(userGroupHolder);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveTenants(final Tenants tenants) throws JAXBException {
|
||||
final Marshaller marshaller = JAXB_TENANTS_CONTEXT.createMarshaller();
|
||||
marshaller.setSchema(tenantsSchema);
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
|
||||
marshaller.marshal(tenants, tenantsFile);
|
||||
}
|
||||
|
||||
private Tenants unmarshallTenants() throws JAXBException {
|
||||
final Unmarshaller unmarshaller = JAXB_TENANTS_CONTEXT.createUnmarshaller();
|
||||
unmarshaller.setSchema(tenantsSchema);
|
||||
|
||||
final JAXBElement<Tenants> element = unmarshaller.unmarshal(new StreamSource(tenantsFile), Tenants.class);
|
||||
return element.getValue();
|
||||
}
|
||||
|
||||
private void populateInitialUsers(final Tenants tenants) {
|
||||
for (String initialUserIdentity : initialUserIdentities) {
|
||||
getOrCreateUser(tenants, initialUserIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshalls an existing authorized-users.xml and converts the object model to the new model.
|
||||
*
|
||||
* @param tenants the current Tenants instance users and groups will be added to
|
||||
* @throws AuthorizerCreationException if the legacy authorized users file that was provided does not exist
|
||||
* @throws JAXBException if the legacy authorized users file that was provided could not be unmarshalled
|
||||
*/
|
||||
private void convertLegacyAuthorizedUsers(final Tenants tenants) throws AuthorizerCreationException, JAXBException {
|
||||
final File authorizedUsersFile = new File(legacyAuthorizedUsersFile);
|
||||
if (!authorizedUsersFile.exists()) {
|
||||
throw new AuthorizerCreationException("Legacy Authorized Users File '" + legacyAuthorizedUsersFile + "' does not exists");
|
||||
}
|
||||
|
||||
final Unmarshaller unmarshaller = JAXB_USERS_CONTEXT.createUnmarshaller();
|
||||
unmarshaller.setSchema(usersSchema);
|
||||
|
||||
final JAXBElement<org.apache.nifi.user.generated.Users> element = unmarshaller.unmarshal(
|
||||
new StreamSource(authorizedUsersFile), org.apache.nifi.user.generated.Users.class);
|
||||
|
||||
final org.apache.nifi.user.generated.Users users = element.getValue();
|
||||
if (users.getUser().isEmpty()) {
|
||||
logger.info("Legacy Authorized Users File contained no users, nothing to convert");
|
||||
return;
|
||||
}
|
||||
|
||||
for (org.apache.nifi.user.generated.User legacyUser : users.getUser()) {
|
||||
// create the identifier of the new user based on the DN
|
||||
final String legacyUserDn = IdentityMappingUtil.mapIdentity(legacyUser.getDn(), identityMappings);
|
||||
org.apache.nifi.authorization.file.tenants.generated.User user = getOrCreateUser(tenants, legacyUserDn);
|
||||
|
||||
// if there was a group name find or create the group and add the user to it
|
||||
org.apache.nifi.authorization.file.tenants.generated.Group group = getOrCreateGroup(tenants, legacyUser.getGroup());
|
||||
if (group != null) {
|
||||
org.apache.nifi.authorization.file.tenants.generated.Group.User groupUser = new org.apache.nifi.authorization.file.tenants.generated.Group.User();
|
||||
groupUser.setIdentifier(user.getIdentifier());
|
||||
group.getUser().add(groupUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the User with the given identity, or creates a new one and adds it to the Tenants.
|
||||
*
|
||||
* @param tenants the Tenants reference
|
||||
* @param userIdentity the user identity to find or create
|
||||
* @return the User from Tenants with the given identity, or a new instance that was added to Tenants
|
||||
*/
|
||||
private org.apache.nifi.authorization.file.tenants.generated.User getOrCreateUser(final Tenants tenants, final String userIdentity) {
|
||||
if (StringUtils.isBlank(userIdentity)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
org.apache.nifi.authorization.file.tenants.generated.User foundUser = null;
|
||||
for (org.apache.nifi.authorization.file.tenants.generated.User user : tenants.getUsers().getUser()) {
|
||||
if (user.getIdentity().equals(userIdentity)) {
|
||||
foundUser = user;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundUser == null) {
|
||||
final String userIdentifier = IdentifierUtil.getIdentifier(userIdentity);
|
||||
foundUser = new org.apache.nifi.authorization.file.tenants.generated.User();
|
||||
foundUser.setIdentifier(userIdentifier);
|
||||
foundUser.setIdentity(userIdentity);
|
||||
tenants.getUsers().getUser().add(foundUser);
|
||||
}
|
||||
|
||||
return foundUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the Group with the given name, or creates a new one and adds it to Tenants.
|
||||
*
|
||||
* @param tenants the Tenants reference
|
||||
* @param groupName the name of the group to look for
|
||||
* @return the Group from Tenants with the given name, or a new instance that was added to Tenants
|
||||
*/
|
||||
private org.apache.nifi.authorization.file.tenants.generated.Group getOrCreateGroup(final Tenants tenants, final String groupName) {
|
||||
if (StringUtils.isBlank(groupName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
org.apache.nifi.authorization.file.tenants.generated.Group foundGroup = null;
|
||||
for (org.apache.nifi.authorization.file.tenants.generated.Group group : tenants.getGroups().getGroup()) {
|
||||
if (group.getName().equals(groupName)) {
|
||||
foundGroup = group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundGroup == null) {
|
||||
final String newGroupIdentifier = IdentifierUtil.getIdentifier(groupName);
|
||||
foundGroup = new org.apache.nifi.authorization.file.tenants.generated.Group();
|
||||
foundGroup.setIdentifier(newGroupIdentifier);
|
||||
foundGroup.setName(groupName);
|
||||
tenants.getGroups().getGroup().add(foundGroup);
|
||||
}
|
||||
|
||||
return foundGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the Authorizations instance by marshalling to a file, then re-populates the
|
||||
* in-memory data structures and sets the new holder.
|
||||
*
|
||||
* Synchronized to ensure only one thread writes the file at a time.
|
||||
*
|
||||
* @param tenants the tenants to save and populate from
|
||||
* @throws AuthorizationAccessException if an error occurs saving the authorizations
|
||||
*/
|
||||
private synchronized void saveAndRefreshHolder(final Tenants tenants) throws AuthorizationAccessException {
|
||||
try {
|
||||
saveTenants(tenants);
|
||||
|
||||
this.userGroupHolder.set(new UserGroupHolder(tenants));
|
||||
} catch (JAXBException e) {
|
||||
throw new AuthorizationAccessException("Unable to save Authorizations", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
}
|
||||
|
||||
private static class UsersAndGroups {
|
||||
final List<User> users;
|
||||
final List<Group> groups;
|
||||
|
||||
public UsersAndGroups(List<User> users, List<Group> groups) {
|
||||
this.users = users;
|
||||
this.groups = groups;
|
||||
}
|
||||
|
||||
public List<User> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public List<Group> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.util.StringUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class IdentifierUtil {
|
||||
|
||||
static String getIdentifier(final String seed) {
|
||||
if (StringUtils.isBlank(seed)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)).toString();
|
||||
}
|
||||
|
||||
private IdentifierUtil() {}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* 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.file.tenants.generated.Groups;
|
||||
import org.apache.nifi.authorization.file.tenants.generated.Tenants;
|
||||
import org.apache.nifi.authorization.file.tenants.generated.Users;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A holder to provide atomic access to user group data structures.
|
||||
*/
|
||||
public class UserGroupHolder {
|
||||
|
||||
private final Tenants tenants;
|
||||
|
||||
private final Set<User> allUsers;
|
||||
private final Map<String,User> usersById;
|
||||
private final Map<String,User> usersByIdentity;
|
||||
|
||||
private final Set<Group> allGroups;
|
||||
private final Map<String,Group> groupsById;
|
||||
private final Map<String, Set<Group>> groupsByUserIdentity;
|
||||
|
||||
/**
|
||||
* Creates a new holder and populates all convenience data structures.
|
||||
*
|
||||
* @param tenants the current tenants instance
|
||||
*/
|
||||
public UserGroupHolder(final Tenants tenants) {
|
||||
this.tenants = tenants;
|
||||
|
||||
// load all users
|
||||
final Users users = tenants.getUsers();
|
||||
final Set<User> allUsers = Collections.unmodifiableSet(createUsers(users));
|
||||
|
||||
// load all groups
|
||||
final Groups groups = tenants.getGroups();
|
||||
final Set<Group> allGroups = Collections.unmodifiableSet(createGroups(groups, users));
|
||||
|
||||
// create a convenience map to retrieve a user by id
|
||||
final Map<String, User> userByIdMap = Collections.unmodifiableMap(createUserByIdMap(allUsers));
|
||||
|
||||
// create a convenience map to retrieve a user by identity
|
||||
final Map<String, User> userByIdentityMap = Collections.unmodifiableMap(createUserByIdentityMap(allUsers));
|
||||
|
||||
// create a convenience map to retrieve a group by id
|
||||
final Map<String, Group> groupByIdMap = Collections.unmodifiableMap(createGroupByIdMap(allGroups));
|
||||
|
||||
// create a convenience map to retrieve the groups for a user identity
|
||||
final Map<String, Set<Group>> groupsByUserIdentityMap = Collections.unmodifiableMap(createGroupsByUserIdentityMap(allGroups, allUsers));
|
||||
|
||||
// set all the holders
|
||||
this.allUsers = allUsers;
|
||||
this.allGroups = allGroups;
|
||||
this.usersById = userByIdMap;
|
||||
this.usersByIdentity = userByIdentityMap;
|
||||
this.groupsById = groupByIdMap;
|
||||
this.groupsByUserIdentity = groupsByUserIdentityMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a set of Users from the JAXB Users.
|
||||
*
|
||||
* @param users the JAXB Users
|
||||
* @return a set of API Users matching the provided JAXB Users
|
||||
*/
|
||||
private Set<User> createUsers(Users users) {
|
||||
Set<User> allUsers = new HashSet<>();
|
||||
if (users == null || users.getUser() == null) {
|
||||
return allUsers;
|
||||
}
|
||||
|
||||
for (org.apache.nifi.authorization.file.tenants.generated.User user : users.getUser()) {
|
||||
final User.Builder builder = new User.Builder()
|
||||
.identity(user.getIdentity())
|
||||
.identifier(user.getIdentifier());
|
||||
|
||||
allUsers.add(builder.build());
|
||||
}
|
||||
|
||||
return allUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a set of Groups from the JAXB Groups.
|
||||
*
|
||||
* @param groups the JAXB Groups
|
||||
* @return a set of API Groups matching the provided JAXB Groups
|
||||
*/
|
||||
private Set<Group> createGroups(Groups groups,
|
||||
Users users) {
|
||||
Set<Group> allGroups = new HashSet<>();
|
||||
if (groups == null || groups.getGroup() == null) {
|
||||
return allGroups;
|
||||
}
|
||||
|
||||
for (org.apache.nifi.authorization.file.tenants.generated.Group group : groups.getGroup()) {
|
||||
final Group.Builder builder = new Group.Builder()
|
||||
.identifier(group.getIdentifier())
|
||||
.name(group.getName());
|
||||
|
||||
for (org.apache.nifi.authorization.file.tenants.generated.Group.User groupUser : group.getUser()) {
|
||||
builder.addUser(groupUser.getIdentifier());
|
||||
}
|
||||
|
||||
allGroups.add(builder.build());
|
||||
}
|
||||
|
||||
return allGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Map from user identifier to User.
|
||||
*
|
||||
* @param users the set of all users
|
||||
* @return the Map from user identifier to User
|
||||
*/
|
||||
private Map<String,User> createUserByIdMap(final Set<User> users) {
|
||||
Map<String,User> usersMap = new HashMap<>();
|
||||
for (User user : users) {
|
||||
usersMap.put(user.getIdentifier(), user);
|
||||
}
|
||||
return usersMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Map from user identity to User.
|
||||
*
|
||||
* @param users the set of all users
|
||||
* @return the Map from user identity to User
|
||||
*/
|
||||
private Map<String,User> createUserByIdentityMap(final Set<User> users) {
|
||||
Map<String,User> usersMap = new HashMap<>();
|
||||
for (User user : users) {
|
||||
usersMap.put(user.getIdentity(), user);
|
||||
}
|
||||
return usersMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Map from group identifier to Group.
|
||||
*
|
||||
* @param groups the set of all groups
|
||||
* @return the Map from group identifier to Group
|
||||
*/
|
||||
private Map<String,Group> createGroupByIdMap(final Set<Group> groups) {
|
||||
Map<String,Group> groupsMap = new HashMap<>();
|
||||
for (Group group : groups) {
|
||||
groupsMap.put(group.getIdentifier(), group);
|
||||
}
|
||||
return groupsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Map from user identity to the set of Groups for that identity.
|
||||
*
|
||||
* @param groups all groups
|
||||
* @param users all users
|
||||
* @return a Map from User identity to the set of Groups for that identity
|
||||
*/
|
||||
private Map<String, Set<Group>> createGroupsByUserIdentityMap(final Set<Group> groups, final Set<User> users) {
|
||||
Map<String, Set<Group>> groupsByUserIdentity = new HashMap<>();
|
||||
|
||||
for (User user : users) {
|
||||
Set<Group> userGroups = new HashSet<>();
|
||||
for (Group group : groups) {
|
||||
for (String groupUser : group.getUsers()) {
|
||||
if (groupUser.equals(user.getIdentifier())) {
|
||||
userGroups.add(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
groupsByUserIdentity.put(user.getIdentity(), userGroups);
|
||||
}
|
||||
|
||||
return groupsByUserIdentity;
|
||||
}
|
||||
|
||||
public Tenants getTenants() {
|
||||
return tenants;
|
||||
}
|
||||
|
||||
public Set<User> getAllUsers() {
|
||||
return allUsers;
|
||||
}
|
||||
|
||||
public Map<String, User> getUsersById() {
|
||||
return usersById;
|
||||
}
|
||||
|
||||
public Map<String, User> getUsersByIdentity() {
|
||||
return usersByIdentity;
|
||||
}
|
||||
|
||||
public Set<Group> getAllGroups() {
|
||||
return allGroups;
|
||||
}
|
||||
|
||||
public Map<String, Group> getGroupsById() {
|
||||
return groupsById;
|
||||
}
|
||||
|
||||
public User getUser(String identity) {
|
||||
if (identity == null) {
|
||||
throw new IllegalArgumentException("Identity cannot be null");
|
||||
}
|
||||
return usersByIdentity.get(identity);
|
||||
}
|
||||
|
||||
public Set<Group> getGroups(String userIdentity) {
|
||||
if (userIdentity == null) {
|
||||
throw new IllegalArgumentException("User Identity cannot be null");
|
||||
}
|
||||
return groupsByUserIdentity.get(userIdentity);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
org.apache.nifi.authorization.FileAccessPolicyProvider
|
|
@ -0,0 +1,15 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
org.apache.nifi.authorization.FileUserGroupProvider
|
File diff suppressed because it is too large
Load Diff
|
@ -21,10 +21,10 @@ import org.apache.nifi.authorization.AuthorizationResult.Result;
|
|||
import org.apache.nifi.authorization.exception.AuthorizerCreationException;
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory;
|
||||
import org.apache.nifi.authorization.resource.ResourceType;
|
||||
import org.apache.nifi.components.PropertyValue;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.util.file.FileUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
@ -46,6 +46,7 @@ import static org.junit.Assert.assertFalse;
|
|||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
@ -188,8 +189,44 @@ public class FileAuthorizerTest {
|
|||
when(properties.getFlowConfigurationFile()).thenReturn(flow);
|
||||
|
||||
configurationContext = mock(AuthorizerConfigurationContext.class);
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAuthorizer.PROP_AUTHORIZATIONS_FILE))).thenReturn(new StandardPropertyValue(primaryAuthorizations.getPath(), null));
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAuthorizer.PROP_TENANTS_FILE))).thenReturn(new StandardPropertyValue(primaryTenants.getPath(), null));
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_AUTHORIZATIONS_FILE))).thenReturn(new StandardPropertyValue(primaryAuthorizations.getPath(), null));
|
||||
when(configurationContext.getProperty(Mockito.eq(FileUserGroupProvider.PROP_TENANTS_FILE))).thenReturn(new StandardPropertyValue(primaryTenants.getPath(), null));
|
||||
when(configurationContext.getProperties()).then((invocation) -> {
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
|
||||
final PropertyValue authFile = configurationContext.getProperty(FileAccessPolicyProvider.PROP_AUTHORIZATIONS_FILE);
|
||||
if (authFile != null) {
|
||||
properties.put(FileAccessPolicyProvider.PROP_AUTHORIZATIONS_FILE, authFile.getValue());
|
||||
}
|
||||
|
||||
final PropertyValue tenantFile = configurationContext.getProperty(FileUserGroupProvider.PROP_TENANTS_FILE);
|
||||
if (tenantFile != null) {
|
||||
properties.put(FileUserGroupProvider.PROP_TENANTS_FILE, tenantFile.getValue());
|
||||
}
|
||||
|
||||
final PropertyValue legacyAuthFile = configurationContext.getProperty(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE);
|
||||
if (legacyAuthFile != null) {
|
||||
properties.put(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE, legacyAuthFile.getValue());
|
||||
}
|
||||
|
||||
final PropertyValue initialAdmin = configurationContext.getProperty(FileAccessPolicyProvider.PROP_INITIAL_ADMIN_IDENTITY);
|
||||
if (initialAdmin != null) {
|
||||
properties.put(FileAccessPolicyProvider.PROP_INITIAL_ADMIN_IDENTITY, initialAdmin.getValue());
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
while (true) {
|
||||
final String key = FileAccessPolicyProvider.PROP_NODE_IDENTITY_PREFIX + i++;
|
||||
final PropertyValue value = configurationContext.getProperty(key);
|
||||
if (value == null) {
|
||||
break;
|
||||
} else {
|
||||
properties.put(key, value.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return properties;
|
||||
});
|
||||
|
||||
authorizer = new FileAuthorizer();
|
||||
authorizer.setNiFiProperties(properties);
|
||||
|
@ -464,7 +501,7 @@ public class FileAuthorizerTest {
|
|||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testOnConfiguredWhenInitialAdminAndLegacyUsersProvided() throws Exception {
|
||||
final String adminIdentity = "admin-user";
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAuthorizer.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE)))
|
||||
|
@ -492,7 +529,7 @@ public class FileAuthorizerTest {
|
|||
public void testOnConfiguredWhenInitialAdminProvided() throws Exception {
|
||||
final String adminIdentity = "admin-user";
|
||||
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAuthorizer.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
|
||||
writeFile(primaryAuthorizations, EMPTY_AUTHORIZATIONS_CONCISE);
|
||||
|
@ -530,7 +567,7 @@ public class FileAuthorizerTest {
|
|||
authorizer.setNiFiProperties(properties);
|
||||
|
||||
final String adminIdentity = "admin-user";
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAuthorizer.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
|
||||
writeFile(primaryAuthorizations, EMPTY_AUTHORIZATIONS_CONCISE);
|
||||
|
@ -568,7 +605,7 @@ public class FileAuthorizerTest {
|
|||
authorizer.setNiFiProperties(properties);
|
||||
|
||||
final String adminIdentity = "admin-user";
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAuthorizer.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
|
||||
writeFile(primaryAuthorizations, EMPTY_AUTHORIZATIONS_CONCISE);
|
||||
|
@ -609,7 +646,7 @@ public class FileAuthorizerTest {
|
|||
authorizer.setNiFiProperties(properties);
|
||||
|
||||
final String adminIdentity = "CN=localhost, OU=Apache NiFi, O=Apache, L=Santa Monica, ST=CA, C=US";
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAuthorizer.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
|
||||
writeFile(primaryAuthorizations, EMPTY_AUTHORIZATIONS_CONCISE);
|
||||
|
@ -626,18 +663,15 @@ public class FileAuthorizerTest {
|
|||
@Test
|
||||
public void testOnConfiguredWhenNodeIdentitiesProvided() throws Exception {
|
||||
final String adminIdentity = "admin-user";
|
||||
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAuthorizer.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
|
||||
final String nodeIdentity1 = "node1";
|
||||
final String nodeIdentity2 = "node2";
|
||||
|
||||
final Map<String,String> props = new HashMap<>();
|
||||
props.put("Node Identity 1", nodeIdentity1);
|
||||
props.put("Node Identity 2", nodeIdentity2);
|
||||
|
||||
when(configurationContext.getProperties()).thenReturn(props);
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_NODE_IDENTITY_PREFIX + "1")))
|
||||
.thenReturn(new StandardPropertyValue(nodeIdentity1, null));
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_NODE_IDENTITY_PREFIX + "2")))
|
||||
.thenReturn(new StandardPropertyValue(nodeIdentity2, null));
|
||||
|
||||
writeFile(primaryAuthorizations, EMPTY_AUTHORIZATIONS_CONCISE);
|
||||
writeFile(primaryTenants, EMPTY_TENANTS_CONCISE);
|
||||
|
@ -662,18 +696,15 @@ public class FileAuthorizerTest {
|
|||
@Test
|
||||
public void testOnConfiguredWhenNodeIdentitiesProvidedAndUsersAlreadyExist() throws Exception {
|
||||
final String adminIdentity = "admin-user";
|
||||
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAuthorizer.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
|
||||
final String nodeIdentity1 = "node1";
|
||||
final String nodeIdentity2 = "node2";
|
||||
|
||||
final Map<String,String> props = new HashMap<>();
|
||||
props.put("Node Identity 1", nodeIdentity1);
|
||||
props.put("Node Identity 2", nodeIdentity2);
|
||||
|
||||
when(configurationContext.getProperties()).thenReturn(props);
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_NODE_IDENTITY_PREFIX + "1")))
|
||||
.thenReturn(new StandardPropertyValue(nodeIdentity1, null));
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_NODE_IDENTITY_PREFIX + "2")))
|
||||
.thenReturn(new StandardPropertyValue(nodeIdentity2, null));
|
||||
|
||||
writeFile(primaryAuthorizations, EMPTY_AUTHORIZATIONS_CONCISE);
|
||||
writeFile(primaryTenants, TENANTS_FOR_ADMIN_AND_NODES);
|
||||
|
@ -709,17 +740,15 @@ public class FileAuthorizerTest {
|
|||
authorizer.setNiFiProperties(properties);
|
||||
|
||||
final String adminIdentity = "CN=user1, OU=Apache NiFi, O=Apache, L=Santa Monica, ST=CA, C=US";
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAuthorizer.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
|
||||
final String nodeIdentity1 = "CN=node1, OU=Apache NiFi, O=Apache, L=Santa Monica, ST=CA, C=US";
|
||||
final String nodeIdentity2 = "CN=node2, OU=Apache NiFi, O=Apache, L=Santa Monica, ST=CA, C=US";
|
||||
|
||||
final Map<String,String> nodeProps = new HashMap<>();
|
||||
nodeProps.put("Node Identity 1", nodeIdentity1);
|
||||
nodeProps.put("Node Identity 2", nodeIdentity2);
|
||||
|
||||
when(configurationContext.getProperties()).thenReturn(nodeProps);
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_INITIAL_ADMIN_IDENTITY)))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_NODE_IDENTITY_PREFIX + "1")))
|
||||
.thenReturn(new StandardPropertyValue(nodeIdentity1, null));
|
||||
when(configurationContext.getProperty(Mockito.eq(FileAccessPolicyProvider.PROP_NODE_IDENTITY_PREFIX + "2")))
|
||||
.thenReturn(new StandardPropertyValue(nodeIdentity2, null));
|
||||
|
||||
writeFile(primaryAuthorizations, EMPTY_AUTHORIZATIONS_CONCISE);
|
||||
writeFile(primaryTenants, EMPTY_TENANTS_CONCISE);
|
||||
|
@ -735,9 +764,12 @@ public class FileAuthorizerTest {
|
|||
assertNotNull(nodeUser2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnConfiguredWhenTenantsAndAuthorizationsFileDoesNotExist() {
|
||||
authorizer.onConfigured(configurationContext);
|
||||
assertEquals(0, authorizer.getAccessPolicies().size());
|
||||
assertEquals(0, authorizer.getUsers().size());
|
||||
assertEquals(0, authorizer.getGroups().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -745,6 +777,8 @@ public class FileAuthorizerTest {
|
|||
writeFile(primaryTenants, EMPTY_TENANTS_CONCISE);
|
||||
authorizer.onConfigured(configurationContext);
|
||||
assertEquals(0, authorizer.getAccessPolicies().size());
|
||||
assertEquals(0, authorizer.getUsers().size());
|
||||
assertEquals(0, authorizer.getGroups().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -752,6 +786,8 @@ public class FileAuthorizerTest {
|
|||
writeFile(primaryAuthorizations, EMPTY_AUTHORIZATIONS_CONCISE);
|
||||
authorizer.onConfigured(configurationContext);
|
||||
assertEquals(0, authorizer.getAccessPolicies().size());
|
||||
assertEquals(0, authorizer.getUsers().size());
|
||||
assertEquals(0, authorizer.getGroups().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1007,9 +1043,6 @@ public class FileAuthorizerTest {
|
|||
final User user = authorizer.getUser("user-1");
|
||||
assertEquals("user-1", user.getIdentifier());
|
||||
|
||||
final AccessPolicy policy1 = authorizer.getAccessPolicy("policy-1");
|
||||
assertTrue(policy1.getUsers().contains("user-1"));
|
||||
|
||||
// delete user-1
|
||||
final User deletedUser = authorizer.deleteUser(user);
|
||||
assertNotNull(deletedUser);
|
||||
|
@ -1018,10 +1051,6 @@ public class FileAuthorizerTest {
|
|||
// should be one less user
|
||||
assertEquals(1, authorizer.getUsers().size());
|
||||
assertNull(authorizer.getUser(user.getIdentifier()));
|
||||
|
||||
// verify policy-1 no longer has a reference to user-1
|
||||
final AccessPolicy updatedPolicy1 = authorizer.getAccessPolicy("policy-1");
|
||||
assertFalse(updatedPolicy1.getUsers().contains("user-1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1121,7 +1150,6 @@ public class FileAuthorizerTest {
|
|||
assertEquals(3, groups.size());
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testAddGroupWhenUserDoesNotExist() throws Exception {
|
||||
writeFile(primaryAuthorizations, EMPTY_AUTHORIZATIONS);
|
||||
|
@ -1170,9 +1198,6 @@ public class FileAuthorizerTest {
|
|||
authorizer.onConfigured(configurationContext);
|
||||
assertEquals(2, authorizer.getGroups().size());
|
||||
|
||||
final AccessPolicy policy1 = authorizer.getAccessPolicy("policy-1");
|
||||
assertTrue(policy1.getGroups().contains("group-1"));
|
||||
|
||||
// retrieve group-1
|
||||
final Group group = authorizer.getGroup("group-1");
|
||||
assertEquals("group-1", group.getIdentifier());
|
||||
|
@ -1187,10 +1212,6 @@ public class FileAuthorizerTest {
|
|||
|
||||
// verify we can no longer retrieve group-1 by identifier
|
||||
assertNull(authorizer.getGroup(group.getIdentifier()));
|
||||
|
||||
// verify group-1 is no longer in policy-1
|
||||
final AccessPolicy updatedPolicy1 = authorizer.getAccessPolicy("policy-1");
|
||||
assertFalse(updatedPolicy1.getGroups().contains("group-1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1289,9 +1310,11 @@ public class FileAuthorizerTest {
|
|||
.action(RequestAction.READ)
|
||||
.build();
|
||||
|
||||
final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) AuthorizerFactory.installIntegrityChecks(authorizer);
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProviderWithChecks = (ConfigurableAccessPolicyProvider) managedAuthorizer.getAccessPolicyProvider();
|
||||
try {
|
||||
final AccessPolicy returnedPolicy2 = authorizer.addAccessPolicy(policy2);
|
||||
Assert.fail("Should have thrown exception");
|
||||
final AccessPolicy returnedPolicy2 = accessPolicyProviderWithChecks.addAccessPolicy(policy2);
|
||||
fail("Should have thrown exception");
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,698 @@
|
|||
/*
|
||||
* 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.attribute.expression.language.StandardPropertyValue;
|
||||
import org.apache.nifi.authorization.exception.AuthorizerCreationException;
|
||||
import org.apache.nifi.components.PropertyValue;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.util.file.FileUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class FileUserGroupProviderTest {
|
||||
|
||||
private static final String EMPTY_TENANTS_CONCISE =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
|
||||
+ "<tenants/>";
|
||||
|
||||
private static final String EMPTY_TENANTS =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
|
||||
+ "<tenants>"
|
||||
+ "</tenants>";
|
||||
|
||||
private static final String BAD_SCHEMA_TENANTS =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
|
||||
+ "<tenant>"
|
||||
+ "</tenant>";
|
||||
|
||||
private static final String SIMPLE_TENANTS_BY_USER =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
|
||||
"<tenants>" +
|
||||
" <users>" +
|
||||
" <user identifier=\"user-1\" identity=\"user-1\"/>" +
|
||||
" <user identifier=\"user-2\" identity=\"user-2\"/>" +
|
||||
" </users>" +
|
||||
"</tenants>";
|
||||
|
||||
private static final String TENANTS =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
|
||||
"<tenants>" +
|
||||
" <groups>" +
|
||||
" <group identifier=\"group-1\" name=\"group-1\">" +
|
||||
" <user identifier=\"user-1\" />" +
|
||||
" </group>" +
|
||||
" <group identifier=\"group-2\" name=\"group-2\">" +
|
||||
" <user identifier=\"user-2\" />" +
|
||||
" </group>" +
|
||||
" </groups>" +
|
||||
" <users>" +
|
||||
" <user identifier=\"user-1\" identity=\"user-1\" />" +
|
||||
" <user identifier=\"user-2\" identity=\"user-2\" />" +
|
||||
" </users>" +
|
||||
"</tenants>";
|
||||
|
||||
private NiFiProperties properties;
|
||||
private FileUserGroupProvider userGroupProvider;
|
||||
private File primaryTenants;
|
||||
private File restoreTenants;
|
||||
|
||||
private AuthorizerConfigurationContext configurationContext;
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
// primary tenants
|
||||
primaryTenants = new File("target/authorizations/users.xml");
|
||||
FileUtils.ensureDirectoryExistAndCanAccess(primaryTenants.getParentFile());
|
||||
|
||||
// restore authorizations
|
||||
restoreTenants = new File("target/restore/users.xml");
|
||||
FileUtils.ensureDirectoryExistAndCanAccess(restoreTenants.getParentFile());
|
||||
|
||||
properties = mock(NiFiProperties.class);
|
||||
when(properties.getRestoreDirectory()).thenReturn(restoreTenants.getParentFile());
|
||||
|
||||
configurationContext = mock(AuthorizerConfigurationContext.class);
|
||||
when(configurationContext.getProperty(eq(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE))).thenReturn(new StandardPropertyValue(null, null));
|
||||
when(configurationContext.getProperty(eq(FileUserGroupProvider.PROP_TENANTS_FILE))).thenReturn(new StandardPropertyValue(primaryTenants.getPath(), null));
|
||||
when(configurationContext.getProperties()).then((invocation) -> {
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
|
||||
final PropertyValue tenantFile = configurationContext.getProperty(FileUserGroupProvider.PROP_TENANTS_FILE);
|
||||
if (tenantFile != null) {
|
||||
properties.put(FileUserGroupProvider.PROP_TENANTS_FILE, tenantFile.getValue());
|
||||
}
|
||||
|
||||
final PropertyValue legacyAuthFile = configurationContext.getProperty(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE);
|
||||
if (legacyAuthFile != null) {
|
||||
properties.put(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE, legacyAuthFile.getValue());
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
while (true) {
|
||||
final String key = FileUserGroupProvider.PROP_INITIAL_USER_IDENTITY_PREFIX + i++;
|
||||
final PropertyValue value = configurationContext.getProperty(key);
|
||||
if (value == null) {
|
||||
break;
|
||||
} else {
|
||||
properties.put(key, value.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return properties;
|
||||
});
|
||||
|
||||
userGroupProvider = new FileUserGroupProvider();
|
||||
userGroupProvider.setNiFiProperties(properties);
|
||||
userGroupProvider.initialize(null);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() throws Exception {
|
||||
deleteFile(primaryTenants);
|
||||
deleteFile(restoreTenants);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnConfiguredWhenLegacyUsersFileProvided() throws Exception {
|
||||
when(configurationContext.getProperty(eq(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE)))
|
||||
.thenReturn(new StandardPropertyValue("src/test/resources/authorized-users.xml", null));
|
||||
|
||||
writeFile(primaryTenants, EMPTY_TENANTS_CONCISE);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
|
||||
// verify all users got created correctly
|
||||
final Set<User> users = userGroupProvider.getUsers();
|
||||
assertEquals(6, users.size());
|
||||
|
||||
final User user1 = userGroupProvider.getUserByIdentity("user1");
|
||||
assertNotNull(user1);
|
||||
|
||||
final User user2 = userGroupProvider.getUserByIdentity("user2");
|
||||
assertNotNull(user2);
|
||||
|
||||
final User user3 = userGroupProvider.getUserByIdentity("user3");
|
||||
assertNotNull(user3);
|
||||
|
||||
final User user4 = userGroupProvider.getUserByIdentity("user4");
|
||||
assertNotNull(user4);
|
||||
|
||||
final User user5 = userGroupProvider.getUserByIdentity("user5");
|
||||
assertNotNull(user5);
|
||||
|
||||
final User user6 = userGroupProvider.getUserByIdentity("user6");
|
||||
assertNotNull(user6);
|
||||
|
||||
// verify one group got created
|
||||
final Set<Group> groups = userGroupProvider.getGroups();
|
||||
assertEquals(1, groups.size());
|
||||
final Group group1 = groups.iterator().next();
|
||||
assertEquals("group1", group1.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnConfiguredWhenLegacyUsersFileProvidedWithIdentityMappings() throws Exception {
|
||||
final Properties props = new Properties();
|
||||
props.setProperty("nifi.security.identity.mapping.pattern.dn1", "^CN=(.*?), OU=(.*?), O=(.*?), L=(.*?), ST=(.*?), C=(.*?)$");
|
||||
props.setProperty("nifi.security.identity.mapping.value.dn1", "$1");
|
||||
|
||||
properties = getNiFiProperties(props);
|
||||
when(properties.getRestoreDirectory()).thenReturn(restoreTenants.getParentFile());
|
||||
userGroupProvider.setNiFiProperties(properties);
|
||||
|
||||
when(configurationContext.getProperty(eq(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE)))
|
||||
.thenReturn(new StandardPropertyValue("src/test/resources/authorized-users-with-dns.xml", null));
|
||||
|
||||
writeFile(primaryTenants, EMPTY_TENANTS_CONCISE);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
|
||||
final User user1 = userGroupProvider.getUserByIdentity("user1");
|
||||
assertNotNull(user1);
|
||||
|
||||
final User user2 = userGroupProvider.getUserByIdentity("user2");
|
||||
assertNotNull(user2);
|
||||
|
||||
final User user3 = userGroupProvider.getUserByIdentity("user3");
|
||||
assertNotNull(user3);
|
||||
|
||||
final User user4 = userGroupProvider.getUserByIdentity("user4");
|
||||
assertNotNull(user4);
|
||||
|
||||
final User user5 = userGroupProvider.getUserByIdentity("user5");
|
||||
assertNotNull(user5);
|
||||
|
||||
final User user6 = userGroupProvider.getUserByIdentity("user6");
|
||||
assertNotNull(user6);
|
||||
|
||||
// verify one group got created
|
||||
final Set<Group> groups = userGroupProvider.getGroups();
|
||||
assertEquals(1, groups.size());
|
||||
final Group group1 = groups.iterator().next();
|
||||
assertEquals("group1", group1.getName());
|
||||
}
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testOnConfiguredWhenBadLegacyUsersFileProvided() throws Exception {
|
||||
when(configurationContext.getProperty(eq(FileAuthorizer.PROP_LEGACY_AUTHORIZED_USERS_FILE)))
|
||||
.thenReturn(new StandardPropertyValue("src/test/resources/does-not-exist.xml", null));
|
||||
|
||||
writeFile(primaryTenants, EMPTY_TENANTS_CONCISE);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnConfiguredWhenInitialUsersNotProvided() throws Exception {
|
||||
writeFile(primaryTenants, EMPTY_TENANTS_CONCISE);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
|
||||
final Set<User> users = userGroupProvider.getUsers();
|
||||
assertEquals(0, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnConfiguredWhenInitialUsersProvided() throws Exception {
|
||||
final String adminIdentity = "admin-user";
|
||||
final String nodeIdentity1 = "node-identity-1";
|
||||
final String nodeIdentity2 = "node-identity-2";
|
||||
|
||||
when(configurationContext.getProperty(eq(FileUserGroupProvider.PROP_INITIAL_USER_IDENTITY_PREFIX + "1")))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
when(configurationContext.getProperty(eq(FileUserGroupProvider.PROP_INITIAL_USER_IDENTITY_PREFIX + "2")))
|
||||
.thenReturn(new StandardPropertyValue(nodeIdentity1, null));
|
||||
when(configurationContext.getProperty(eq(FileUserGroupProvider.PROP_INITIAL_USER_IDENTITY_PREFIX + "3")))
|
||||
.thenReturn(new StandardPropertyValue(nodeIdentity2, null));
|
||||
|
||||
writeFile(primaryTenants, EMPTY_TENANTS_CONCISE);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
|
||||
final Set<User> users = userGroupProvider.getUsers();
|
||||
assertEquals(3, users.size());
|
||||
|
||||
assertTrue(users.contains(new User.Builder().identifierGenerateFromSeed(adminIdentity).identity(adminIdentity).build()));
|
||||
assertTrue(users.contains(new User.Builder().identifierGenerateFromSeed(nodeIdentity1).identity(nodeIdentity1).build()));
|
||||
assertTrue(users.contains(new User.Builder().identifierGenerateFromSeed(nodeIdentity2).identity(nodeIdentity2).build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnConfiguredWhenTenantsExistAndInitialUsersProvided() throws Exception {
|
||||
final String adminIdentity = "admin-user";
|
||||
final String nodeIdentity1 = "node-identity-1";
|
||||
final String nodeIdentity2 = "node-identity-2";
|
||||
|
||||
// despite setting initial users, they will not be loaded as the tenants file is non-empty
|
||||
when(configurationContext.getProperty(eq(FileUserGroupProvider.PROP_INITIAL_USER_IDENTITY_PREFIX + "1")))
|
||||
.thenReturn(new StandardPropertyValue(adminIdentity, null));
|
||||
when(configurationContext.getProperty(eq(FileUserGroupProvider.PROP_INITIAL_USER_IDENTITY_PREFIX + "2")))
|
||||
.thenReturn(new StandardPropertyValue(nodeIdentity1, null));
|
||||
when(configurationContext.getProperty(eq(FileUserGroupProvider.PROP_INITIAL_USER_IDENTITY_PREFIX + "3")))
|
||||
.thenReturn(new StandardPropertyValue(nodeIdentity2, null));
|
||||
|
||||
writeFile(primaryTenants, SIMPLE_TENANTS_BY_USER);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
|
||||
final Set<User> users = userGroupProvider.getUsers();
|
||||
assertEquals(2, users.size());
|
||||
|
||||
assertTrue(users.contains(new User.Builder().identifier("user-1").identity("user-1").build()));
|
||||
assertTrue(users.contains(new User.Builder().identifier("user-2").identity("user-2").build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnConfiguredWhenTenantsFileDoesNotExist() throws Exception {
|
||||
writeFile(primaryTenants, EMPTY_TENANTS_CONCISE);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(0, userGroupProvider.getUsers().size());
|
||||
assertEquals(0, userGroupProvider.getGroups().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnConfiguredWhenRestoreDoesNotExist() throws Exception {
|
||||
writeFile(primaryTenants, EMPTY_TENANTS_CONCISE);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
|
||||
assertEquals(primaryTenants.length(), restoreTenants.length());
|
||||
}
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testOnConfiguredWhenPrimaryDoesNotExist() throws Exception {
|
||||
writeFile(restoreTenants, EMPTY_TENANTS_CONCISE);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testOnConfiguredWhenPrimaryTenantsDifferentThanRestore() throws Exception {
|
||||
writeFile(primaryTenants, EMPTY_TENANTS);
|
||||
writeFile(restoreTenants, EMPTY_TENANTS_CONCISE);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
}
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testOnConfiguredWithBadTenantsSchema() throws Exception {
|
||||
writeFile(primaryTenants, BAD_SCHEMA_TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllUsersGroupsPolicies() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
|
||||
final Set<Group> groups = userGroupProvider.getGroups();
|
||||
assertEquals(2, groups.size());
|
||||
|
||||
boolean foundGroup1 = false;
|
||||
boolean foundGroup2 = false;
|
||||
|
||||
for (Group group : groups) {
|
||||
if (group.getIdentifier().equals("group-1") && group.getName().equals("group-1")
|
||||
&& group.getUsers().size() == 1 && group.getUsers().contains("user-1")) {
|
||||
foundGroup1 = true;
|
||||
} else if (group.getIdentifier().equals("group-2") && group.getName().equals("group-2")
|
||||
&& group.getUsers().size() == 1 && group.getUsers().contains("user-2")) {
|
||||
foundGroup2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(foundGroup1);
|
||||
assertTrue(foundGroup2);
|
||||
|
||||
final Set<User> users = userGroupProvider.getUsers();
|
||||
assertEquals(2, users.size());
|
||||
|
||||
boolean foundUser1 = false;
|
||||
boolean foundUser2 = false;
|
||||
|
||||
for (User user : users) {
|
||||
if (user.getIdentifier().equals("user-1") && user.getIdentity().equals("user-1")) {
|
||||
foundUser1 = true;
|
||||
} else if (user.getIdentifier().equals("user-2") && user.getIdentity().equals("user-2")) {
|
||||
foundUser2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(foundUser1);
|
||||
assertTrue(foundUser2);
|
||||
}
|
||||
|
||||
// --------------- User Tests ------------------------
|
||||
|
||||
@Test
|
||||
public void testAddUser() throws Exception {
|
||||
writeFile(primaryTenants, EMPTY_TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(0, userGroupProvider.getUsers().size());
|
||||
|
||||
final User user = new User.Builder()
|
||||
.identifier("user-1")
|
||||
.identity("user-identity-1")
|
||||
.build();
|
||||
|
||||
final User addedUser = userGroupProvider.addUser(user);
|
||||
assertNotNull(addedUser);
|
||||
assertEquals(user.getIdentifier(), addedUser.getIdentifier());
|
||||
assertEquals(user.getIdentity(), addedUser.getIdentity());
|
||||
|
||||
final Set<User> users = userGroupProvider.getUsers();
|
||||
assertEquals(1, users.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserByIdentifierWhenFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getUsers().size());
|
||||
|
||||
final String identifier = "user-1";
|
||||
final User user = userGroupProvider.getUser(identifier);
|
||||
assertNotNull(user);
|
||||
assertEquals(identifier, user.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserByIdentifierWhenNotFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getUsers().size());
|
||||
|
||||
final String identifier = "user-X";
|
||||
final User user = userGroupProvider.getUser(identifier);
|
||||
assertNull(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserByIdentityWhenFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getUsers().size());
|
||||
|
||||
final String identity = "user-1";
|
||||
final User user = userGroupProvider.getUserByIdentity(identity);
|
||||
assertNotNull(user);
|
||||
assertEquals(identity, user.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserByIdentityWhenNotFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getUsers().size());
|
||||
|
||||
final String identity = "user-X";
|
||||
final User user = userGroupProvider.getUserByIdentity(identity);
|
||||
assertNull(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteUser() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getUsers().size());
|
||||
|
||||
// retrieve user-1 and verify it exists
|
||||
final User user = userGroupProvider.getUser("user-1");
|
||||
assertEquals("user-1", user.getIdentifier());
|
||||
|
||||
// delete user-1
|
||||
final User deletedUser = userGroupProvider.deleteUser(user);
|
||||
assertNotNull(deletedUser);
|
||||
assertEquals("user-1", deletedUser.getIdentifier());
|
||||
|
||||
// should be one less user
|
||||
assertEquals(1, userGroupProvider.getUsers().size());
|
||||
assertNull(userGroupProvider.getUser(user.getIdentifier()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteUserWhenNotFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getUsers().size());
|
||||
|
||||
//user that doesn't exist
|
||||
final User user = new User.Builder().identifier("user-X").identity("user-identity-X").build();
|
||||
|
||||
// should return null and still have 2 users because nothing was deleted
|
||||
final User deletedUser = userGroupProvider.deleteUser(user);
|
||||
assertNull(deletedUser);
|
||||
assertEquals(2, userGroupProvider.getUsers().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateUserWhenFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getUsers().size());
|
||||
|
||||
final User user = new User.Builder()
|
||||
.identifier("user-1")
|
||||
.identity("new-identity")
|
||||
.build();
|
||||
|
||||
final User updatedUser = userGroupProvider.updateUser(user);
|
||||
assertNotNull(updatedUser);
|
||||
assertEquals(user.getIdentifier(), updatedUser.getIdentifier());
|
||||
assertEquals(user.getIdentity(), updatedUser.getIdentity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateUserWhenNotFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getUsers().size());
|
||||
|
||||
final User user = new User.Builder()
|
||||
.identifier("user-X")
|
||||
.identity("new-identity")
|
||||
.build();
|
||||
|
||||
final User updatedUser = userGroupProvider.updateUser(user);
|
||||
assertNull(updatedUser);
|
||||
}
|
||||
|
||||
// --------------- Group Tests ------------------------
|
||||
|
||||
@Test
|
||||
public void testAddGroup() throws Exception {
|
||||
writeFile(primaryTenants, EMPTY_TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(0, userGroupProvider.getGroups().size());
|
||||
|
||||
final Group group = new Group.Builder()
|
||||
.identifier("group-id-1")
|
||||
.name("group-name-1")
|
||||
.build();
|
||||
|
||||
final Group addedGroup = userGroupProvider.addGroup(group);
|
||||
assertNotNull(addedGroup);
|
||||
assertEquals(group.getIdentifier(), addedGroup.getIdentifier());
|
||||
assertEquals(group.getName(), addedGroup.getName());
|
||||
assertEquals(0, addedGroup.getUsers().size());
|
||||
|
||||
final Set<Group> groups = userGroupProvider.getGroups();
|
||||
assertEquals(1, groups.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddGroupWithUser() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getGroups().size());
|
||||
|
||||
final Group group = new Group.Builder()
|
||||
.identifier("group-id-XXX")
|
||||
.name("group-name-XXX")
|
||||
.addUser("user-1")
|
||||
.build();
|
||||
|
||||
final Group addedGroup = userGroupProvider.addGroup(group);
|
||||
assertNotNull(addedGroup);
|
||||
assertEquals(group.getIdentifier(), addedGroup.getIdentifier());
|
||||
assertEquals(group.getName(), addedGroup.getName());
|
||||
assertEquals(1, addedGroup.getUsers().size());
|
||||
|
||||
final Set<Group> groups = userGroupProvider.getGroups();
|
||||
assertEquals(3, groups.size());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testAddGroupWhenUserDoesNotExist() throws Exception {
|
||||
writeFile(primaryTenants, EMPTY_TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(0, userGroupProvider.getGroups().size());
|
||||
|
||||
final Group group = new Group.Builder()
|
||||
.identifier("group-id-1")
|
||||
.name("group-name-1")
|
||||
.addUser("user1")
|
||||
.build();
|
||||
|
||||
userGroupProvider.addGroup(group);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGroupByIdentifierWhenFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getGroups().size());
|
||||
|
||||
final String identifier = "group-1";
|
||||
final Group group = userGroupProvider.getGroup(identifier);
|
||||
assertNotNull(group);
|
||||
assertEquals(identifier, group.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGroupByIdentifierWhenNotFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getGroups().size());
|
||||
|
||||
final String identifier = "group-X";
|
||||
final Group group = userGroupProvider.getGroup(identifier);
|
||||
assertNull(group);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteGroupWhenFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getGroups().size());
|
||||
|
||||
// retrieve group-1
|
||||
final Group group = userGroupProvider.getGroup("group-1");
|
||||
assertEquals("group-1", group.getIdentifier());
|
||||
|
||||
// delete group-1
|
||||
final Group deletedGroup = userGroupProvider.deleteGroup(group);
|
||||
assertNotNull(deletedGroup);
|
||||
assertEquals("group-1", deletedGroup.getIdentifier());
|
||||
|
||||
// verify there is one less overall group
|
||||
assertEquals(1, userGroupProvider.getGroups().size());
|
||||
|
||||
// verify we can no longer retrieve group-1 by identifier
|
||||
assertNull(userGroupProvider.getGroup(group.getIdentifier()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteGroupWhenNotFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getGroups().size());
|
||||
|
||||
final Group group = new Group.Builder()
|
||||
.identifier("group-id-X")
|
||||
.name("group-name-X")
|
||||
.build();
|
||||
|
||||
final Group deletedGroup = userGroupProvider.deleteGroup(group);
|
||||
assertNull(deletedGroup);
|
||||
assertEquals(2, userGroupProvider.getGroups().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateGroupWhenFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getGroups().size());
|
||||
|
||||
// verify user-1 is in group-1 before the update
|
||||
final Group groupBefore = userGroupProvider.getGroup("group-1");
|
||||
assertEquals(1, groupBefore.getUsers().size());
|
||||
assertTrue(groupBefore.getUsers().contains("user-1"));
|
||||
|
||||
final Group group = new Group.Builder()
|
||||
.identifier("group-1")
|
||||
.name("new-name")
|
||||
.addUser("user-2")
|
||||
.build();
|
||||
|
||||
final Group updatedGroup = userGroupProvider.updateGroup(group);
|
||||
assertEquals(group.getIdentifier(), updatedGroup.getIdentifier());
|
||||
assertEquals(group.getName(), updatedGroup.getName());
|
||||
|
||||
assertEquals(1, updatedGroup.getUsers().size());
|
||||
assertTrue(updatedGroup.getUsers().contains("user-2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateGroupWhenNotFound() throws Exception {
|
||||
writeFile(primaryTenants, TENANTS);
|
||||
userGroupProvider.onConfigured(configurationContext);
|
||||
assertEquals(2, userGroupProvider.getGroups().size());
|
||||
|
||||
final Group group = new Group.Builder()
|
||||
.identifier("group-X")
|
||||
.name("group-X")
|
||||
.build();
|
||||
|
||||
final Group updatedGroup = userGroupProvider.updateGroup(group);
|
||||
assertNull(updatedGroup);
|
||||
assertEquals(2, userGroupProvider.getGroups().size());
|
||||
}
|
||||
|
||||
private static void writeFile(final File file, final String content) throws Exception {
|
||||
byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
|
||||
try (final FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean deleteFile(final File file) {
|
||||
if (file.isDirectory()) {
|
||||
FileUtils.deleteFilesInDir(file, null, null, true, true);
|
||||
}
|
||||
return FileUtils.deleteFile(file, null, 10);
|
||||
}
|
||||
|
||||
private NiFiProperties getNiFiProperties(final Properties properties) {
|
||||
final NiFiProperties nifiProperties = Mockito.mock(NiFiProperties.class);
|
||||
when(nifiProperties.getPropertyKeys()).thenReturn(properties.stringPropertyNames());
|
||||
|
||||
when(nifiProperties.getProperty(anyString())).then(new Answer<String>() {
|
||||
@Override
|
||||
public String answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
return properties.getProperty((String)invocationOnMock.getArguments()[0]);
|
||||
}
|
||||
});
|
||||
return nifiProperties;
|
||||
}
|
||||
|
||||
}
|
|
@ -22,10 +22,15 @@ package org.apache.nifi.authorization;
|
|||
public class StandardAuthorizerInitializationContext implements AuthorizerInitializationContext {
|
||||
|
||||
private final String identifier;
|
||||
private final UserGroupProviderLookup userGroupProviderLookup;
|
||||
private final AccessPolicyProviderLookup accessPolicyProviderLookup;
|
||||
private final AuthorizerLookup authorizerLookup;
|
||||
|
||||
public StandardAuthorizerInitializationContext(String identifier, AuthorizerLookup authorizerLookup) {
|
||||
public StandardAuthorizerInitializationContext(String identifier, UserGroupProviderLookup userGroupProviderLookup,
|
||||
AccessPolicyProviderLookup accessPolicyProviderLookup, AuthorizerLookup authorizerLookup) {
|
||||
this.identifier = identifier;
|
||||
this.userGroupProviderLookup = userGroupProviderLookup;
|
||||
this.accessPolicyProviderLookup = accessPolicyProviderLookup;
|
||||
this.authorizerLookup = authorizerLookup;
|
||||
}
|
||||
|
||||
|
@ -38,4 +43,13 @@ public class StandardAuthorizerInitializationContext implements AuthorizerInitia
|
|||
return authorizerLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicyProviderLookup getAccessPolicyProviderLookup() {
|
||||
return accessPolicyProviderLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserGroupProviderLookup getUserGroupProviderLookup() {
|
||||
return userGroupProviderLookup;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* 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.commons.lang3.StringUtils;
|
||||
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.exception.UninheritableAuthorizationsException;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Set;
|
||||
|
||||
public class StandardManagedAuthorizer implements ManagedAuthorizer {
|
||||
|
||||
private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
|
||||
private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();
|
||||
|
||||
private static final String USER_GROUP_PROVIDER_ELEMENT = "userGroupProvider";
|
||||
private static final String ACCESS_POLICY_PROVIDER_ELEMENT = "accessPolicyProvider";
|
||||
|
||||
private AccessPolicyProviderLookup accessPolicyProviderLookup;
|
||||
private AccessPolicyProvider accessPolicyProvider;
|
||||
private UserGroupProvider userGroupProvider;
|
||||
|
||||
@Override
|
||||
public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
accessPolicyProviderLookup = initializationContext.getAccessPolicyProviderLookup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
final String accessPolicyProviderKey = configurationContext.getProperty("Access Policy Provider").getValue();
|
||||
accessPolicyProvider = accessPolicyProviderLookup.getAccessPolicyProvider(accessPolicyProviderKey);
|
||||
|
||||
// ensure the desired access policy provider was found
|
||||
if (accessPolicyProvider == null) {
|
||||
throw new AuthorizerCreationException(String.format("Unable to locate configured Access Policy Provider: %s", accessPolicyProviderKey));
|
||||
}
|
||||
|
||||
userGroupProvider = accessPolicyProvider.getUserGroupProvider();
|
||||
|
||||
// ensure the desired access policy provider has a user group provider
|
||||
if (userGroupProvider == null) {
|
||||
throw new AuthorizerCreationException(String.format("Configured Access Policy Provider %s does not contain a User Group Provider", accessPolicyProviderKey));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException {
|
||||
final String resourceIdentifier = request.getResource().getIdentifier();
|
||||
final AccessPolicy policy = accessPolicyProvider.getAccessPolicy(resourceIdentifier, request.getAction());
|
||||
if (policy == null) {
|
||||
return AuthorizationResult.resourceNotFound();
|
||||
}
|
||||
|
||||
final UserAndGroups userAndGroups = userGroupProvider.getUserAndGroups(request.getIdentity());
|
||||
|
||||
final User user = userAndGroups.getUser();
|
||||
if (user == null) {
|
||||
return AuthorizationResult.denied(String.format("Unknown user with identity '%s'.", request.getIdentity()));
|
||||
}
|
||||
|
||||
final Set<Group> userGroups = userAndGroups.getGroups();
|
||||
if (policy.getUsers().contains(user.getIdentifier()) || containsGroup(userGroups, policy)) {
|
||||
return AuthorizationResult.approved();
|
||||
}
|
||||
|
||||
return AuthorizationResult.denied(request.getExplanationSupplier().get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the policy contains one of the user's groups.
|
||||
*
|
||||
* @param userGroups the set of the user's groups
|
||||
* @param policy the policy
|
||||
* @return true if one of the Groups in userGroups is contained in the policy
|
||||
*/
|
||||
private boolean containsGroup(final Set<Group> userGroups, final AccessPolicy policy) {
|
||||
if (userGroups == null || userGroups.isEmpty() || policy.getGroups().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Group userGroup : userGroups) {
|
||||
if (policy.getGroups().contains(userGroup.getIdentifier())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFingerprint() throws AuthorizationAccessException {
|
||||
XMLStreamWriter writer = null;
|
||||
final StringWriter out = new StringWriter();
|
||||
try {
|
||||
writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
|
||||
writer.writeStartDocument();
|
||||
writer.writeStartElement("managedAuthorizations");
|
||||
|
||||
writer.writeStartElement(ACCESS_POLICY_PROVIDER_ELEMENT);
|
||||
if (accessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
|
||||
writer.writeCharacters(((ConfigurableAccessPolicyProvider) accessPolicyProvider).getFingerprint());
|
||||
}
|
||||
writer.writeEndElement();
|
||||
|
||||
writer.writeStartElement(USER_GROUP_PROVIDER_ELEMENT);
|
||||
if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
|
||||
writer.writeCharacters(((ConfigurableUserGroupProvider) userGroupProvider).getFingerprint());
|
||||
}
|
||||
writer.writeEndElement();
|
||||
|
||||
writer.writeEndElement();
|
||||
writer.writeEndDocument();
|
||||
writer.flush();
|
||||
} catch (XMLStreamException e) {
|
||||
throw new AuthorizationAccessException("Unable to generate fingerprint", e);
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (XMLStreamException e) {
|
||||
// nothing to do here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException {
|
||||
if (StringUtils.isBlank(fingerprint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final FingerprintHolder fingerprintHolder = parseFingerprint(fingerprint);
|
||||
|
||||
if (StringUtils.isNotBlank(fingerprintHolder.getPolicyFingerprint()) && accessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
|
||||
((ConfigurableAccessPolicyProvider) accessPolicyProvider).inheritFingerprint(fingerprintHolder.getPolicyFingerprint());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(fingerprintHolder.getUserGroupFingerprint()) && userGroupProvider instanceof ConfigurableUserGroupProvider) {
|
||||
((ConfigurableUserGroupProvider) userGroupProvider).inheritFingerprint(fingerprintHolder.getUserGroupFingerprint());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException {
|
||||
final FingerprintHolder fingerprintHolder = parseFingerprint(proposedFingerprint);
|
||||
|
||||
if (StringUtils.isNotBlank(fingerprintHolder.getPolicyFingerprint())) {
|
||||
if (accessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
|
||||
((ConfigurableAccessPolicyProvider) accessPolicyProvider).checkInheritability(fingerprintHolder.getPolicyFingerprint());
|
||||
} else {
|
||||
throw new UninheritableAuthorizationsException("Policy fingerprint is not blank and the configured AccessPolicyProvider does not support fingerprinting.");
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(fingerprintHolder.getUserGroupFingerprint())) {
|
||||
if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
|
||||
((ConfigurableUserGroupProvider) userGroupProvider).checkInheritability(fingerprintHolder.getUserGroupFingerprint());
|
||||
} else {
|
||||
throw new UninheritableAuthorizationsException("User/Group fingerprint is not blank and the configured UserGroupProvider does not support fingerprinting.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final FingerprintHolder parseFingerprint(final String fingerprint) throws AuthorizationAccessException {
|
||||
final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) {
|
||||
final DocumentBuilder docBuilder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
|
||||
final Document document = docBuilder.parse(in);
|
||||
final Element rootElement = document.getDocumentElement();
|
||||
|
||||
final NodeList accessPolicyProviderList = rootElement.getElementsByTagName(ACCESS_POLICY_PROVIDER_ELEMENT);
|
||||
if (accessPolicyProviderList.getLength() != 1) {
|
||||
throw new AuthorizationAccessException(String.format("Only one %s element is allowed: %s", ACCESS_POLICY_PROVIDER_ELEMENT, fingerprint));
|
||||
}
|
||||
|
||||
final NodeList userGroupProviderList = rootElement.getElementsByTagName(USER_GROUP_PROVIDER_ELEMENT);
|
||||
if (userGroupProviderList.getLength() != 1) {
|
||||
throw new AuthorizationAccessException(String.format("Only one %s element is allowed: %s", USER_GROUP_PROVIDER_ELEMENT, fingerprint));
|
||||
}
|
||||
|
||||
final Node accessPolicyProvider = accessPolicyProviderList.item(0);
|
||||
final Node userGroupProvider = userGroupProviderList.item(0);
|
||||
return new FingerprintHolder(accessPolicyProvider.getTextContent(), userGroupProvider.getTextContent());
|
||||
} catch (SAXException | ParserConfigurationException | IOException e) {
|
||||
throw new AuthorizationAccessException("Unable to parse fingerprint", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicyProvider getAccessPolicyProvider() {
|
||||
return accessPolicyProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
|
||||
}
|
||||
|
||||
private static class FingerprintHolder {
|
||||
private final String policyFingerprint;
|
||||
private final String userGroupFingerprint;
|
||||
|
||||
public FingerprintHolder(String policyFingerprint, String userGroupFingerprint) {
|
||||
this.policyFingerprint = policyFingerprint;
|
||||
this.userGroupFingerprint = userGroupFingerprint;
|
||||
}
|
||||
|
||||
public String getPolicyFingerprint() {
|
||||
return policyFingerprint;
|
||||
}
|
||||
|
||||
public String getUserGroupFingerprint() {
|
||||
return userGroupFingerprint;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,11 @@
|
|||
*/
|
||||
package org.apache.nifi.authorization.user;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An implementation of NiFiUser.
|
||||
|
@ -24,42 +28,20 @@ import java.util.Objects;
|
|||
public class StandardNiFiUser implements NiFiUser {
|
||||
|
||||
public static final String ANONYMOUS_IDENTITY = "anonymous";
|
||||
public static final StandardNiFiUser ANONYMOUS = new StandardNiFiUser(ANONYMOUS_IDENTITY, null, null, true);
|
||||
public static final StandardNiFiUser ANONYMOUS = new Builder().identity(ANONYMOUS_IDENTITY).anonymous(true).build();
|
||||
|
||||
private final String identity;
|
||||
private final Set<String> groups;
|
||||
private final NiFiUser chain;
|
||||
private final String clientAddress;
|
||||
private final boolean isAnonymous;
|
||||
|
||||
public StandardNiFiUser(String identity) {
|
||||
this(identity, null, null, false);
|
||||
}
|
||||
|
||||
public StandardNiFiUser(String identity, String clientAddress) {
|
||||
this(identity, null, clientAddress, false);
|
||||
}
|
||||
|
||||
public StandardNiFiUser(String identity, NiFiUser chain) {
|
||||
this(identity, chain, null, false);
|
||||
}
|
||||
|
||||
public StandardNiFiUser(String identity, NiFiUser chain, String clientAddress) {
|
||||
this(identity, chain, clientAddress, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is private as the only instance of this class which should have {@code isAnonymous} set to true is the singleton ANONYMOUS.
|
||||
*
|
||||
* @param identity the identity string for the user (i.e. "Andy" or "CN=alopresto, OU=Apache NiFi")
|
||||
* @param chain the proxy chain that leads to this users
|
||||
* @param clientAddress the source address of the request
|
||||
* @param isAnonymous true to represent the canonical "anonymous" user
|
||||
*/
|
||||
private StandardNiFiUser(String identity, NiFiUser chain, String clientAddress, boolean isAnonymous) {
|
||||
this.identity = identity;
|
||||
this.chain = chain;
|
||||
this.clientAddress = clientAddress;
|
||||
this.isAnonymous = isAnonymous;
|
||||
private StandardNiFiUser(final Builder builder) {
|
||||
this.identity = builder.identity;
|
||||
this.groups = builder.groups == null ? null : Collections.unmodifiableSet(builder.groups);
|
||||
this.chain = builder.chain;
|
||||
this.clientAddress = builder.clientAddress;
|
||||
this.isAnonymous = builder.isAnonymous;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,7 +52,7 @@ public class StandardNiFiUser implements NiFiUser {
|
|||
* @return an anonymous user instance with the identity "anonymous"
|
||||
*/
|
||||
public static StandardNiFiUser populateAnonymousUser(NiFiUser chain, String clientAddress) {
|
||||
return new StandardNiFiUser(ANONYMOUS_IDENTITY, chain, clientAddress, true);
|
||||
return new Builder().identity(ANONYMOUS_IDENTITY).chain(chain).clientAddress(clientAddress).anonymous(true).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,6 +60,11 @@ public class StandardNiFiUser implements NiFiUser {
|
|||
return identity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NiFiUser getChain() {
|
||||
return chain;
|
||||
|
@ -116,6 +103,87 @@ public class StandardNiFiUser implements NiFiUser {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("identity[%s]", getIdentity());
|
||||
final String formattedGroups;
|
||||
if (groups == null) {
|
||||
formattedGroups = "none";
|
||||
} else {
|
||||
formattedGroups = StringUtils.join(groups, ", ");
|
||||
}
|
||||
|
||||
return String.format("identity[%s], groups[%s]", getIdentity(), formattedGroups);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for a StandardNiFiUser
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private String identity;
|
||||
private Set<String> groups;
|
||||
private NiFiUser chain;
|
||||
private String clientAddress;
|
||||
private boolean isAnonymous = false;
|
||||
|
||||
/**
|
||||
* Sets the identity.
|
||||
*
|
||||
* @param identity the identity string for the user (i.e. "Andy" or "CN=alopresto, OU=Apache NiFi")
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder identity(final String identity) {
|
||||
this.identity = identity;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the groups.
|
||||
*
|
||||
* @param groups the user groups
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder groups(final Set<String> groups) {
|
||||
this.groups = groups;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the chain.
|
||||
*
|
||||
* @param chain the proxy chain that leads to this users
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder chain(final NiFiUser chain) {
|
||||
this.chain = chain;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client address.
|
||||
*
|
||||
* @param clientAddress the source address of the request
|
||||
* @return the builder
|
||||
*/
|
||||
public Builder clientAddress(final String clientAddress) {
|
||||
this.clientAddress = clientAddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this user is the canonical "anonymous" user
|
||||
*
|
||||
* @param isAnonymous true to represent the canonical "anonymous" user
|
||||
* @return the builder
|
||||
*/
|
||||
private Builder anonymous(final boolean isAnonymous) {
|
||||
this.isAnonymous = isAnonymous;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return builds a StandardNiFiUser from the current state of the builder
|
||||
*/
|
||||
public StandardNiFiUser build() {
|
||||
return new StandardNiFiUser(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.authorization.util;
|
||||
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.Group;
|
||||
import org.apache.nifi.authorization.ManagedAuthorizer;
|
||||
import org.apache.nifi.authorization.UserAndGroups;
|
||||
import org.apache.nifi.authorization.UserGroupProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class UserGroupUtil {
|
||||
|
||||
/**
|
||||
* Gets the groups for the user with the specified identity. Returns null if the authorizer is not able to load user groups.
|
||||
*
|
||||
* @param authorizer the authorizer to load the groups from
|
||||
* @param userIdentity the user identity
|
||||
* @return the listing of groups for the user
|
||||
*/
|
||||
public static Set<String> getUserGroups(final Authorizer authorizer, final String userIdentity) {
|
||||
if (authorizer instanceof ManagedAuthorizer) {
|
||||
final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) authorizer;
|
||||
final UserGroupProvider userGroupProvider = managedAuthorizer.getAccessPolicyProvider().getUserGroupProvider();
|
||||
final UserAndGroups userAndGroups = userGroupProvider.getUserAndGroups(userIdentity);
|
||||
final Set<Group> userGroups = userAndGroups.getGroups();
|
||||
|
||||
if (userGroups == null || userGroups.isEmpty()) {
|
||||
return Collections.EMPTY_SET;
|
||||
} else {
|
||||
return userAndGroups.getGroups().stream().map(group -> group.getName()).collect(Collectors.toSet());
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
org.apache.nifi.authorization.StandardManagedAuthorizer
|
|
@ -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.attribute.expression.language.StandardPropertyValue;
|
||||
import org.apache.nifi.authorization.exception.AuthorizationAccessException;
|
||||
import org.apache.nifi.authorization.exception.AuthorizerCreationException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class StandardManagedAuthorizerTest {
|
||||
|
||||
private static final String EMPTY_FINGERPRINT = "<?xml version=\"1.0\" ?>"
|
||||
+ "<managedAuthorizations>"
|
||||
+ "<accessPolicyProvider></accessPolicyProvider>"
|
||||
+ "<userGroupProvider></userGroupProvider>"
|
||||
+ "</managedAuthorizations>";
|
||||
|
||||
private static final String NON_EMPTY_FINGERPRINT = "<?xml version=\"1.0\" ?>"
|
||||
+ "<managedAuthorizations>"
|
||||
+ "<accessPolicyProvider>"
|
||||
+ "<accessPolicies>"
|
||||
+ "<policy identifier=\"policy-id-1\" resource=\"resource2\" actions=\"READ\">"
|
||||
+ "<policyUser identifier=\"user-id-1\"></policyUser>"
|
||||
+ "<policyGroup identifier=\"group-id-1\"></policyGroup>"
|
||||
+ "</policy>"
|
||||
+ "</accessPolicies>"
|
||||
+ "</accessPolicyProvider>"
|
||||
+ "<userGroupProvider>"
|
||||
+ "<tenants>"
|
||||
+ "<user identifier=\"user-id-1\" identity=\"user-1\"></user>"
|
||||
+ "<group identifier=\"group-id-1\" name=\"group-1\">"
|
||||
+ "<groupUser identifier=\"user-id-1\"></groupUser>"
|
||||
+ "</group>"
|
||||
+ "</tenants>"
|
||||
+ "</userGroupProvider>"
|
||||
+ "</managedAuthorizations>";
|
||||
|
||||
private static final String ACCESS_POLICY_FINGERPRINT =
|
||||
"<accessPolicies>"
|
||||
+ "<policy identifier=\"policy-id-1\" resource=\"resource2\" actions=\"READ\">"
|
||||
+ "<policyUser identifier=\"user-id-1\"></policyUser>"
|
||||
+ "<policyGroup identifier=\"group-id-1\"></policyGroup>"
|
||||
+ "</policy>"
|
||||
+ "</accessPolicies>";
|
||||
|
||||
private static final String TENANT_FINGERPRINT =
|
||||
"<tenants>"
|
||||
+ "<user identifier=\"user-id-1\" identity=\"user-1\"></user>"
|
||||
+ "<group identifier=\"group-id-1\" name=\"group-1\">"
|
||||
+ "<groupUser identifier=\"user-id-1\"></groupUser>"
|
||||
+ "</group>"
|
||||
+ "</tenants>";
|
||||
|
||||
private static final Resource TEST_RESOURCE = new Resource() {
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return "1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "resource1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSafeDescription() {
|
||||
return "description1";
|
||||
}
|
||||
};
|
||||
|
||||
@Test(expected = AuthorizerCreationException.class)
|
||||
public void testNullAccessPolicyProvider() throws Exception {
|
||||
getStandardManagedAuthorizer(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyFingerPrint() throws Exception {
|
||||
final UserGroupProvider userGroupProvider = mock(UserGroupProvider.class);
|
||||
|
||||
final AccessPolicyProvider accessPolicyProvider = mock(AccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
Assert.assertEquals(EMPTY_FINGERPRINT, managedAuthorizer.getFingerprint());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonEmptyFingerPrint() throws Exception {
|
||||
final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class);
|
||||
when(userGroupProvider.getFingerprint()).thenReturn(TENANT_FINGERPRINT);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getFingerprint()).thenReturn(ACCESS_POLICY_FINGERPRINT);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
Assert.assertEquals(NON_EMPTY_FINGERPRINT, managedAuthorizer.getFingerprint());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInheritEmptyFingerprint() throws Exception {
|
||||
final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
managedAuthorizer.inheritFingerprint(EMPTY_FINGERPRINT);
|
||||
|
||||
verify(userGroupProvider, times(0)).inheritFingerprint(anyString());
|
||||
verify(accessPolicyProvider, times(0)).inheritFingerprint(anyString());
|
||||
}
|
||||
|
||||
@Test(expected = AuthorizationAccessException.class)
|
||||
public void testInheritInvalidFingerprint() throws Exception {
|
||||
final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
managedAuthorizer.inheritFingerprint("not a valid fingerprint");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInheritNonEmptyFingerprint() throws Exception {
|
||||
final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
managedAuthorizer.inheritFingerprint(NON_EMPTY_FINGERPRINT);
|
||||
|
||||
verify(userGroupProvider, times(1)).inheritFingerprint(TENANT_FINGERPRINT);
|
||||
verify(accessPolicyProvider, times(1)).inheritFingerprint(ACCESS_POLICY_FINGERPRINT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckInheritEmptyFingerprint() throws Exception {
|
||||
final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
managedAuthorizer.checkInheritability(EMPTY_FINGERPRINT);
|
||||
|
||||
verify(userGroupProvider, times(0)).inheritFingerprint(anyString());
|
||||
verify(accessPolicyProvider, times(0)).inheritFingerprint(anyString());
|
||||
}
|
||||
|
||||
@Test(expected = AuthorizationAccessException.class)
|
||||
public void testCheckInheritInvalidFingerprint() throws Exception {
|
||||
final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
managedAuthorizer.checkInheritability("not a valid fingerprint");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckInheritNonEmptyFingerprint() throws Exception {
|
||||
final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
managedAuthorizer.checkInheritability(NON_EMPTY_FINGERPRINT);
|
||||
|
||||
verify(userGroupProvider, times(1)).checkInheritability(TENANT_FINGERPRINT);
|
||||
verify(accessPolicyProvider, times(1)).checkInheritability(ACCESS_POLICY_FINGERPRINT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthorizationByUser() throws Exception {
|
||||
final String userIdentifier = "userIdentifier1";
|
||||
final String userIdentity = "userIdentity1";
|
||||
|
||||
final User user = new User.Builder()
|
||||
.identity(userIdentity)
|
||||
.identifier(userIdentifier)
|
||||
.build();
|
||||
|
||||
final AccessPolicy policy = new AccessPolicy.Builder()
|
||||
.identifier("1")
|
||||
.resource(TEST_RESOURCE.getIdentifier())
|
||||
.addUser(userIdentifier)
|
||||
.action(RequestAction.READ)
|
||||
.build();
|
||||
|
||||
final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class);
|
||||
when(userGroupProvider.getUserAndGroups(userIdentity)).thenReturn(new UserAndGroups() {
|
||||
@Override
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
});
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getAccessPolicy(TEST_RESOURCE.getIdentifier(), RequestAction.READ)).thenReturn(policy);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.identity(userIdentity)
|
||||
.resource(TEST_RESOURCE)
|
||||
.action(RequestAction.READ)
|
||||
.accessAttempt(true)
|
||||
.anonymous(false)
|
||||
.build();
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
assertEquals(AuthorizationResult.approved(), managedAuthorizer.authorize(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthorizationByGroup() throws Exception {
|
||||
final String userIdentifier = "userIdentifier1";
|
||||
final String userIdentity = "userIdentity1";
|
||||
final String groupIdentifier = "groupIdentifier1";
|
||||
|
||||
final User user = new User.Builder()
|
||||
.identity(userIdentity)
|
||||
.identifier(userIdentifier)
|
||||
.build();
|
||||
|
||||
final Group group = new Group.Builder()
|
||||
.identifier(groupIdentifier)
|
||||
.name(groupIdentifier)
|
||||
.addUser(user.getIdentifier())
|
||||
.build();
|
||||
|
||||
final AccessPolicy policy = new AccessPolicy.Builder()
|
||||
.identifier("1")
|
||||
.resource(TEST_RESOURCE.getIdentifier())
|
||||
.addGroup(groupIdentifier)
|
||||
.action(RequestAction.READ)
|
||||
.build();
|
||||
|
||||
final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class);
|
||||
when(userGroupProvider.getUserAndGroups(userIdentity)).thenReturn(new UserAndGroups() {
|
||||
@Override
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() {
|
||||
return Stream.of(group).collect(Collectors.toSet());
|
||||
}
|
||||
});
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getAccessPolicy(TEST_RESOURCE.getIdentifier(), RequestAction.READ)).thenReturn(policy);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.identity(userIdentity)
|
||||
.resource(TEST_RESOURCE)
|
||||
.action(RequestAction.READ)
|
||||
.accessAttempt(true)
|
||||
.anonymous(false)
|
||||
.build();
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
assertEquals(AuthorizationResult.approved(), managedAuthorizer.authorize(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceNotFound() throws Exception {
|
||||
final String userIdentity = "userIdentity1";
|
||||
|
||||
final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class);
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getAccessPolicy(TEST_RESOURCE.getIdentifier(), RequestAction.READ)).thenReturn(null);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.identity(userIdentity)
|
||||
.resource(TEST_RESOURCE)
|
||||
.action(RequestAction.READ)
|
||||
.accessAttempt(true)
|
||||
.anonymous(false)
|
||||
.build();
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
assertEquals(AuthorizationResult.resourceNotFound(), managedAuthorizer.authorize(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnauthorizedDueToUnknownUser() throws Exception {
|
||||
final String userIdentifier = "userIdentifier1";
|
||||
final String userIdentity = "userIdentity1";
|
||||
final String notUser1Identity = "not userIdentity1";
|
||||
|
||||
final User user = new User.Builder()
|
||||
.identity(userIdentity)
|
||||
.identifier(userIdentifier)
|
||||
.build();
|
||||
|
||||
final AccessPolicy policy = new AccessPolicy.Builder()
|
||||
.identifier("1")
|
||||
.resource(TEST_RESOURCE.getIdentifier())
|
||||
.addUser(userIdentifier)
|
||||
.action(RequestAction.READ)
|
||||
.build();
|
||||
|
||||
final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class);
|
||||
when(userGroupProvider.getUserAndGroups(notUser1Identity)).thenReturn(new UserAndGroups() {
|
||||
@Override
|
||||
public User getUser() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
});
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getAccessPolicy(TEST_RESOURCE.getIdentifier(), RequestAction.READ)).thenReturn(policy);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.identity(notUser1Identity)
|
||||
.resource(TEST_RESOURCE)
|
||||
.action(RequestAction.READ)
|
||||
.accessAttempt(true)
|
||||
.anonymous(false)
|
||||
.build();
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
assertTrue(AuthorizationResult.denied().getResult().equals(managedAuthorizer.authorize(request).getResult()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnauthorizedDueToLackOfPermission() throws Exception {
|
||||
final String userIdentifier = "userIdentifier1";
|
||||
final String userIdentity = "userIdentity1";
|
||||
|
||||
final User user = new User.Builder()
|
||||
.identity(userIdentity)
|
||||
.identifier(userIdentifier)
|
||||
.build();
|
||||
|
||||
final AccessPolicy policy = new AccessPolicy.Builder()
|
||||
.identifier("1")
|
||||
.resource(TEST_RESOURCE.getIdentifier())
|
||||
.addUser("userIdentity2")
|
||||
.action(RequestAction.READ)
|
||||
.build();
|
||||
|
||||
final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class);
|
||||
when(userGroupProvider.getUserAndGroups(userIdentity)).thenReturn(new UserAndGroups() {
|
||||
@Override
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() {
|
||||
return Collections.EMPTY_SET;
|
||||
}
|
||||
});
|
||||
|
||||
final ConfigurableAccessPolicyProvider accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
|
||||
when(accessPolicyProvider.getAccessPolicy(TEST_RESOURCE.getIdentifier(), RequestAction.READ)).thenReturn(policy);
|
||||
when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
|
||||
|
||||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.identity(userIdentity)
|
||||
.resource(TEST_RESOURCE)
|
||||
.action(RequestAction.READ)
|
||||
.accessAttempt(true)
|
||||
.anonymous(false)
|
||||
.build();
|
||||
|
||||
final StandardManagedAuthorizer managedAuthorizer = getStandardManagedAuthorizer(accessPolicyProvider);
|
||||
assertTrue(AuthorizationResult.denied().getResult().equals(managedAuthorizer.authorize(request).getResult()));
|
||||
}
|
||||
|
||||
private StandardManagedAuthorizer getStandardManagedAuthorizer(final AccessPolicyProvider accessPolicyProvider) {
|
||||
final StandardManagedAuthorizer managedAuthorizer = new StandardManagedAuthorizer();
|
||||
|
||||
final AuthorizerConfigurationContext configurationContext = mock(AuthorizerConfigurationContext.class);
|
||||
when(configurationContext.getProperty("Access Policy Provider")).thenReturn(new StandardPropertyValue("access-policy-provider", null));
|
||||
|
||||
final AccessPolicyProviderLookup accessPolicyProviderLookup = mock(AccessPolicyProviderLookup.class);
|
||||
when(accessPolicyProviderLookup.getAccessPolicyProvider("access-policy-provider")).thenReturn(accessPolicyProvider);
|
||||
|
||||
final AuthorizerInitializationContext initializationContext = mock(AuthorizerInitializationContext.class);
|
||||
when(initializationContext.getAccessPolicyProviderLookup()).thenReturn(accessPolicyProviderLookup);
|
||||
|
||||
managedAuthorizer.initialize(initializationContext);
|
||||
managedAuthorizer.onConfigured(configurationContext);
|
||||
|
||||
return managedAuthorizer;
|
||||
}
|
||||
}
|
|
@ -16,14 +16,6 @@
|
|||
*/
|
||||
package org.apache.nifi.authorization.resource;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.apache.nifi.authorization.AccessDeniedException;
|
||||
import org.apache.nifi.authorization.AuthorizationRequest;
|
||||
import org.apache.nifi.authorization.AuthorizationResult;
|
||||
|
@ -31,11 +23,19 @@ import org.apache.nifi.authorization.AuthorizationResult.Result;
|
|||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.StandardNiFiUser;
|
||||
import org.apache.nifi.authorization.user.StandardNiFiUser.Builder;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class DataAuthorizableTest {
|
||||
|
||||
private static final String IDENTITY_1 = "identity-1";
|
||||
|
@ -83,20 +83,20 @@ public class DataAuthorizableTest {
|
|||
|
||||
@Test(expected = AccessDeniedException.class)
|
||||
public void testAuthorizeUnauthorizedUser() {
|
||||
final NiFiUser user = new StandardNiFiUser("unknown");
|
||||
final NiFiUser user = new Builder().identity("unknown").build();
|
||||
testDataAuthorizable.authorize(testAuthorizer, RequestAction.READ, user, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckAuthorizationUnauthorizedUser() {
|
||||
final NiFiUser user = new StandardNiFiUser("unknown");
|
||||
final NiFiUser user = new Builder().identity("unknown").build();
|
||||
final AuthorizationResult result = testDataAuthorizable.checkAuthorization(testAuthorizer, RequestAction.READ, user, null);
|
||||
assertEquals(Result.Denied, result.getResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthorizedUser() {
|
||||
final NiFiUser user = new StandardNiFiUser(IDENTITY_1);
|
||||
final NiFiUser user = new Builder().identity(IDENTITY_1).build();
|
||||
testDataAuthorizable.authorize(testAuthorizer, RequestAction.READ, user, null);
|
||||
|
||||
verify(testAuthorizer, times(1)).authorize(argThat(new ArgumentMatcher<AuthorizationRequest>() {
|
||||
|
@ -109,7 +109,7 @@ public class DataAuthorizableTest {
|
|||
|
||||
@Test
|
||||
public void testCheckAuthorizationUser() {
|
||||
final NiFiUser user = new StandardNiFiUser(IDENTITY_1);
|
||||
final NiFiUser user = new Builder().identity(IDENTITY_1).build();
|
||||
final AuthorizationResult result = testDataAuthorizable.checkAuthorization(testAuthorizer, RequestAction.READ, user, null);
|
||||
|
||||
assertEquals(Result.Approved, result.getResult());
|
||||
|
@ -123,9 +123,9 @@ public class DataAuthorizableTest {
|
|||
|
||||
@Test
|
||||
public void testAuthorizedUserChain() {
|
||||
final NiFiUser proxy2 = new StandardNiFiUser(PROXY_2);
|
||||
final NiFiUser proxy1 = new StandardNiFiUser(PROXY_1, proxy2);
|
||||
final NiFiUser user = new StandardNiFiUser(IDENTITY_1, proxy1);
|
||||
final NiFiUser proxy2 = new Builder().identity(PROXY_2).build();
|
||||
final NiFiUser proxy1 = new Builder().identity(PROXY_1).chain(proxy2).build();
|
||||
final NiFiUser user = new Builder().identity(IDENTITY_1).chain(proxy1).build();
|
||||
testDataAuthorizable.authorize(testAuthorizer, RequestAction.READ, user, null);
|
||||
|
||||
verify(testAuthorizer, times(3)).authorize(any(AuthorizationRequest.class));
|
||||
|
@ -136,9 +136,9 @@ public class DataAuthorizableTest {
|
|||
|
||||
@Test
|
||||
public void testCheckAuthorizationUserChain() {
|
||||
final NiFiUser proxy2 = new StandardNiFiUser(PROXY_2);
|
||||
final NiFiUser proxy1 = new StandardNiFiUser(PROXY_1, proxy2);
|
||||
final NiFiUser user = new StandardNiFiUser(IDENTITY_1, proxy1);
|
||||
final NiFiUser proxy2 = new Builder().identity(PROXY_2).build();
|
||||
final NiFiUser proxy1 = new Builder().identity(PROXY_1).chain(proxy2).build();
|
||||
final NiFiUser user = new Builder().identity(IDENTITY_1).chain(proxy1).build();
|
||||
final AuthorizationResult result = testDataAuthorizable.checkAuthorization(testAuthorizer, RequestAction.READ, user, null);
|
||||
|
||||
assertEquals(Result.Approved, result.getResult());
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.apache.nifi.cluster.coordination.http;
|
||||
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.AccessPolicyEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.BulletinBoardEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.ComponentStateEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.ConnectionEndpointMerger;
|
||||
|
@ -60,9 +61,14 @@ import org.apache.nifi.cluster.coordination.http.endpoints.RemoteProcessGroupsEn
|
|||
import org.apache.nifi.cluster.coordination.http.endpoints.ReportingTaskEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.ReportingTaskTypesEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.ReportingTasksEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.SearchUsersEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.StatusHistoryEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.SystemDiagnosticsEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.TemplatesEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.UserEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.UserGroupEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.UserGroupsEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.endpoints.UsersEndpointMerger;
|
||||
import org.apache.nifi.cluster.coordination.http.replication.RequestReplicator;
|
||||
import org.apache.nifi.cluster.manager.NodeResponse;
|
||||
import org.apache.nifi.stream.io.NullOutputStream;
|
||||
|
@ -141,6 +147,12 @@ public class StandardHttpResponseMapper implements HttpResponseMapper {
|
|||
endpointMergers.add(new FunnelEndpointMerger());
|
||||
endpointMergers.add(new FunnelsEndpointMerger());
|
||||
endpointMergers.add(new ControllerEndpointMerger());
|
||||
endpointMergers.add(new UsersEndpointMerger());
|
||||
endpointMergers.add(new UserEndpointMerger());
|
||||
endpointMergers.add(new UserGroupsEndpointMerger());
|
||||
endpointMergers.add(new UserGroupEndpointMerger());
|
||||
endpointMergers.add(new AccessPolicyEndpointMerger());
|
||||
endpointMergers.add(new SearchUsersEndpointMerger());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.nifi.cluster.coordination.http.endpoints;
|
||||
|
||||
import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
|
||||
import org.apache.nifi.cluster.manager.AccessPolicyEntityMerger;
|
||||
import org.apache.nifi.cluster.manager.NodeResponse;
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.entity.AccessPolicyEntity;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class AccessPolicyEndpointMerger extends AbstractSingleEntityEndpoint<AccessPolicyEntity> implements EndpointResponseMerger {
|
||||
public static final Pattern ACCESS_POLICIES_URI_PATTERN = Pattern.compile("/nifi-api/policies");
|
||||
public static final Pattern ACCESS_POLICY_URI_PATTERN = Pattern.compile("/nifi-api/policies/[a-f0-9\\-]{36}");
|
||||
public static final Pattern ACCESS_POLICY_LOOKUP_URI_PATTERN = Pattern.compile("/nifi-api/policies/(?:read|write)/(?:[\\w-]+?/?)+");
|
||||
private final AccessPolicyEntityMerger accessPolicyEntityMerger = new AccessPolicyEntityMerger();
|
||||
|
||||
@Override
|
||||
public boolean canHandle(final URI uri, final String method) {
|
||||
if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && (ACCESS_POLICY_URI_PATTERN.matcher(uri.getPath()).matches())) {
|
||||
return true;
|
||||
} else if ("GET".equalsIgnoreCase(method) && ACCESS_POLICY_LOOKUP_URI_PATTERN.matcher(uri.getPath()).matches()) {
|
||||
return true;
|
||||
} else if ("POST".equalsIgnoreCase(method) && ACCESS_POLICIES_URI_PATTERN.matcher(uri.getPath()).matches()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<AccessPolicyEntity> getEntityClass() {
|
||||
return AccessPolicyEntity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mergeResponses(final AccessPolicyEntity clientEntity, final Map<NodeIdentifier, AccessPolicyEntity> entityMap,
|
||||
final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses) {
|
||||
|
||||
accessPolicyEntityMerger.merge(clientEntity, entityMap);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.nifi.cluster.coordination.http.endpoints;
|
||||
|
||||
import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
|
||||
import org.apache.nifi.cluster.manager.NodeResponse;
|
||||
import org.apache.nifi.web.api.entity.TenantEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantsEntity;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SearchUsersEndpointMerger implements EndpointResponseMerger {
|
||||
public static final Pattern SEARCH_TENANTS_URI_PATTERN = Pattern.compile("/nifi-api/tenants/search-results");
|
||||
|
||||
@Override
|
||||
public boolean canHandle(final URI uri, final String method) {
|
||||
return "GET".equalsIgnoreCase(method) && SEARCH_TENANTS_URI_PATTERN.matcher(uri.getPath()).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final NodeResponse merge(final URI uri, final String method, final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses, final NodeResponse clientResponse) {
|
||||
if (!canHandle(uri, method)) {
|
||||
throw new IllegalArgumentException("Cannot use Endpoint Mapper of type " + getClass().getSimpleName() + " to map responses for URI " + uri + ", HTTP Method " + method);
|
||||
}
|
||||
|
||||
final TenantsEntity responseEntity = clientResponse.getClientResponse().getEntity(TenantsEntity.class);
|
||||
final Collection<TenantEntity> userEntities = responseEntity.getUsers();
|
||||
final Collection<TenantEntity> userGroupEntities = responseEntity.getUserGroups();
|
||||
|
||||
for (final NodeResponse nodeResponse : successfulResponses) {
|
||||
final TenantsEntity nodeResponseEntity = nodeResponse == clientResponse ? responseEntity : nodeResponse.getClientResponse().getEntity(TenantsEntity.class);
|
||||
|
||||
// only retain users/groups that all nodes agree on
|
||||
userEntities.retainAll(nodeResponseEntity.getUsers());
|
||||
userGroupEntities.retainAll(nodeResponseEntity.getUserGroups());
|
||||
}
|
||||
|
||||
// create a new client response
|
||||
return new NodeResponse(clientResponse, responseEntity);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.nifi.cluster.coordination.http.endpoints;
|
||||
|
||||
import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
|
||||
import org.apache.nifi.cluster.manager.NodeResponse;
|
||||
import org.apache.nifi.cluster.manager.UserEntityMerger;
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.entity.UserEntity;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class UserEndpointMerger extends AbstractSingleEntityEndpoint<UserEntity> implements EndpointResponseMerger {
|
||||
public static final Pattern USERS_URI_PATTERN = Pattern.compile("/nifi-api/tenants/users");
|
||||
public static final Pattern USER_URI_PATTERN = Pattern.compile("/nifi-api/tenants/users/[a-f0-9\\-]{36}");
|
||||
private final UserEntityMerger userEntityMerger = new UserEntityMerger();
|
||||
|
||||
@Override
|
||||
public boolean canHandle(final URI uri, final String method) {
|
||||
if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && (USER_URI_PATTERN.matcher(uri.getPath()).matches())) {
|
||||
return true;
|
||||
} else if ("POST".equalsIgnoreCase(method) && USERS_URI_PATTERN.matcher(uri.getPath()).matches()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<UserEntity> getEntityClass() {
|
||||
return UserEntity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mergeResponses(final UserEntity clientEntity, final Map<NodeIdentifier, UserEntity> entityMap,
|
||||
final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses) {
|
||||
|
||||
userEntityMerger.merge(clientEntity, entityMap);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.nifi.cluster.coordination.http.endpoints;
|
||||
|
||||
import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
|
||||
import org.apache.nifi.cluster.manager.NodeResponse;
|
||||
import org.apache.nifi.cluster.manager.UserGroupEntityMerger;
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.entity.UserGroupEntity;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class UserGroupEndpointMerger extends AbstractSingleEntityEndpoint<UserGroupEntity> implements EndpointResponseMerger {
|
||||
public static final Pattern USER_GROUPS_URI_PATTERN = Pattern.compile("/nifi-api/tenants/user-groups");
|
||||
public static final Pattern USER_GROUP_URI_PATTERN = Pattern.compile("/nifi-api/tenants/user-groups/[a-f0-9\\-]{36}");
|
||||
private final UserGroupEntityMerger userGroupEntityMerger = new UserGroupEntityMerger();
|
||||
|
||||
@Override
|
||||
public boolean canHandle(final URI uri, final String method) {
|
||||
if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && (USER_GROUP_URI_PATTERN.matcher(uri.getPath()).matches())) {
|
||||
return true;
|
||||
} else if ("POST".equalsIgnoreCase(method) && USER_GROUPS_URI_PATTERN.matcher(uri.getPath()).matches()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<UserGroupEntity> getEntityClass() {
|
||||
return UserGroupEntity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mergeResponses(final UserGroupEntity clientEntity, final Map<NodeIdentifier, UserGroupEntity> entityMap,
|
||||
final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses) {
|
||||
|
||||
userGroupEntityMerger.merge(clientEntity, entityMap);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.nifi.cluster.coordination.http.endpoints;
|
||||
|
||||
import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
|
||||
import org.apache.nifi.cluster.manager.NodeResponse;
|
||||
import org.apache.nifi.cluster.manager.UserGroupsEntityMerger;
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.entity.UserGroupEntity;
|
||||
import org.apache.nifi.web.api.entity.UserGroupsEntity;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class UserGroupsEndpointMerger implements EndpointResponseMerger {
|
||||
public static final Pattern USER_GROUPS_URI_PATTERN = Pattern.compile("/nifi-api/tenants/user-groups");
|
||||
|
||||
@Override
|
||||
public boolean canHandle(final URI uri, final String method) {
|
||||
return "GET".equalsIgnoreCase(method) && USER_GROUPS_URI_PATTERN.matcher(uri.getPath()).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final NodeResponse merge(final URI uri, final String method, final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses, final NodeResponse clientResponse) {
|
||||
if (!canHandle(uri, method)) {
|
||||
throw new IllegalArgumentException("Cannot use Endpoint Mapper of type " + getClass().getSimpleName() + " to map responses for URI " + uri + ", HTTP Method " + method);
|
||||
}
|
||||
|
||||
final UserGroupsEntity responseEntity = clientResponse.getClientResponse().getEntity(UserGroupsEntity.class);
|
||||
final Collection<UserGroupEntity> userGroupEntities = responseEntity.getUserGroups();
|
||||
|
||||
final Map<String, Map<NodeIdentifier, UserGroupEntity>> entityMap = new HashMap<>();
|
||||
for (final NodeResponse nodeResponse : successfulResponses) {
|
||||
final UserGroupsEntity nodeResponseEntity = nodeResponse == clientResponse ? responseEntity : nodeResponse.getClientResponse().getEntity(UserGroupsEntity.class);
|
||||
final Collection<UserGroupEntity> nodeUserGroupEntities = nodeResponseEntity.getUserGroups();
|
||||
|
||||
// only retain user groups that all nodes agree on
|
||||
userGroupEntities.retainAll(nodeUserGroupEntities);
|
||||
|
||||
for (final UserGroupEntity nodeUserGroupEntity : nodeUserGroupEntities) {
|
||||
final NodeIdentifier nodeId = nodeResponse.getNodeId();
|
||||
Map<NodeIdentifier, UserGroupEntity> innerMap = entityMap.get(nodeId);
|
||||
if (innerMap == null) {
|
||||
innerMap = new HashMap<>();
|
||||
entityMap.put(nodeUserGroupEntity.getId(), innerMap);
|
||||
}
|
||||
|
||||
innerMap.put(nodeResponse.getNodeId(), nodeUserGroupEntity);
|
||||
}
|
||||
}
|
||||
|
||||
UserGroupsEntityMerger.mergeUserGroups(userGroupEntities, entityMap);
|
||||
|
||||
// create a new client response
|
||||
return new NodeResponse(clientResponse, responseEntity);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.nifi.cluster.coordination.http.endpoints;
|
||||
|
||||
import org.apache.nifi.cluster.coordination.http.EndpointResponseMerger;
|
||||
import org.apache.nifi.cluster.manager.NodeResponse;
|
||||
import org.apache.nifi.cluster.manager.UsersEntityMerger;
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.entity.UserEntity;
|
||||
import org.apache.nifi.web.api.entity.UsersEntity;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class UsersEndpointMerger implements EndpointResponseMerger {
|
||||
public static final Pattern TENANTS_URI_PATTERN = Pattern.compile("/nifi-api/tenants/users");
|
||||
|
||||
@Override
|
||||
public boolean canHandle(final URI uri, final String method) {
|
||||
return "GET".equalsIgnoreCase(method) && TENANTS_URI_PATTERN.matcher(uri.getPath()).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final NodeResponse merge(final URI uri, final String method, final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses, final NodeResponse clientResponse) {
|
||||
if (!canHandle(uri, method)) {
|
||||
throw new IllegalArgumentException("Cannot use Endpoint Mapper of type " + getClass().getSimpleName() + " to map responses for URI " + uri + ", HTTP Method " + method);
|
||||
}
|
||||
|
||||
final UsersEntity responseEntity = clientResponse.getClientResponse().getEntity(UsersEntity.class);
|
||||
final Collection<UserEntity> userEntities = responseEntity.getUsers();
|
||||
|
||||
final Map<String, Map<NodeIdentifier, UserEntity>> entityMap = new HashMap<>();
|
||||
for (final NodeResponse nodeResponse : successfulResponses) {
|
||||
final UsersEntity nodeResponseEntity = nodeResponse == clientResponse ? responseEntity : nodeResponse.getClientResponse().getEntity(UsersEntity.class);
|
||||
final Collection<UserEntity> nodeUserEntities = nodeResponseEntity.getUsers();
|
||||
|
||||
// only retain users that all nodes agree on
|
||||
userEntities.retainAll(nodeUserEntities);
|
||||
|
||||
for (final UserEntity nodeUserEntity : nodeUserEntities) {
|
||||
final NodeIdentifier nodeId = nodeResponse.getNodeId();
|
||||
Map<NodeIdentifier, UserEntity> innerMap = entityMap.get(nodeId);
|
||||
if (innerMap == null) {
|
||||
innerMap = new HashMap<>();
|
||||
entityMap.put(nodeUserEntity.getId(), innerMap);
|
||||
}
|
||||
|
||||
innerMap.put(nodeResponse.getNodeId(), nodeUserEntity);
|
||||
}
|
||||
}
|
||||
|
||||
UsersEntityMerger.mergeUsers(userEntities, entityMap);
|
||||
|
||||
// create a new client response
|
||||
return new NodeResponse(clientResponse, responseEntity);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.cluster.manager;
|
||||
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.dto.AccessPolicyDTO;
|
||||
import org.apache.nifi.web.api.entity.AccessPolicyEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantEntity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class AccessPolicyEntityMerger implements ComponentEntityMerger<AccessPolicyEntity> {
|
||||
@Override
|
||||
public void merge(AccessPolicyEntity clientEntity, Map<NodeIdentifier, AccessPolicyEntity> entityMap) {
|
||||
ComponentEntityMerger.super.merge(clientEntity, entityMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the AccessPolicyEntity responses.
|
||||
*
|
||||
* @param clientEntity the entity being returned to the client
|
||||
* @param entityMap all node responses
|
||||
*/
|
||||
public void mergeComponents(final AccessPolicyEntity clientEntity, final Map<NodeIdentifier, AccessPolicyEntity> entityMap) {
|
||||
final AccessPolicyDTO clientDto = clientEntity.getComponent();
|
||||
final Map<NodeIdentifier, AccessPolicyDTO> dtoMap = new HashMap<>();
|
||||
for (final Map.Entry<NodeIdentifier, AccessPolicyEntity> entry : entityMap.entrySet()) {
|
||||
final AccessPolicyEntity nodeAccessPolicyEntity = entry.getValue();
|
||||
final AccessPolicyDTO nodeAccessPolicyDto = nodeAccessPolicyEntity.getComponent();
|
||||
dtoMap.put(entry.getKey(), nodeAccessPolicyDto);
|
||||
}
|
||||
|
||||
mergeDtos(clientDto, dtoMap);
|
||||
}
|
||||
|
||||
private static void mergeDtos(final AccessPolicyDTO clientDto, final Map<NodeIdentifier, AccessPolicyDTO> dtoMap) {
|
||||
// if unauthorized for the client dto, simple return
|
||||
if (clientDto == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Set<TenantEntity> users = new HashSet<>(clientDto.getUsers());
|
||||
final Set<TenantEntity> userGroups = new HashSet<>(clientDto.getUserGroups());
|
||||
|
||||
for (final Map.Entry<NodeIdentifier, AccessPolicyDTO> nodeEntry : dtoMap.entrySet()) {
|
||||
final AccessPolicyDTO nodeAccessPolicy = nodeEntry.getValue();
|
||||
|
||||
if (nodeAccessPolicy != null) {
|
||||
users.retainAll(nodeAccessPolicy.getUsers());
|
||||
userGroups.retainAll(nodeAccessPolicy.getUserGroups());
|
||||
}
|
||||
}
|
||||
|
||||
clientDto.setUsers(users);
|
||||
clientDto.setUserGroups(userGroups);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.cluster.manager;
|
||||
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.dto.UserDTO;
|
||||
import org.apache.nifi.web.api.entity.AccessPolicySummaryEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantEntity;
|
||||
import org.apache.nifi.web.api.entity.UserEntity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class UserEntityMerger implements ComponentEntityMerger<UserEntity> {
|
||||
|
||||
@Override
|
||||
public void merge(UserEntity clientEntity, Map<NodeIdentifier, UserEntity> entityMap) {
|
||||
ComponentEntityMerger.super.merge(clientEntity, entityMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the UserEntity responses.
|
||||
*
|
||||
* @param clientEntity the entity being returned to the client
|
||||
* @param entityMap all node responses
|
||||
*/
|
||||
public void mergeComponents(final UserEntity clientEntity, final Map<NodeIdentifier, UserEntity> entityMap) {
|
||||
final UserDTO clientDto = clientEntity.getComponent();
|
||||
final Map<NodeIdentifier, UserDTO> dtoMap = new HashMap<>();
|
||||
for (final Map.Entry<NodeIdentifier, UserEntity> entry : entityMap.entrySet()) {
|
||||
final UserEntity nodeUserEntity = entry.getValue();
|
||||
final UserDTO nodeUserDto = nodeUserEntity.getComponent();
|
||||
dtoMap.put(entry.getKey(), nodeUserDto);
|
||||
}
|
||||
|
||||
mergeDtos(clientDto, dtoMap);
|
||||
}
|
||||
|
||||
private static void mergeDtos(final UserDTO clientDto, final Map<NodeIdentifier, UserDTO> dtoMap) {
|
||||
// if unauthorized for the client dto, simple return
|
||||
if (clientDto == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Set<AccessPolicySummaryEntity> accessPolicyEntities = new HashSet<>(clientDto.getAccessPolicies());
|
||||
final Set<TenantEntity> tenantEntities = new HashSet<>(clientDto.getUserGroups());
|
||||
|
||||
for (final Map.Entry<NodeIdentifier, UserDTO> nodeEntry : dtoMap.entrySet()) {
|
||||
final UserDTO nodeUser = nodeEntry.getValue();
|
||||
|
||||
if (nodeUser != null) {
|
||||
accessPolicyEntities.retainAll(nodeUser.getAccessPolicies());
|
||||
tenantEntities.retainAll(nodeUser.getUserGroups());
|
||||
}
|
||||
}
|
||||
|
||||
clientDto.setAccessPolicies(accessPolicyEntities);
|
||||
clientDto.setUserGroups(tenantEntities);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.cluster.manager;
|
||||
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.dto.UserGroupDTO;
|
||||
import org.apache.nifi.web.api.entity.AccessPolicyEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantEntity;
|
||||
import org.apache.nifi.web.api.entity.UserGroupEntity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class UserGroupEntityMerger implements ComponentEntityMerger<UserGroupEntity> {
|
||||
@Override
|
||||
public void merge(UserGroupEntity clientEntity, Map<NodeIdentifier, UserGroupEntity> entityMap) {
|
||||
ComponentEntityMerger.super.merge(clientEntity, entityMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the UserGroupEntity responses.
|
||||
*
|
||||
* @param clientEntity the entity being returned to the client
|
||||
* @param entityMap all node responses
|
||||
*/
|
||||
public void mergeComponents(final UserGroupEntity clientEntity, final Map<NodeIdentifier, UserGroupEntity> entityMap) {
|
||||
final UserGroupDTO clientDto = clientEntity.getComponent();
|
||||
final Map<NodeIdentifier, UserGroupDTO> dtoMap = new HashMap<>();
|
||||
for (final Map.Entry<NodeIdentifier, UserGroupEntity> entry : entityMap.entrySet()) {
|
||||
final UserGroupEntity nodeUserGroupEntity = entry.getValue();
|
||||
final UserGroupDTO nodeUserGroupDto = nodeUserGroupEntity.getComponent();
|
||||
dtoMap.put(entry.getKey(), nodeUserGroupDto);
|
||||
}
|
||||
|
||||
mergeDtos(clientDto, dtoMap);
|
||||
}
|
||||
|
||||
private static void mergeDtos(final UserGroupDTO clientDto, final Map<NodeIdentifier, UserGroupDTO> dtoMap) {
|
||||
// if unauthorized for the client dto, simple return
|
||||
if (clientDto == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Set<AccessPolicyEntity> accessPolicyEntities = new HashSet<>(clientDto.getAccessPolicies());
|
||||
final Set<TenantEntity> userEntities = new HashSet<>(clientDto.getUsers());
|
||||
|
||||
for (final Map.Entry<NodeIdentifier, UserGroupDTO> nodeEntry : dtoMap.entrySet()) {
|
||||
final UserGroupDTO nodeUserGroup = nodeEntry.getValue();
|
||||
|
||||
if (nodeUserGroup != null) {
|
||||
accessPolicyEntities.retainAll(nodeUserGroup.getAccessPolicies());
|
||||
userEntities.retainAll(nodeUserGroup.getUsers());
|
||||
}
|
||||
}
|
||||
|
||||
clientDto.setAccessPolicies(accessPolicyEntities);
|
||||
clientDto.setUsers(userEntities);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.cluster.manager;
|
||||
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.entity.UserGroupEntity;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
public class UserGroupsEntityMerger implements ComponentEntityMerger<UserGroupEntity> {
|
||||
private static final UserGroupEntityMerger userGroupEntityMerger = new UserGroupEntityMerger();
|
||||
|
||||
/**
|
||||
* Merges multiple UserGroupEntity responses.
|
||||
*
|
||||
* @param userGroupEntities entities being returned to the client
|
||||
* @param entityMap all node responses
|
||||
*/
|
||||
public static void mergeUserGroups(final Collection<UserGroupEntity> userGroupEntities, final Map<String, Map<NodeIdentifier, UserGroupEntity>> entityMap) {
|
||||
for (final UserGroupEntity entity : userGroupEntities) {
|
||||
userGroupEntityMerger.merge(entity, entityMap.get(entity.getId()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.cluster.manager;
|
||||
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.entity.UserEntity;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
public class UsersEntityMerger implements ComponentEntityMerger<UserEntity> {
|
||||
private static final UserEntityMerger userEntityMerger = new UserEntityMerger();
|
||||
|
||||
/**
|
||||
* Merges multiple UserEntity responses.
|
||||
*
|
||||
* @param userEntities entities being returned to the client
|
||||
* @param entityMap all node responses
|
||||
*/
|
||||
public static void mergeUsers(final Collection<UserEntity> userEntities, final Map<String, Map<NodeIdentifier, UserEntity>> entityMap) {
|
||||
for (final UserEntity entity : userEntities) {
|
||||
userEntityMerger.merge(entity, entityMap.get(entity.getId()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.cluster.coordination.http.endpoints;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class AccessPolicyEndpointMergerTest {
|
||||
|
||||
@Test
|
||||
public void testCanHandle() throws Exception {
|
||||
final AccessPolicyEndpointMerger merger = new AccessPolicyEndpointMerger();
|
||||
assertTrue(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies"), "POST"));
|
||||
assertFalse(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies"), "GET"));
|
||||
assertFalse(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies"), "PUT"));
|
||||
assertTrue(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies/" + UUID.randomUUID().toString()), "PUT"));
|
||||
assertFalse(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies/Read/flow"), "GET"));
|
||||
assertTrue(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies/read/flow"), "GET"));
|
||||
assertTrue(merger.canHandle(URI.create("http://localhost:8080/nifi-api/policies/read/processors/" + UUID.randomUUID().toString()), "GET"));
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@ import org.apache.commons.collections4.map.MultiValueMap;
|
|||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.NiFiUserDetails;
|
||||
import org.apache.nifi.authorization.user.StandardNiFiUser;
|
||||
import org.apache.nifi.authorization.user.StandardNiFiUser.Builder;
|
||||
import org.apache.nifi.cluster.coordination.ClusterCoordinator;
|
||||
import org.apache.nifi.cluster.coordination.node.NodeConnectionState;
|
||||
import org.apache.nifi.cluster.coordination.node.NodeConnectionStatus;
|
||||
|
@ -157,9 +158,9 @@ public class TestThreadPoolRequestReplicator {
|
|||
final Entity entity = new ProcessorEntity();
|
||||
|
||||
// set the user
|
||||
final NiFiUser proxy2 = new StandardNiFiUser(proxyIdentity2);
|
||||
final NiFiUser proxy1 = new StandardNiFiUser(proxyIdentity1, proxy2);
|
||||
final NiFiUser user = new StandardNiFiUser(userIdentity, proxy1);
|
||||
final NiFiUser proxy2 = new Builder().identity(proxyIdentity2).build();
|
||||
final NiFiUser proxy1 = new Builder().identity(proxyIdentity1).chain(proxy2).build();
|
||||
final NiFiUser user = new Builder().identity(userIdentity).chain(proxy1).build();
|
||||
final Authentication authentication = new NiFiAuthenticationToken(new NiFiUserDetails(user));
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.cluster.manager;
|
||||
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.dto.AccessPolicyDTO;
|
||||
import org.apache.nifi.web.api.dto.PermissionsDTO;
|
||||
import org.apache.nifi.web.api.dto.TenantDTO;
|
||||
import org.apache.nifi.web.api.entity.AccessPolicyEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantEntity;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
public class AccessPolicyEntityMergerTest {
|
||||
|
||||
@Test
|
||||
public void testMergeAccessPolicy() throws Exception {
|
||||
final NodeIdentifier node1 = new NodeIdentifier("node-1", "host-1", 8080, "host-1", 19998, null, null, null, false);
|
||||
final NodeIdentifier node2 = new NodeIdentifier("node-2", "host-2", 8081, "host-2", 19999, null, null, null, false);
|
||||
|
||||
final PermissionsDTO permissed = new PermissionsDTO();
|
||||
permissed.setCanRead(true);
|
||||
permissed.setCanWrite(true);
|
||||
|
||||
final TenantDTO user1DTO = new TenantDTO();
|
||||
user1DTO.setId("user-1");
|
||||
|
||||
final TenantEntity user1Entity = new TenantEntity();
|
||||
user1Entity.setPermissions(permissed);
|
||||
user1Entity.setId(user1DTO.getId());
|
||||
user1Entity.setComponent(user1DTO);
|
||||
|
||||
final TenantDTO user2DTO = new TenantDTO();
|
||||
user1DTO.setId("user-2");
|
||||
|
||||
final TenantEntity user2Entity = new TenantEntity();
|
||||
user2Entity.setPermissions(permissed);
|
||||
user2Entity.setId(user2DTO.getId());
|
||||
user2Entity.setComponent(user2DTO);
|
||||
|
||||
final AccessPolicyDTO accessPolicy1DTO = new AccessPolicyDTO();
|
||||
accessPolicy1DTO.setId("policy-1");
|
||||
accessPolicy1DTO.setUsers(Stream.of(user1Entity, user2Entity).collect(Collectors.toSet()));
|
||||
accessPolicy1DTO.setUserGroups(Stream.of(user2Entity).collect(Collectors.toSet()));
|
||||
|
||||
final AccessPolicyEntity accessPolicy1Entity = new AccessPolicyEntity();
|
||||
accessPolicy1Entity.setPermissions(permissed);
|
||||
accessPolicy1Entity.setId(accessPolicy1DTO.getId());
|
||||
accessPolicy1Entity.setComponent(accessPolicy1DTO);
|
||||
|
||||
final AccessPolicyDTO accessPolicy2DTO = new AccessPolicyDTO();
|
||||
accessPolicy2DTO.setId("policy-2");
|
||||
accessPolicy2DTO.setUsers(Stream.of(user1Entity).collect(Collectors.toSet()));
|
||||
accessPolicy2DTO.setUserGroups(Stream.of(user1Entity, user2Entity).collect(Collectors.toSet()));
|
||||
|
||||
final AccessPolicyEntity accessPolicy2Entity = new AccessPolicyEntity();
|
||||
accessPolicy2Entity.setPermissions(permissed);
|
||||
accessPolicy2Entity.setId(accessPolicy2DTO.getId());
|
||||
accessPolicy2Entity.setComponent(accessPolicy2DTO);
|
||||
|
||||
final Map<NodeIdentifier, AccessPolicyEntity> nodeMap = new HashMap<>();
|
||||
nodeMap.put(node1, accessPolicy1Entity);
|
||||
nodeMap.put(node2, accessPolicy2Entity);
|
||||
|
||||
final AccessPolicyEntityMerger merger = new AccessPolicyEntityMerger();
|
||||
merger.merge(accessPolicy1Entity, nodeMap);
|
||||
|
||||
assertEquals(1, accessPolicy1DTO.getUserGroups().size());
|
||||
assertTrue(accessPolicy1DTO.getUsers().contains(user1Entity));
|
||||
|
||||
assertEquals(1, accessPolicy1DTO.getUserGroups().size());
|
||||
assertTrue(accessPolicy1DTO.getUserGroups().contains(user2Entity));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.cluster.manager;
|
||||
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.dto.AccessPolicySummaryDTO;
|
||||
import org.apache.nifi.web.api.dto.PermissionsDTO;
|
||||
import org.apache.nifi.web.api.dto.TenantDTO;
|
||||
import org.apache.nifi.web.api.dto.UserDTO;
|
||||
import org.apache.nifi.web.api.entity.AccessPolicySummaryEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantEntity;
|
||||
import org.apache.nifi.web.api.entity.UserEntity;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
public class UserEntityMergerTest {
|
||||
|
||||
@Test
|
||||
public void testMergeAccessPolicy() throws Exception {
|
||||
final NodeIdentifier node1 = new NodeIdentifier("node-1", "host-1", 8080, "host-1", 19998, null, null, null, false);
|
||||
final NodeIdentifier node2 = new NodeIdentifier("node-2", "host-2", 8081, "host-2", 19999, null, null, null, false);
|
||||
|
||||
final PermissionsDTO permissed = new PermissionsDTO();
|
||||
permissed.setCanRead(true);
|
||||
permissed.setCanWrite(true);
|
||||
|
||||
final TenantDTO userGroup1DTO = new TenantDTO();
|
||||
userGroup1DTO.setId("user-group-1");
|
||||
|
||||
final TenantEntity userGroup1Entity = new TenantEntity();
|
||||
userGroup1Entity.setPermissions(permissed);
|
||||
userGroup1Entity.setId(userGroup1DTO.getId());
|
||||
userGroup1Entity.setComponent(userGroup1DTO);
|
||||
|
||||
final TenantDTO userGroup2DTO = new TenantDTO();
|
||||
userGroup1DTO.setId("user-group-2");
|
||||
|
||||
final TenantEntity userGroup2Entity = new TenantEntity();
|
||||
userGroup2Entity.setPermissions(permissed);
|
||||
userGroup2Entity.setId(userGroup2DTO.getId());
|
||||
userGroup2Entity.setComponent(userGroup2DTO);
|
||||
|
||||
final AccessPolicySummaryDTO policy1DTO = new AccessPolicySummaryDTO();
|
||||
policy1DTO.setId("policy-1");
|
||||
|
||||
final AccessPolicySummaryEntity policy1Entity = new AccessPolicySummaryEntity();
|
||||
policy1Entity.setPermissions(permissed);
|
||||
policy1Entity.setId(policy1DTO.getId());
|
||||
policy1Entity.setComponent(policy1DTO);
|
||||
|
||||
final AccessPolicySummaryDTO policy2DTO = new AccessPolicySummaryDTO();
|
||||
policy2DTO.setId("policy-2");
|
||||
|
||||
final AccessPolicySummaryEntity policy2Entity = new AccessPolicySummaryEntity();
|
||||
policy2Entity.setPermissions(permissed);
|
||||
policy2Entity.setId(policy2DTO.getId());
|
||||
policy2Entity.setComponent(policy2DTO);
|
||||
|
||||
final UserDTO user1DTO = new UserDTO();
|
||||
user1DTO.setId("user-1");
|
||||
user1DTO.setAccessPolicies(Stream.of(policy1Entity, policy2Entity).collect(Collectors.toSet()));
|
||||
user1DTO.setUserGroups(Stream.of(userGroup2Entity).collect(Collectors.toSet()));
|
||||
|
||||
final UserEntity user1Entity = new UserEntity();
|
||||
user1Entity.setPermissions(permissed);
|
||||
user1Entity.setId(user1DTO.getId());
|
||||
user1Entity.setComponent(user1DTO);
|
||||
|
||||
final UserDTO user2DTO = new UserDTO();
|
||||
user2DTO.setId("user-2");
|
||||
user2DTO.setAccessPolicies(Stream.of(policy1Entity).collect(Collectors.toSet()));
|
||||
user2DTO.setUserGroups(Stream.of(userGroup1Entity, userGroup2Entity).collect(Collectors.toSet()));
|
||||
|
||||
final UserEntity user2Entity = new UserEntity();
|
||||
user2Entity.setPermissions(permissed);
|
||||
user2Entity.setId(user2DTO.getId());
|
||||
user2Entity.setComponent(user2DTO);
|
||||
|
||||
final Map<NodeIdentifier, UserEntity> nodeMap = new HashMap<>();
|
||||
nodeMap.put(node1, user1Entity);
|
||||
nodeMap.put(node2, user2Entity);
|
||||
|
||||
final UserEntityMerger merger = new UserEntityMerger();
|
||||
merger.merge(user1Entity, nodeMap);
|
||||
|
||||
assertEquals(1, user1DTO.getUserGroups().size());
|
||||
assertTrue(user1DTO.getAccessPolicies().contains(policy1Entity));
|
||||
|
||||
assertEquals(1, user1DTO.getUserGroups().size());
|
||||
assertTrue(user1DTO.getUserGroups().contains(userGroup2Entity));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.cluster.manager;
|
||||
|
||||
import org.apache.nifi.cluster.protocol.NodeIdentifier;
|
||||
import org.apache.nifi.web.api.dto.AccessPolicyDTO;
|
||||
import org.apache.nifi.web.api.dto.PermissionsDTO;
|
||||
import org.apache.nifi.web.api.dto.TenantDTO;
|
||||
import org.apache.nifi.web.api.dto.UserGroupDTO;
|
||||
import org.apache.nifi.web.api.entity.AccessPolicyEntity;
|
||||
import org.apache.nifi.web.api.entity.TenantEntity;
|
||||
import org.apache.nifi.web.api.entity.UserGroupEntity;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
public class UserGroupEntityMergerTest {
|
||||
|
||||
@Test
|
||||
public void testMergeAccessPolicy() throws Exception {
|
||||
final NodeIdentifier node1 = new NodeIdentifier("node-1", "host-1", 8080, "host-1", 19998, null, null, null, false);
|
||||
final NodeIdentifier node2 = new NodeIdentifier("node-2", "host-2", 8081, "host-2", 19999, null, null, null, false);
|
||||
|
||||
final PermissionsDTO permissed = new PermissionsDTO();
|
||||
permissed.setCanRead(true);
|
||||
permissed.setCanWrite(true);
|
||||
|
||||
final TenantDTO user1DTO = new TenantDTO();
|
||||
user1DTO.setId("user-1");
|
||||
|
||||
final TenantEntity user1Entity = new TenantEntity();
|
||||
user1Entity.setPermissions(permissed);
|
||||
user1Entity.setId(user1DTO.getId());
|
||||
user1Entity.setComponent(user1DTO);
|
||||
|
||||
final TenantDTO user2DTO = new TenantDTO();
|
||||
user1DTO.setId("user-2");
|
||||
|
||||
final TenantEntity user2Entity = new TenantEntity();
|
||||
user2Entity.setPermissions(permissed);
|
||||
user2Entity.setId(user2DTO.getId());
|
||||
user2Entity.setComponent(user2DTO);
|
||||
|
||||
final AccessPolicyDTO policy1DTO = new AccessPolicyDTO();
|
||||
policy1DTO.setId("policy-1");
|
||||
|
||||
final AccessPolicyEntity policy1Entity = new AccessPolicyEntity();
|
||||
policy1Entity.setPermissions(permissed);
|
||||
policy1Entity.setId(policy1DTO.getId());
|
||||
policy1Entity.setComponent(policy1DTO);
|
||||
|
||||
final AccessPolicyDTO policy2DTO = new AccessPolicyDTO();
|
||||
policy2DTO.setId("policy-2");
|
||||
|
||||
final AccessPolicyEntity policy2Entity = new AccessPolicyEntity();
|
||||
policy2Entity.setPermissions(permissed);
|
||||
policy2Entity.setId(policy2DTO.getId());
|
||||
policy2Entity.setComponent(policy2DTO);
|
||||
|
||||
final UserGroupDTO userGroup1DTO = new UserGroupDTO();
|
||||
userGroup1DTO.setId("user-1");
|
||||
userGroup1DTO.setAccessPolicies(Stream.of(policy1Entity, policy2Entity).collect(Collectors.toSet()));
|
||||
userGroup1DTO.setUsers(Stream.of(user2Entity).collect(Collectors.toSet()));
|
||||
|
||||
final UserGroupEntity userGroup1Entity = new UserGroupEntity();
|
||||
userGroup1Entity.setPermissions(permissed);
|
||||
userGroup1Entity.setId(userGroup1DTO.getId());
|
||||
userGroup1Entity.setComponent(userGroup1DTO);
|
||||
|
||||
final UserGroupDTO userGroup2DTO = new UserGroupDTO();
|
||||
userGroup2DTO.setId("user-2");
|
||||
userGroup2DTO.setAccessPolicies(Stream.of(policy1Entity).collect(Collectors.toSet()));
|
||||
userGroup2DTO.setUsers(Stream.of(user1Entity, user2Entity).collect(Collectors.toSet()));
|
||||
|
||||
final UserGroupEntity userGroup2Entity = new UserGroupEntity();
|
||||
userGroup2Entity.setPermissions(permissed);
|
||||
userGroup2Entity.setId(userGroup2DTO.getId());
|
||||
userGroup2Entity.setComponent(userGroup2DTO);
|
||||
|
||||
final Map<NodeIdentifier, UserGroupEntity> nodeMap = new HashMap<>();
|
||||
nodeMap.put(node1, userGroup1Entity);
|
||||
nodeMap.put(node2, userGroup2Entity);
|
||||
|
||||
final UserGroupEntityMerger merger = new UserGroupEntityMerger();
|
||||
merger.merge(userGroup1Entity, nodeMap);
|
||||
|
||||
assertEquals(1, userGroup1DTO.getUsers().size());
|
||||
assertTrue(userGroup1DTO.getAccessPolicies().contains(policy1Entity));
|
||||
|
||||
assertEquals(1, userGroup1DTO.getUsers().size());
|
||||
assertTrue(userGroup1DTO.getUsers().contains(user2Entity));
|
||||
}
|
||||
|
||||
}
|
|
@ -32,7 +32,7 @@ public interface DataFlow {
|
|||
|
||||
/**
|
||||
* @return the raw byte array of the Authorizer's fingerprint,
|
||||
* null when not using a sub-class of AbstractPolicyBasedAuthorizer
|
||||
* null when not using a ManagedAuthorizer
|
||||
*/
|
||||
public byte[] getAuthorizerFingerprint();
|
||||
|
||||
|
|
|
@ -188,6 +188,10 @@
|
|||
<version>4.3.1.201605051710-r</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-authorizer</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
package org.apache.nifi.controller;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.AuthorizerCapabilityDetection;
|
||||
import org.apache.nifi.authorization.ManagedAuthorizer;
|
||||
import org.apache.nifi.bundle.Bundle;
|
||||
import org.apache.nifi.cluster.ConnectionException;
|
||||
import org.apache.nifi.cluster.coordination.ClusterCoordinator;
|
||||
|
@ -572,8 +573,8 @@ public class StandardFlowService implements FlowService, ProtocolHandler {
|
|||
}
|
||||
|
||||
private byte[] getAuthorizerFingerprint() {
|
||||
final boolean isInternalAuthorizer = (authorizer instanceof AbstractPolicyBasedAuthorizer);
|
||||
return isInternalAuthorizer ? ((AbstractPolicyBasedAuthorizer) authorizer).getFingerprint().getBytes(StandardCharsets.UTF_8) : null;
|
||||
final boolean isInternalAuthorizer = AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer);
|
||||
return isInternalAuthorizer ? ((ManagedAuthorizer) authorizer).getFingerprint().getBytes(StandardCharsets.UTF_8) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,8 +18,10 @@ package org.apache.nifi.controller;
|
|||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.AuthorizerCapabilityDetection;
|
||||
import org.apache.nifi.authorization.ManagedAuthorizer;
|
||||
import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException;
|
||||
import org.apache.nifi.bundle.BundleCoordinate;
|
||||
import org.apache.nifi.cluster.protocol.DataFlow;
|
||||
import org.apache.nifi.cluster.protocol.StandardDataFlow;
|
||||
|
@ -224,15 +226,15 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
|
|||
logger.trace("Getting Authorizer fingerprint from controller");
|
||||
|
||||
final byte[] existingAuthFingerprint;
|
||||
final AbstractPolicyBasedAuthorizer policyBasedAuthorizer;
|
||||
final ManagedAuthorizer managedAuthorizer;
|
||||
final Authorizer authorizer = controller.getAuthorizer();
|
||||
|
||||
if (authorizer instanceof AbstractPolicyBasedAuthorizer) {
|
||||
policyBasedAuthorizer = (AbstractPolicyBasedAuthorizer) authorizer;
|
||||
existingAuthFingerprint = policyBasedAuthorizer.getFingerprint().getBytes(StandardCharsets.UTF_8);
|
||||
if (AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
|
||||
managedAuthorizer = (ManagedAuthorizer) authorizer;
|
||||
existingAuthFingerprint = managedAuthorizer.getFingerprint().getBytes(StandardCharsets.UTF_8);
|
||||
} else {
|
||||
existingAuthFingerprint = null;
|
||||
policyBasedAuthorizer = null;
|
||||
managedAuthorizer = null;
|
||||
}
|
||||
|
||||
final Set<String> missingComponents = new HashSet<>();
|
||||
|
@ -249,7 +251,7 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
|
|||
if (existingFlowEmpty) {
|
||||
configuration = parseFlowBytes(proposedFlow.getFlow());
|
||||
if (configuration != null) {
|
||||
logger.trace("Checking bunde compatibility");
|
||||
logger.trace("Checking bundle compatibility");
|
||||
checkBundleCompatibility(configuration);
|
||||
}
|
||||
} else {
|
||||
|
@ -272,7 +274,7 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
|
|||
|
||||
logger.trace("Checking authorizer inheritability");
|
||||
|
||||
final AuthorizerInheritability authInheritability = checkAuthorizerInheritability(existingDataFlow, proposedFlow);
|
||||
final AuthorizerInheritability authInheritability = checkAuthorizerInheritability(authorizer, existingDataFlow, proposedFlow);
|
||||
if (!authInheritability.isInheritable() && authInheritability.getReason() != null) {
|
||||
throw new UninheritableFlowException("Proposed Authorizer is not inheritable by the flow controller because of Authorizer differences: " + authInheritability.getReason());
|
||||
}
|
||||
|
@ -415,10 +417,10 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
|
|||
}
|
||||
|
||||
// if auths are inheritable and we have a policy based authorizer, then inherit
|
||||
if (authInheritability.isInheritable() && policyBasedAuthorizer != null) {
|
||||
if (authInheritability.isInheritable() && managedAuthorizer != null) {
|
||||
logger.trace("Inheriting authorizations");
|
||||
final String proposedAuthFingerprint = new String(proposedFlow.getAuthorizerFingerprint(), StandardCharsets.UTF_8);
|
||||
policyBasedAuthorizer.inheritFingerprint(proposedAuthFingerprint);
|
||||
managedAuthorizer.inheritFingerprint(proposedAuthFingerprint);
|
||||
}
|
||||
|
||||
logger.debug("Finished syncing flows");
|
||||
|
@ -1391,7 +1393,7 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
|
|||
* @param proposedFlow the proposed DataFlow
|
||||
* @return the AuthorizerInheritability result
|
||||
*/
|
||||
public AuthorizerInheritability checkAuthorizerInheritability(final DataFlow existingFlow, final DataFlow proposedFlow) {
|
||||
private AuthorizerInheritability checkAuthorizerInheritability(final Authorizer authorizer, final DataFlow existingFlow, final DataFlow proposedFlow) {
|
||||
final byte[] existing = existingFlow.getAuthorizerFingerprint();
|
||||
final byte[] proposed = proposedFlow.getAuthorizerFingerprint();
|
||||
|
||||
|
@ -1414,15 +1416,20 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
|
|||
|
||||
// both are internal, but not the same
|
||||
if (!Arrays.equals(existing, proposed)) {
|
||||
final byte[] emptyAuthBytes = AbstractPolicyBasedAuthorizer.EMPTY_FINGERPRINT.getBytes(StandardCharsets.UTF_8);
|
||||
if (AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
|
||||
final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) authorizer;
|
||||
|
||||
// if current is empty then we can take all the proposed authorizations
|
||||
// otherwise they are both internal authorizers and don't match so we can't proceed
|
||||
if (Arrays.equals(emptyAuthBytes, existing)) {
|
||||
return AuthorizerInheritability.inheritable();
|
||||
try {
|
||||
// if the configurations are not equal, see if the manager indicates the proposed configuration is inheritable
|
||||
managedAuthorizer.checkInheritability(new String(proposed, StandardCharsets.UTF_8));
|
||||
return AuthorizerInheritability.inheritable();
|
||||
} catch (final UninheritableAuthorizationsException e) {
|
||||
return AuthorizerInheritability.uninheritable("Proposed Authorizations do not match current Authorizations: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
// should never hit since the existing is only null when authorizer is not managed
|
||||
return AuthorizerInheritability.uninheritable(
|
||||
"Proposed Authorizations do not match current Authorizations");
|
||||
"Proposed Authorizations do not match current Authorizations and are not configured with an internal Authorizer");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,9 @@ package org.apache.nifi.nar;
|
|||
|
||||
import org.apache.nifi.annotation.behavior.RequiresInstanceClassLoading;
|
||||
import org.apache.nifi.authentication.LoginIdentityProvider;
|
||||
import org.apache.nifi.authorization.AccessPolicyProvider;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.UserGroupProvider;
|
||||
import org.apache.nifi.bundle.Bundle;
|
||||
import org.apache.nifi.bundle.BundleCoordinate;
|
||||
import org.apache.nifi.components.ConfigurableComponent;
|
||||
|
@ -82,6 +84,8 @@ public class ExtensionManager {
|
|||
definitionMap.put(ReportingTask.class, new HashSet<>());
|
||||
definitionMap.put(ControllerService.class, new HashSet<>());
|
||||
definitionMap.put(Authorizer.class, new HashSet<>());
|
||||
definitionMap.put(UserGroupProvider.class, new HashSet<>());
|
||||
definitionMap.put(AccessPolicyProvider.class, new HashSet<>());
|
||||
definitionMap.put(LoginIdentityProvider.class, new HashSet<>());
|
||||
definitionMap.put(ProvenanceRepository.class, new HashSet<>());
|
||||
definitionMap.put(ComponentStatusRepository.class, new HashSet<>());
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
package org.apache.nifi.nar;
|
||||
|
||||
import org.apache.nifi.authentication.LoginIdentityProvider;
|
||||
import org.apache.nifi.authorization.AccessPolicyProvider;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.UserGroupProvider;
|
||||
import org.apache.nifi.bundle.Bundle;
|
||||
import org.apache.nifi.components.Validator;
|
||||
import org.apache.nifi.components.state.StateProvider;
|
||||
|
@ -64,6 +66,8 @@ public class NarThreadContextClassLoader extends URLClassLoader {
|
|||
narSpecificClasses.add(StreamCallback.class);
|
||||
narSpecificClasses.add(ControllerService.class);
|
||||
narSpecificClasses.add(Authorizer.class);
|
||||
narSpecificClasses.add(UserGroupProvider.class);
|
||||
narSpecificClasses.add(AccessPolicyProvider.class);
|
||||
narSpecificClasses.add(LoginIdentityProvider.class);
|
||||
narSpecificClasses.add(ProvenanceRepository.class);
|
||||
narSpecificClasses.add(ComponentStatusRepository.class);
|
||||
|
|
|
@ -141,7 +141,7 @@
|
|||
<nifi.security.truststoreType />
|
||||
<nifi.security.truststorePasswd />
|
||||
<nifi.security.needClientAuth />
|
||||
<nifi.security.user.authorizer>file-provider</nifi.security.user.authorizer>
|
||||
<nifi.security.user.authorizer>managed-authorizer</nifi.security.user.authorizer>
|
||||
<nifi.security.user.login.identity.provider />
|
||||
<nifi.security.x509.principal.extractor />
|
||||
<nifi.security.ocsp.responder.url />
|
||||
|
|
|
@ -14,13 +14,105 @@
|
|||
limitations under the License.
|
||||
-->
|
||||
<!--
|
||||
This file lists the authority providers to use when running securely. In order
|
||||
to use a specific provider it must be configured here and it's identifier
|
||||
must be specified in the nifi.properties file.
|
||||
This file lists the userGroupProviders, accessPolicyProviders, and authorizers to use when running securely. In order
|
||||
to use a specific authorizer it must be configured here and it's identifier must be specified in the nifi.properties file.
|
||||
If the authorizer is a managedAuthorizer, it may need to be configured with an accessPolicyProvider and an userGroupProvider.
|
||||
This file allows for configuration of them, but they must be configured in order:
|
||||
|
||||
...
|
||||
all userGroupProviders
|
||||
all accessPolicyProviders
|
||||
all Authorizers
|
||||
...
|
||||
-->
|
||||
<authorizers>
|
||||
|
||||
<!--
|
||||
The FileUserGroupProvider will provide support for managing users and groups which is backed by a file
|
||||
on the local file system.
|
||||
|
||||
- Users File - The file where the FileUserGroupProvider will store users and groups.
|
||||
|
||||
- Legacy Authorized Users File - The full path to an existing authorized-users.xml that will be automatically
|
||||
be used to load the users and groups into the Users File.
|
||||
|
||||
- Initial User Identity [unique key] - The identity of a users and systems to seed the Users File. The name of
|
||||
each property must be unique, for example: "Initial User Identity A", "Initial User Identity B",
|
||||
"Initial User Identity C" or "Initial User Identity 1", "Initial User Identity 2", "Initial User Identity 3"
|
||||
|
||||
NOTE: Any identity mapping rules specified in nifi.properties will also be applied to the user identities,
|
||||
so the values should be the unmapped identities (i.e. full DN from a certificate).
|
||||
-->
|
||||
<userGroupProvider>
|
||||
<identifier>file-user-group-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileUserGroupProvider</class>
|
||||
<property name="Users File">./conf/users.xml</property>
|
||||
<property name="Legacy Authorized Users File"></property>
|
||||
|
||||
<property name="Initial User Identity 1"></property>
|
||||
</userGroupProvider>
|
||||
|
||||
<!--
|
||||
The FileAccessPolicyProvider will provide support for managing access policies which is backed by a file
|
||||
on the local file system.
|
||||
|
||||
- User Group Provider - The identifier for an User Group Provider defined above that will be used to access
|
||||
users and groups for use in the managed access policies.
|
||||
|
||||
- Authorizations File - The file where the FileAccessPolicyProvider will store policies.
|
||||
|
||||
- Initial Admin Identity - The identity of an initial admin user that will be granted access to the UI and
|
||||
given the ability to create additional users, groups, and policies. The value of this property could be
|
||||
a DN when using certificates or LDAP, or a Kerberos principal. This property will only be used when there
|
||||
are no other policies defined. If this property is specified then a Legacy Authorized Users File can not be specified.
|
||||
|
||||
NOTE: Any identity mapping rules specified in nifi.properties will also be applied to the initial admin identity,
|
||||
so the value should be the unmapped identity. This identity must be found in the configured User Group Provider.
|
||||
|
||||
- Legacy Authorized Users File - The full path to an existing authorized-users.xml that will be automatically
|
||||
converted to the new authorizations model. If this property is specified then an Initial Admin Identity can
|
||||
not be specified, and this property will only be used when there are no other users, groups, and policies defined.
|
||||
|
||||
NOTE: Any users in the legacy users file must be found in the configured User Group Provider.
|
||||
|
||||
- Node Identity [unique key] - The identity of a NiFi cluster node. When clustered, a property for each node
|
||||
should be defined, so that every node knows about every other node. If not clustered these properties can be ignored.
|
||||
The name of each property must be unique, for example for a three node cluster:
|
||||
"Node Identity A", "Node Identity B", "Node Identity C" or "Node Identity 1", "Node Identity 2", "Node Identity 3"
|
||||
|
||||
NOTE: Any identity mapping rules specified in nifi.properties will also be applied to the node identities,
|
||||
so the values should be the unmapped identities (i.e. full DN from a certificate). This identity must be found
|
||||
in the configured User Group Provider.
|
||||
-->
|
||||
<accessPolicyProvider>
|
||||
<identifier>file-access-policy-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileAccessPolicyProvider</class>
|
||||
<property name="User Group Provider">file-user-group-provider</property>
|
||||
<property name="Authorizations File">./conf/authorizations.xml</property>
|
||||
<property name="Initial Admin Identity"></property>
|
||||
<property name="Legacy Authorized Users File"></property>
|
||||
|
||||
<property name="Node Identity 1"></property>
|
||||
</accessPolicyProvider>
|
||||
|
||||
<!--
|
||||
The StandardManagedAuthorizer. This authorizer implementation must be configured with the
|
||||
Access Policy Provider which it will use to access and manage users, groups, and policies.
|
||||
These users, groups, and policies will be used to make all access decisions during authorization
|
||||
requests.
|
||||
|
||||
- Access Policy Provider - The identifier for an Access Policy Provider defined above.
|
||||
-->
|
||||
<authorizer>
|
||||
<identifier>managed-authorizer</identifier>
|
||||
<class>org.apache.nifi.authorization.StandardManagedAuthorizer</class>
|
||||
<property name="Access Policy Provider">file-access-policy-provider</property>
|
||||
</authorizer>
|
||||
|
||||
<!--
|
||||
NOTE: This Authorizer has been replaced with the more granular approach configured above with the Standard
|
||||
Managed Authorizer. However, it is still available for backwards compatibility reasons.
|
||||
|
||||
The FileAuthorizer is NiFi's provided authorizer and has the following properties:
|
||||
|
||||
- Authorizations File - The file where the FileAuthorizer will store policies.
|
||||
|
@ -48,7 +140,7 @@
|
|||
NOTE: Any identity mapping rules specified in nifi.properties will also be applied to the node identities,
|
||||
so the values should be the unmapped identities (i.e. full DN from a certificate).
|
||||
-->
|
||||
<authorizer>
|
||||
<!-- <authorizer>
|
||||
<identifier>file-provider</identifier>
|
||||
<class>org.apache.nifi.authorization.FileAuthorizer</class>
|
||||
<property name="Authorizations File">./conf/authorizations.xml</property>
|
||||
|
@ -56,9 +148,7 @@
|
|||
<property name="Initial Admin Identity"></property>
|
||||
<property name="Legacy Authorized Users File"></property>
|
||||
|
||||
<!-- Provide the identity (typically a DN) of each node when clustered, see above description of Node Identity.
|
||||
<property name="Node Identity 1"></property>
|
||||
<property name="Node Identity 2"></property>
|
||||
-->
|
||||
</authorizer>
|
||||
-->
|
||||
</authorizers>
|
|
@ -16,24 +16,6 @@
|
|||
*/
|
||||
package org.apache.nifi.remote;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.apache.nifi.authorization.AuthorizationResult;
|
||||
import org.apache.nifi.authorization.AuthorizationResult.Result;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
|
@ -41,9 +23,10 @@ import org.apache.nifi.authorization.RequestAction;
|
|||
import org.apache.nifi.authorization.resource.Authorizable;
|
||||
import org.apache.nifi.authorization.resource.DataTransferAuthorizable;
|
||||
import org.apache.nifi.authorization.user.NiFiUser;
|
||||
import org.apache.nifi.authorization.user.StandardNiFiUser;
|
||||
import org.apache.nifi.authorization.user.StandardNiFiUser.Builder;
|
||||
import org.apache.nifi.authorization.util.IdentityMapping;
|
||||
import org.apache.nifi.authorization.util.IdentityMappingUtil;
|
||||
import org.apache.nifi.authorization.util.UserGroupUtil;
|
||||
import org.apache.nifi.components.ValidationResult;
|
||||
import org.apache.nifi.connectable.ConnectableType;
|
||||
import org.apache.nifi.controller.AbstractPort;
|
||||
|
@ -73,6 +56,24 @@ import org.apache.nifi.util.NiFiProperties;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class StandardRootGroupPort extends AbstractPort implements RootGroupPort {
|
||||
|
||||
private static final String CATEGORY = "Site to Site";
|
||||
|
@ -362,8 +363,8 @@ public class StandardRootGroupPort extends AbstractPort implements RootGroupPort
|
|||
}
|
||||
|
||||
final String identity = IdentityMappingUtil.mapIdentity(dn, identityMappings);
|
||||
|
||||
return checkUserAuthorization(new StandardNiFiUser(identity));
|
||||
final Set<String> groups = UserGroupUtil.getUserGroups(authorizer, identity);
|
||||
return checkUserAuthorization(new Builder().identity(identity).groups(groups).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2559,6 +2559,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
|
|||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getDataTransferResource(port.getResource()))
|
||||
.identity(user.getIdentity())
|
||||
.groups(user.getGroups())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(false)
|
||||
.action(RequestAction.WRITE)
|
||||
|
|
|
@ -17,20 +17,6 @@
|
|||
package org.apache.nifi.web;
|
||||
|
||||
import com.sun.jersey.core.util.MultivaluedMapImpl;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.ws.rs.HttpMethod;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.action.Action;
|
||||
import org.apache.nifi.action.Component;
|
||||
|
@ -79,6 +65,21 @@ import org.apache.nifi.web.util.ClientResponseUtils;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.ws.rs.HttpMethod;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Implements the NiFiWebConfigurationContext interface to support a context in both standalone and clustered environments.
|
||||
*/
|
||||
|
@ -110,6 +111,7 @@ public class StandardNiFiWebConfigurationContext implements NiFiWebConfiguration
|
|||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getFlowResource())
|
||||
.identity(user.getIdentity())
|
||||
.groups(user.getGroups())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(RequestAction.READ)
|
||||
|
|
|
@ -23,8 +23,8 @@ import com.wordnik.swagger.annotations.ApiResponse;
|
|||
import com.wordnik.swagger.annotations.ApiResponses;
|
||||
import com.wordnik.swagger.annotations.Authorization;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.AuthorizerCapabilityDetection;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.resource.Authorizable;
|
||||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||
|
@ -140,8 +140,8 @@ public class AccessPolicyResource extends ApplicationResource {
|
|||
) @PathParam("resource") String rawResource) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
// parse the action and resource type
|
||||
|
@ -203,8 +203,8 @@ public class AccessPolicyResource extends ApplicationResource {
|
|||
) final AccessPolicyEntity requestAccessPolicyEntity) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isConfigurableAccessPolicyProvider(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_CONFIGURABLE_POLICIES);
|
||||
}
|
||||
|
||||
if (requestAccessPolicyEntity == null || requestAccessPolicyEntity.getComponent() == null) {
|
||||
|
@ -294,8 +294,8 @@ public class AccessPolicyResource extends ApplicationResource {
|
|||
@PathParam("id") final String id) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
if (isReplicateRequest()) {
|
||||
|
@ -356,8 +356,8 @@ public class AccessPolicyResource extends ApplicationResource {
|
|||
) final AccessPolicyEntity requestAccessPolicyEntity) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isConfigurableAccessPolicyProvider(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_CONFIGURABLE_POLICIES);
|
||||
}
|
||||
|
||||
if (requestAccessPolicyEntity == null || requestAccessPolicyEntity.getComponent() == null) {
|
||||
|
@ -454,8 +454,8 @@ public class AccessPolicyResource extends ApplicationResource {
|
|||
@PathParam("id") final String id) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isConfigurableAccessPolicyProvider(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_CONFIGURABLE_POLICIES);
|
||||
}
|
||||
|
||||
if (isReplicateRequest()) {
|
||||
|
|
|
@ -110,6 +110,7 @@ public class ControllerResource extends ApplicationResource {
|
|||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getControllerResource())
|
||||
.identity(user.getIdentity())
|
||||
.groups(user.getGroups())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(action)
|
||||
|
|
|
@ -94,6 +94,7 @@ public class CountersResource extends ApplicationResource {
|
|||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getCountersResource())
|
||||
.identity(user.getIdentity())
|
||||
.groups(user.getGroups())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(action)
|
||||
|
|
|
@ -217,6 +217,7 @@ public class FlowResource extends ApplicationResource {
|
|||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getFlowResource())
|
||||
.identity(user.getIdentity())
|
||||
.groups(user.getGroups())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(RequestAction.READ)
|
||||
|
@ -2236,6 +2237,7 @@ public class FlowResource extends ApplicationResource {
|
|||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getControllerResource())
|
||||
.identity(user.getIdentity())
|
||||
.groups(user.getGroups())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(RequestAction.READ)
|
||||
|
|
|
@ -114,6 +114,7 @@ public class ProvenanceResource extends ApplicationResource {
|
|||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getProvenanceResource())
|
||||
.identity(user.getIdentity())
|
||||
.groups(user.getGroups())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(RequestAction.READ)
|
||||
|
|
|
@ -74,6 +74,7 @@ public class ResourceResource extends ApplicationResource {
|
|||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getResourceResource())
|
||||
.identity(user.getIdentity())
|
||||
.groups(user.getGroups())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(RequestAction.READ)
|
||||
|
|
|
@ -112,6 +112,7 @@ public class SiteToSiteResource extends ApplicationResource {
|
|||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getSiteToSiteResource())
|
||||
.identity(user.getIdentity())
|
||||
.groups(user.getGroups())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(RequestAction.READ)
|
||||
|
|
|
@ -77,6 +77,7 @@ public class SystemDiagnosticsResource extends ApplicationResource {
|
|||
final AuthorizationRequest request = new AuthorizationRequest.Builder()
|
||||
.resource(ResourceFactory.getSystemResource())
|
||||
.identity(user.getIdentity())
|
||||
.groups(user.getGroups())
|
||||
.anonymous(user.isAnonymous())
|
||||
.accessAttempt(true)
|
||||
.action(RequestAction.READ)
|
||||
|
|
|
@ -23,8 +23,8 @@ import com.wordnik.swagger.annotations.ApiResponse;
|
|||
import com.wordnik.swagger.annotations.ApiResponses;
|
||||
import com.wordnik.swagger.annotations.Authorization;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.AuthorizerCapabilityDetection;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.resource.Authorizable;
|
||||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||
|
@ -149,8 +149,8 @@ public class TenantsResource extends ApplicationResource {
|
|||
) final UserEntity requestUserEntity) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_CONFIGURABLE_USERS);
|
||||
}
|
||||
|
||||
if (requestUserEntity == null || requestUserEntity.getComponent() == null) {
|
||||
|
@ -234,8 +234,8 @@ public class TenantsResource extends ApplicationResource {
|
|||
@PathParam("id") final String id) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
if (isReplicateRequest()) {
|
||||
|
@ -284,8 +284,8 @@ public class TenantsResource extends ApplicationResource {
|
|||
public Response getUsers() {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
if (isReplicateRequest()) {
|
||||
|
@ -352,8 +352,8 @@ public class TenantsResource extends ApplicationResource {
|
|||
) final UserEntity requestUserEntity) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_CONFIGURABLE_USERS);
|
||||
}
|
||||
|
||||
if (requestUserEntity == null || requestUserEntity.getComponent() == null) {
|
||||
|
@ -448,8 +448,8 @@ public class TenantsResource extends ApplicationResource {
|
|||
@PathParam("id") final String id) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_CONFIGURABLE_USERS);
|
||||
}
|
||||
|
||||
if (isReplicateRequest()) {
|
||||
|
@ -538,8 +538,8 @@ public class TenantsResource extends ApplicationResource {
|
|||
) final UserGroupEntity requestUserGroupEntity) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_CONFIGURABLE_USERS);
|
||||
}
|
||||
|
||||
if (requestUserGroupEntity == null || requestUserGroupEntity.getComponent() == null) {
|
||||
|
@ -623,8 +623,8 @@ public class TenantsResource extends ApplicationResource {
|
|||
@PathParam("id") final String id) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
if (isReplicateRequest()) {
|
||||
|
@ -673,8 +673,8 @@ public class TenantsResource extends ApplicationResource {
|
|||
public Response getUserGroups() {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
if (isReplicateRequest()) {
|
||||
|
@ -740,8 +740,8 @@ public class TenantsResource extends ApplicationResource {
|
|||
) final UserGroupEntity requestUserGroupEntity) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_CONFIGURABLE_USERS);
|
||||
}
|
||||
|
||||
if (requestUserGroupEntity == null || requestUserGroupEntity.getComponent() == null) {
|
||||
|
@ -836,8 +836,8 @@ public class TenantsResource extends ApplicationResource {
|
|||
@PathParam("id") final String id) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_CONFIGURABLE_USERS);
|
||||
}
|
||||
|
||||
if (isReplicateRequest()) {
|
||||
|
@ -897,7 +897,7 @@ public class TenantsResource extends ApplicationResource {
|
|||
@ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
|
||||
}
|
||||
)
|
||||
public Response searchCluster(
|
||||
public Response searchTenants(
|
||||
@ApiParam(
|
||||
value = "Identity to search for.",
|
||||
required = true
|
||||
|
@ -905,8 +905,8 @@ public class TenantsResource extends ApplicationResource {
|
|||
@QueryParam("q") @DefaultValue(StringUtils.EMPTY) String value) {
|
||||
|
||||
// ensure we're running with a configurable authorizer
|
||||
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
|
||||
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
if (isReplicateRequest()) {
|
||||
|
|
|
@ -55,7 +55,7 @@ public class AccessDeniedExceptionMapper implements ExceptionMapper<AccessDenied
|
|||
if (user == null) {
|
||||
identity = "<no user found>";
|
||||
} else {
|
||||
identity = user.getIdentity();
|
||||
identity = user.toString();
|
||||
}
|
||||
|
||||
logger.info(String.format("%s does not have permission to access the requested resource. %s Returning %s response.", identity, exception.getMessage(), status));
|
||||
|
|
|
@ -38,9 +38,9 @@ import org.apache.nifi.annotation.behavior.Stateful;
|
|||
import org.apache.nifi.annotation.documentation.CapabilityDescription;
|
||||
import org.apache.nifi.annotation.documentation.DeprecationNotice;
|
||||
import org.apache.nifi.annotation.documentation.Tags;
|
||||
import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
|
||||
import org.apache.nifi.authorization.AccessPolicy;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.AuthorizerCapabilityDetection;
|
||||
import org.apache.nifi.authorization.Group;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.Resource;
|
||||
|
@ -220,7 +220,9 @@ public final class DtoFactory {
|
|||
// get the refresh interval
|
||||
final long refreshInterval = FormatUtils.getTimeDuration(autoRefreshInterval, TimeUnit.SECONDS);
|
||||
dto.setAutoRefreshIntervalSeconds(refreshInterval);
|
||||
dto.setSupportsConfigurableAuthorizer(authorizer instanceof AbstractPolicyBasedAuthorizer);
|
||||
dto.setSupportsManagedAuthorizer(AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer));
|
||||
dto.setSupportsConfigurableUsersAndGroups(AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer));
|
||||
dto.setSupportsConfigurableAuthorizer(AuthorizerCapabilityDetection.isConfigurableAccessPolicyProvider(authorizer));
|
||||
|
||||
final Date now = new Date();
|
||||
dto.setTimeOffset(TimeZone.getDefault().getOffset(now.getTime()));
|
||||
|
|
|
@ -23,7 +23,9 @@ import org.apache.nifi.web.api.dto.AccessPolicyDTO;
|
|||
|
||||
public interface AccessPolicyDAO {
|
||||
|
||||
String MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER = "This NiFi is not configured to internally manage users, groups, and policies. Please contact your system administrator.";
|
||||
String MSG_NON_MANAGED_AUTHORIZER = "This NiFi is not configured to internally manage users, groups, or policies. Please contact your system administrator.";
|
||||
String MSG_NON_CONFIGURABLE_POLICIES = "This NiFi is not configured to allow configurable policies. Please contact your system administrator.";
|
||||
String MSG_NON_CONFIGURABLE_USERS = "This NiFi is not configured to allow configurable users and groups. Please contact your system administrator.";
|
||||
|
||||
/**
|
||||
* Whether or not NiFi supports a configurable authorizer.
|
||||
|
|
|
@ -16,15 +16,21 @@
|
|||
*/
|
||||
package org.apache.nifi.web.dao.impl;
|
||||
|
||||
import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
|
||||
import org.apache.nifi.authorization.AccessPolicy;
|
||||
import org.apache.nifi.authorization.AccessPolicyProvider;
|
||||
import org.apache.nifi.authorization.AccessPolicyProviderInitializationContext;
|
||||
import org.apache.nifi.authorization.Authorizer;
|
||||
import org.apache.nifi.authorization.AuthorizerCapabilityDetection;
|
||||
import org.apache.nifi.authorization.AuthorizerConfigurationContext;
|
||||
import org.apache.nifi.authorization.AuthorizerInitializationContext;
|
||||
import org.apache.nifi.authorization.Group;
|
||||
import org.apache.nifi.authorization.ManagedAuthorizer;
|
||||
import org.apache.nifi.authorization.ConfigurableAccessPolicyProvider;
|
||||
import org.apache.nifi.authorization.ConfigurableUserGroupProvider;
|
||||
import org.apache.nifi.authorization.RequestAction;
|
||||
import org.apache.nifi.authorization.User;
|
||||
import org.apache.nifi.authorization.UsersAndAccessPolicies;
|
||||
import org.apache.nifi.authorization.UserAndGroups;
|
||||
import org.apache.nifi.authorization.UserGroupProvider;
|
||||
import org.apache.nifi.authorization.UserGroupProviderInitializationContext;
|
||||
import org.apache.nifi.authorization.exception.AuthorizationAccessException;
|
||||
import org.apache.nifi.authorization.exception.AuthorizerCreationException;
|
||||
import org.apache.nifi.authorization.exception.AuthorizerDestructionException;
|
||||
|
@ -44,118 +50,101 @@ import java.util.stream.Collectors;
|
|||
|
||||
public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGroupDAO, UserDAO {
|
||||
|
||||
private final AbstractPolicyBasedAuthorizer authorizer;
|
||||
private final boolean supportsConfigurableAuthorizer;
|
||||
private final AccessPolicyProvider accessPolicyProvider;
|
||||
private final UserGroupProvider userGroupProvider;
|
||||
|
||||
public StandardPolicyBasedAuthorizerDAO(final Authorizer authorizer) {
|
||||
if (authorizer instanceof AbstractPolicyBasedAuthorizer) {
|
||||
this.authorizer = (AbstractPolicyBasedAuthorizer) authorizer;
|
||||
this.supportsConfigurableAuthorizer = true;
|
||||
if (AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
|
||||
accessPolicyProvider = ((ManagedAuthorizer) authorizer).getAccessPolicyProvider();
|
||||
} else {
|
||||
this.authorizer = new AbstractPolicyBasedAuthorizer() {
|
||||
@Override
|
||||
public Group doAddGroup(final Group group) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getGroup(final String identifier) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group doUpdateGroup(final Group group) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group deleteGroup(final Group group) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User doAddUser(final User user) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(final String identifier) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByIdentity(final String identity) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User doUpdateUser(final User user) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User deleteUser(final User user) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<User> getUsers() throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy doAddAccessPolicy(final AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(final String identifier) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy updateAccessPolicy(final AccessPolicy accessPolicy) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy deleteAccessPolicy(final AccessPolicy policy) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
}
|
||||
|
||||
accessPolicyProvider = new AccessPolicyProvider() {
|
||||
@Override
|
||||
public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsersAndAccessPolicies getUsersAndAccessPolicies() throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
|
||||
public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doOnConfigured(final AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
public UserGroupProvider getUserGroupProvider() {
|
||||
return new UserGroupProvider() {
|
||||
@Override
|
||||
public Set<User> getUsers() throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(String identifier) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByIdentity(String identity) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Group> getGroups() throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getGroup(String identifier) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException {
|
||||
throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDestruction() throws AuthorizerDestructionException {
|
||||
|
||||
}
|
||||
};
|
||||
this.supportsConfigurableAuthorizer = false;
|
||||
}
|
||||
|
||||
userGroupProvider = accessPolicyProvider.getUserGroupProvider();
|
||||
}
|
||||
|
||||
private AccessPolicy findAccessPolicy(final RequestAction requestAction, final String resource) {
|
||||
return authorizer.getAccessPolicies().stream()
|
||||
return accessPolicyProvider.getAccessPolicies().stream()
|
||||
.filter(policy -> policy.getAction().equals(requestAction) && policy.getResource().equals(resource))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
@ -163,23 +152,28 @@ public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGr
|
|||
|
||||
@Override
|
||||
public boolean supportsConfigurableAuthorizer() {
|
||||
return supportsConfigurableAuthorizer;
|
||||
return accessPolicyProvider instanceof ConfigurableAccessPolicyProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAccessPolicy(final String accessPolicyId) {
|
||||
return authorizer.getAccessPolicy(accessPolicyId) != null;
|
||||
return accessPolicyProvider.getAccessPolicy(accessPolicyId) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy createAccessPolicy(final AccessPolicyDTO accessPolicyDTO) {
|
||||
return authorizer.addAccessPolicy(buildAccessPolicy(accessPolicyDTO.getId(),
|
||||
accessPolicyDTO.getResource(), RequestAction.valueOfValue(accessPolicyDTO.getAction()), accessPolicyDTO));
|
||||
if (supportsConfigurableAuthorizer()) {
|
||||
final ConfigurableAccessPolicyProvider configurableAccessPolicyProvider = (ConfigurableAccessPolicyProvider) accessPolicyProvider;
|
||||
return configurableAccessPolicyProvider.addAccessPolicy(buildAccessPolicy(accessPolicyDTO.getId(),
|
||||
accessPolicyDTO.getResource(), RequestAction.valueOfValue(accessPolicyDTO.getAction()), accessPolicyDTO));
|
||||
} else {
|
||||
throw new IllegalStateException(MSG_NON_CONFIGURABLE_POLICIES);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy getAccessPolicy(final String accessPolicyId) {
|
||||
final AccessPolicy accessPolicy = authorizer.getAccessPolicy(accessPolicyId);
|
||||
final AccessPolicy accessPolicy = accessPolicyProvider.getAccessPolicy(accessPolicyId);
|
||||
if (accessPolicy == null) {
|
||||
throw new ResourceNotFoundException(String.format("Unable to find access policy with id '%s'.", accessPolicyId));
|
||||
}
|
||||
|
@ -210,14 +204,25 @@ public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGr
|
|||
|
||||
@Override
|
||||
public AccessPolicy updateAccessPolicy(final AccessPolicyDTO accessPolicyDTO) {
|
||||
final AccessPolicy currentAccessPolicy = getAccessPolicy(accessPolicyDTO.getId());
|
||||
return authorizer.updateAccessPolicy(buildAccessPolicy(currentAccessPolicy.getIdentifier(),
|
||||
currentAccessPolicy.getResource(), currentAccessPolicy.getAction(), accessPolicyDTO));
|
||||
if (supportsConfigurableAuthorizer()) {
|
||||
final ConfigurableAccessPolicyProvider configurableAccessPolicyProvider = (ConfigurableAccessPolicyProvider) accessPolicyProvider;
|
||||
|
||||
final AccessPolicy currentAccessPolicy = getAccessPolicy(accessPolicyDTO.getId());
|
||||
return configurableAccessPolicyProvider.updateAccessPolicy(buildAccessPolicy(currentAccessPolicy.getIdentifier(),
|
||||
currentAccessPolicy.getResource(), currentAccessPolicy.getAction(), accessPolicyDTO));
|
||||
} else {
|
||||
throw new IllegalStateException(MSG_NON_CONFIGURABLE_POLICIES);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessPolicy deleteAccessPolicy(final String accessPolicyId) {
|
||||
return authorizer.deleteAccessPolicy(getAccessPolicy(accessPolicyId));
|
||||
if (supportsConfigurableAuthorizer()) {
|
||||
final ConfigurableAccessPolicyProvider configurableAccessPolicyProvider = (ConfigurableAccessPolicyProvider) accessPolicyProvider;
|
||||
return configurableAccessPolicyProvider.deleteAccessPolicy(getAccessPolicy(accessPolicyId));
|
||||
} else {
|
||||
throw new IllegalStateException(MSG_NON_CONFIGURABLE_POLICIES);
|
||||
}
|
||||
}
|
||||
|
||||
private AccessPolicy buildAccessPolicy(final String identifier, final String resource, final RequestAction action, final AccessPolicyDTO accessPolicyDTO) {
|
||||
|
@ -238,17 +243,22 @@ public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGr
|
|||
|
||||
@Override
|
||||
public boolean hasUserGroup(final String userGroupId) {
|
||||
return authorizer.getGroup(userGroupId) != null;
|
||||
return userGroupProvider.getGroup(userGroupId) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group createUserGroup(final UserGroupDTO userGroupDTO) {
|
||||
return authorizer.addGroup(buildUserGroup(userGroupDTO.getId(), userGroupDTO));
|
||||
if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
|
||||
final ConfigurableUserGroupProvider configurableUserGroupProvider = (ConfigurableUserGroupProvider) userGroupProvider;
|
||||
return configurableUserGroupProvider.addGroup(buildUserGroup(userGroupDTO.getId(), userGroupDTO));
|
||||
} else {
|
||||
throw new IllegalStateException(MSG_NON_CONFIGURABLE_USERS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group getUserGroup(final String userGroupId) {
|
||||
final Group userGroup = authorizer.getGroup(userGroupId);
|
||||
final Group userGroup = userGroupProvider.getGroup(userGroupId);
|
||||
if (userGroup == null) {
|
||||
throw new ResourceNotFoundException(String.format("Unable to find user group with id '%s'.", userGroupId));
|
||||
}
|
||||
|
@ -257,14 +267,14 @@ public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGr
|
|||
|
||||
@Override
|
||||
public Set<Group> getUserGroupsForUser(String userId) {
|
||||
return authorizer.getGroups().stream()
|
||||
return userGroupProvider.getGroups().stream()
|
||||
.filter(g -> g.getUsers().contains(userId))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AccessPolicy> getAccessPoliciesForUser(String userId) {
|
||||
return authorizer.getAccessPolicies().stream()
|
||||
return accessPolicyProvider.getAccessPolicies().stream()
|
||||
.filter(p -> {
|
||||
// policy contains the user
|
||||
if (p.getUsers().contains(userId)) {
|
||||
|
@ -272,14 +282,14 @@ public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGr
|
|||
}
|
||||
|
||||
// policy contains a group with the user
|
||||
return !p.getGroups().stream().filter(g -> authorizer.getGroup(g).getUsers().contains(userId)).collect(Collectors.toSet()).isEmpty();
|
||||
return !p.getGroups().stream().filter(g -> userGroupProvider.getGroup(g).getUsers().contains(userId)).collect(Collectors.toSet()).isEmpty();
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AccessPolicy> getAccessPoliciesForUserGroup(String userGroupId) {
|
||||
return authorizer.getAccessPolicies().stream()
|
||||
return accessPolicyProvider.getAccessPolicies().stream()
|
||||
.filter(p -> {
|
||||
// policy contains the user group
|
||||
return p.getGroups().contains(userGroupId);
|
||||
|
@ -289,17 +299,46 @@ public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGr
|
|||
|
||||
@Override
|
||||
public Set<Group> getUserGroups() {
|
||||
return authorizer.getGroups();
|
||||
return userGroupProvider.getGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group updateUserGroup(final UserGroupDTO userGroupDTO) {
|
||||
return authorizer.updateGroup(buildUserGroup(getUserGroup(userGroupDTO.getId()).getIdentifier(), userGroupDTO));
|
||||
if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
|
||||
final ConfigurableUserGroupProvider configurableUserGroupProvider = (ConfigurableUserGroupProvider) userGroupProvider;
|
||||
return configurableUserGroupProvider.updateGroup(buildUserGroup(getUserGroup(userGroupDTO.getId()).getIdentifier(), userGroupDTO));
|
||||
} else {
|
||||
throw new IllegalStateException(MSG_NON_CONFIGURABLE_USERS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group deleteUserGroup(final String userGroupId) {
|
||||
return authorizer.deleteGroup(getUserGroup(userGroupId));
|
||||
if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
|
||||
final ConfigurableUserGroupProvider configurableUserGroupProvider = (ConfigurableUserGroupProvider) userGroupProvider;
|
||||
|
||||
final Group group = getUserGroup(userGroupId);
|
||||
final Group removedGroup = configurableUserGroupProvider.deleteGroup(group);
|
||||
|
||||
// ensure the user was removed
|
||||
if (removedGroup == null) {
|
||||
throw new ResourceNotFoundException(String.format("Unable to find user group with id '%s'.", removedGroup));
|
||||
}
|
||||
|
||||
// remove any references to the user group being deleted from policies if possible
|
||||
if (accessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
|
||||
for (AccessPolicy policy : accessPolicyProvider.getAccessPolicies()) {
|
||||
if (policy.getGroups().contains(removedGroup.getIdentifier())) {
|
||||
final AccessPolicy.Builder builder = new AccessPolicy.Builder(policy).removeGroup(removedGroup.getIdentifier());
|
||||
((ConfigurableAccessPolicyProvider) accessPolicyProvider).updateAccessPolicy(builder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return removedGroup;
|
||||
} else {
|
||||
throw new IllegalStateException(MSG_NON_CONFIGURABLE_USERS);
|
||||
}
|
||||
}
|
||||
|
||||
private Group buildUserGroup(final String identifier, final UserGroupDTO userGroupDTO) {
|
||||
|
@ -313,17 +352,22 @@ public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGr
|
|||
|
||||
@Override
|
||||
public boolean hasUser(final String userId) {
|
||||
return authorizer.getUser(userId) != null;
|
||||
return userGroupProvider.getUser(userId) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User createUser(final UserDTO userDTO) {
|
||||
return authorizer.addUser(buildUser(userDTO.getId(), userDTO));
|
||||
if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
|
||||
final ConfigurableUserGroupProvider configurableUserGroupProvider = (ConfigurableUserGroupProvider) userGroupProvider;
|
||||
return configurableUserGroupProvider.addUser(buildUser(userDTO.getId(), userDTO));
|
||||
} else {
|
||||
throw new IllegalStateException(MSG_NON_CONFIGURABLE_USERS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser(final String userId) {
|
||||
final User user = authorizer.getUser(userId);
|
||||
final User user = userGroupProvider.getUser(userId);
|
||||
if (user == null) {
|
||||
throw new ResourceNotFoundException(String.format("Unable to find user with id '%s'.", userId));
|
||||
}
|
||||
|
@ -332,18 +376,46 @@ public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGr
|
|||
|
||||
@Override
|
||||
public Set<User> getUsers() {
|
||||
return authorizer.getUsers();
|
||||
return userGroupProvider.getUsers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public User updateUser(final UserDTO userDTO) {
|
||||
return authorizer.updateUser(buildUser(getUser(userDTO.getId()).getIdentifier(), userDTO));
|
||||
if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
|
||||
final ConfigurableUserGroupProvider configurableUserGroupProvider = (ConfigurableUserGroupProvider) userGroupProvider;
|
||||
return configurableUserGroupProvider.updateUser(buildUser(getUser(userDTO.getId()).getIdentifier(), userDTO));
|
||||
} else {
|
||||
throw new IllegalStateException(MSG_NON_CONFIGURABLE_USERS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User deleteUser(final String userId) {
|
||||
final User user = getUser(userId);
|
||||
return authorizer.deleteUser(user);
|
||||
if (userGroupProvider instanceof ConfigurableUserGroupProvider) {
|
||||
final ConfigurableUserGroupProvider configurableUserGroupProvider = (ConfigurableUserGroupProvider) userGroupProvider;
|
||||
|
||||
final User user = getUser(userId);
|
||||
final User removedUser = configurableUserGroupProvider.deleteUser(user);
|
||||
|
||||
// ensure the user was removed
|
||||
if (removedUser == null) {
|
||||
throw new ResourceNotFoundException(String.format("Unable to find user with id '%s'.", userId));
|
||||
}
|
||||
|
||||
// remove any references to the user being deleted from policies if possible
|
||||
if (accessPolicyProvider instanceof ConfigurableAccessPolicyProvider) {
|
||||
for (AccessPolicy policy : accessPolicyProvider.getAccessPolicies()) {
|
||||
if (policy.getUsers().contains(removedUser.getIdentifier())) {
|
||||
final AccessPolicy.Builder builder = new AccessPolicy.Builder(policy).removeUser(removedUser.getIdentifier());
|
||||
((ConfigurableAccessPolicyProvider) accessPolicyProvider).updateAccessPolicy(builder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return removedUser;
|
||||
} else {
|
||||
throw new IllegalStateException(MSG_NON_CONFIGURABLE_USERS);
|
||||
}
|
||||
}
|
||||
|
||||
private User buildUser(final String identifier, final UserDTO userDTO) {
|
||||
|
|
|
@ -16,35 +16,51 @@
|
|||
*/
|
||||
package org.apache.nifi.web
|
||||
|
||||
import org.apache.nifi.authorization.*
|
||||
import org.apache.nifi.authorization.AccessDeniedException
|
||||
import org.apache.nifi.authorization.AccessPolicy
|
||||
import org.apache.nifi.authorization.AuthorizableLookup
|
||||
import org.apache.nifi.authorization.AuthorizationResult
|
||||
import org.apache.nifi.authorization.Authorizer
|
||||
import org.apache.nifi.authorization.Group
|
||||
import org.apache.nifi.authorization.RequestAction
|
||||
import org.apache.nifi.authorization.Resource
|
||||
import org.apache.nifi.authorization.User
|
||||
import org.apache.nifi.authorization.resource.Authorizable
|
||||
import org.apache.nifi.authorization.resource.ResourceFactory
|
||||
import org.apache.nifi.authorization.user.NiFiUser
|
||||
import org.apache.nifi.authorization.user.StandardNiFiUser
|
||||
import org.apache.nifi.authorization.user.NiFiUserDetails
|
||||
import org.apache.nifi.authorization.user.StandardNiFiUser
|
||||
import org.apache.nifi.controller.service.ControllerServiceProvider
|
||||
import org.apache.nifi.reporting.Bulletin
|
||||
import org.apache.nifi.reporting.BulletinRepository
|
||||
import org.apache.nifi.reporting.ComponentType
|
||||
import org.apache.nifi.web.api.dto.*
|
||||
import org.apache.nifi.web.api.dto.AccessPolicyDTO
|
||||
import org.apache.nifi.web.api.dto.BulletinDTO
|
||||
import org.apache.nifi.web.api.dto.DtoFactory
|
||||
import org.apache.nifi.web.api.dto.EntityFactory
|
||||
import org.apache.nifi.web.api.dto.RevisionDTO
|
||||
import org.apache.nifi.web.api.dto.UserDTO
|
||||
import org.apache.nifi.web.api.dto.UserGroupDTO
|
||||
import org.apache.nifi.web.api.entity.BulletinEntity
|
||||
import org.apache.nifi.web.api.entity.UserEntity
|
||||
import org.apache.nifi.web.controller.ControllerFacade
|
||||
import org.apache.nifi.web.dao.AccessPolicyDAO
|
||||
import org.apache.nifi.web.dao.UserDAO
|
||||
import org.apache.nifi.web.dao.UserGroupDAO
|
||||
import org.apache.nifi.web.revision.*
|
||||
import org.apache.nifi.web.revision.DeleteRevisionTask
|
||||
import org.apache.nifi.web.revision.ReadOnlyRevisionCallback
|
||||
import org.apache.nifi.web.revision.RevisionClaim
|
||||
import org.apache.nifi.web.revision.RevisionManager
|
||||
import org.apache.nifi.web.revision.UpdateRevisionTask
|
||||
import org.apache.nifi.web.security.token.NiFiAuthenticationToken
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import spock.lang.Ignore
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Unroll
|
||||
|
||||
|
||||
class StandardNiFiServiceFacadeSpec extends Specification {
|
||||
|
||||
def setup() {
|
||||
final NiFiUser user = new StandardNiFiUser("nifi-user");
|
||||
final NiFiUser user = new StandardNiFiUser.Builder().identity("nifi-user").build();
|
||||
final NiFiAuthenticationToken auth = new NiFiAuthenticationToken(new NiFiUserDetails(user));
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,14 @@ import spock.lang.Unroll
|
|||
|
||||
class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
||||
|
||||
private AbstractPolicyBasedAuthorizer mockAuthorizer() {
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
authorizer.getAccessPolicyProvider() >> {
|
||||
callRealMethod();
|
||||
}
|
||||
return authorizer;
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "test non-policy-based authorizer #method throws IllegalStateException"() {
|
||||
when:
|
||||
|
@ -34,31 +42,57 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
|
||||
then:
|
||||
def e = thrown(IllegalStateException)
|
||||
assert e.message.equalsIgnoreCase(StandardPolicyBasedAuthorizerDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER)
|
||||
assert e.message.equalsIgnoreCase(StandardPolicyBasedAuthorizerDAO.MSG_NON_MANAGED_AUTHORIZER)
|
||||
|
||||
where:
|
||||
method | daoMethod
|
||||
'createAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createAccessPolicy(new AccessPolicyDTO(id: '1', resource: '/1', action: "read")) }
|
||||
'createUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createUser(new UserDTO(id: '1', identity: 'a')) }
|
||||
'createUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createUserGroup(new UserGroupDTO(id: '1', identity: 'a')) }
|
||||
'deleteAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteAccessPolicy('1') }
|
||||
'deleteUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteUser('1') }
|
||||
'deleteUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteUserGroup('1') }
|
||||
'getAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).getAccessPolicy('1') }
|
||||
'getUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).getUser('1') }
|
||||
'getUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).getUserGroup('1') }
|
||||
'hasAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).hasAccessPolicy('1') }
|
||||
'hasUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).hasUser('1') }
|
||||
'hasUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).hasUserGroup('1') }
|
||||
'updateAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateAccessPolicy(new AccessPolicyDTO(id: '1', resource: '/1', action: "read")) }
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "test non-configurable user group provider #method throws IllegalStateException"() {
|
||||
when:
|
||||
daoMethod()
|
||||
|
||||
then:
|
||||
def e = thrown(IllegalStateException)
|
||||
assert e.message.equalsIgnoreCase(StandardPolicyBasedAuthorizerDAO.MSG_NON_CONFIGURABLE_USERS)
|
||||
|
||||
where:
|
||||
method | daoMethod
|
||||
'createUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createUser(new UserDTO(id: '1', identity: 'a')) }
|
||||
'createUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createUserGroup(new UserGroupDTO(id: '1', identity: 'a')) }
|
||||
'deleteUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteUser('1') }
|
||||
'deleteUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteUserGroup('1') }
|
||||
'updateUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateUser(new UserDTO(id: '1', identity: 'a')) }
|
||||
'updateUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateUserGroup(new UserGroupDTO(id: '1', identity: 'a')) }
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "test non-configurable access policy provider #method throws IllegalStateException"() {
|
||||
when:
|
||||
daoMethod()
|
||||
|
||||
then:
|
||||
def e = thrown(IllegalStateException)
|
||||
assert e.message.equalsIgnoreCase(StandardPolicyBasedAuthorizerDAO.MSG_NON_CONFIGURABLE_POLICIES)
|
||||
|
||||
where:
|
||||
method | daoMethod
|
||||
'createAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createAccessPolicy(new AccessPolicyDTO(id: '1', resource: '/1', action: "read")) }
|
||||
'deleteAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteAccessPolicy('1') }
|
||||
'updateAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateAccessPolicy(new AccessPolicyDTO(id: '1', resource: '/1', action: "read")) }
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "HasAccessPolicy: accessPolicy: #accessPolicy"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -79,7 +113,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "CreateAccessPolicy: accessPolicy=#accessPolicy"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', action: "read",
|
||||
users: [new TenantEntity(id: 'user-id-1')] as Set,
|
||||
|
@ -92,7 +126,6 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
noExceptionThrown()
|
||||
|
||||
then:
|
||||
1 * authorizer.getAccessPolicies() >> accessPolicies
|
||||
1 * authorizer.doAddAccessPolicy(accessPolicy) >> accessPolicy
|
||||
0 * _
|
||||
result?.equals accessPolicy
|
||||
|
@ -106,7 +139,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "GetAccessPolicy: success"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -126,7 +159,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "GetAccessPolicy: failure"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -141,7 +174,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "UpdateAccessPolicy: success"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', action: "read",
|
||||
users: [new TenantEntity(id: 'user-id-1')] as Set,
|
||||
|
@ -165,7 +198,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "UpdateAccessPolicy: failure"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', action: "read",
|
||||
users: [new TenantEntity(id: 'user-id-1')] as Set,
|
||||
|
@ -183,7 +216,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "DeleteAccessPolicy: success"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -204,7 +237,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "DeleteAccessPolicy: failure"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -219,7 +252,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "HasUserGroup: userGroup=#userGroup"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -239,7 +272,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "CreateUserGroup: userGroup=#userGroup"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
def requestDTO = new UserGroupDTO(id: 'user-group-id-1', identity: 'user group identity', users: [new TenantEntity(id: 'user-id-1')] as Set)
|
||||
|
||||
|
@ -250,8 +283,6 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
noExceptionThrown()
|
||||
|
||||
then:
|
||||
1 * authorizer.getUsers() >> users
|
||||
1 * authorizer.getGroups() >> groups
|
||||
1 * authorizer.doAddGroup(userGroup) >> userGroup
|
||||
0 * _
|
||||
result?.equals userGroup
|
||||
|
@ -265,7 +296,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "GetUserGroup: success"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -284,7 +315,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "GetUserGroup: failure"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -299,7 +330,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "GetUserGroups: success"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -318,7 +349,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "UpdateUserGroup: success"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
def requestDTO = new UserGroupDTO(id: 'user-group-id-1', identity: 'user group identity', users: [new TenantEntity(id: 'user-id-1')] as Set)
|
||||
|
||||
|
@ -327,8 +358,6 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
|
||||
then:
|
||||
1 * authorizer.getGroup(requestDTO.id) >> userGroup
|
||||
1 * authorizer.getUsers() >> users
|
||||
1 * authorizer.getGroups() >> groups
|
||||
1 * authorizer.doUpdateGroup(userGroup) >> userGroup
|
||||
0 * _
|
||||
result?.equals(userGroup)
|
||||
|
@ -342,7 +371,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "UpdateUserGroup: failure"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
def requestDTO = new UserGroupDTO(id: 'user-group-id-1', identity: 'user group identity', users: [new TenantEntity(id: 'user-id-1')] as Set)
|
||||
|
||||
|
@ -358,7 +387,10 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "DeleteUserGroup: success"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
authorizer.getAccessPolicyProvider().getAccessPolicies() >> {
|
||||
callRealMethod();
|
||||
}
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -367,6 +399,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
then:
|
||||
1 * authorizer.getGroup('user-group-id-1') >> userGroup
|
||||
1 * authorizer.deleteGroup(userGroup) >> userGroup
|
||||
1 * authorizer.getAccessPolicies() >> []
|
||||
0 * _
|
||||
assert result?.equals(userGroup)
|
||||
|
||||
|
@ -378,7 +411,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "DeleteUserGroup: failure"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -393,7 +426,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "HasUser: user=#user"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -412,7 +445,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "CreateUser: user=#user"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
def requestDTO = new UserDTO(id: 'user-id-1', identity: 'user identity', userGroups: [new TenantEntity(id: 'user-group-id-1')] as Set)
|
||||
|
||||
|
@ -423,8 +456,6 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
noExceptionThrown()
|
||||
|
||||
then:
|
||||
1 * authorizer.getUsers() >> users
|
||||
1 * authorizer.getGroups() >> groups
|
||||
1 * authorizer.doAddUser(user) >> user
|
||||
0 * _
|
||||
result?.equals user
|
||||
|
@ -438,7 +469,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "GetUser: success"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -457,7 +488,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "GetUser: failure"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -472,7 +503,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "GetUsers: success"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -491,7 +522,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "UpdateUser: success"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
def requestDTO = new UserDTO(id: 'user-id-1', identity: 'user identity', userGroups: [new TenantEntity(id: 'user-group-id-1')] as Set)
|
||||
|
||||
|
@ -500,8 +531,6 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
|
||||
then:
|
||||
1 * authorizer.getUser(requestDTO.id) >> user
|
||||
1 * authorizer.getUsers() >> users
|
||||
1 * authorizer.getGroups() >> groups
|
||||
1 * authorizer.doUpdateUser(user) >> user
|
||||
0 * _
|
||||
result?.equals(user)
|
||||
|
@ -515,7 +544,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "UpdateUser: failure"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
def requestDTO = new UserDTO(id: 'user-id-1', identity: 'user identity', userGroups: [new TenantEntity(id: 'user-group-id-1')] as Set)
|
||||
|
||||
|
@ -531,7 +560,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "DeleteUser: success"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
@ -540,6 +569,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
then:
|
||||
1 * authorizer.getUser('user-id-1') >> user
|
||||
1 * authorizer.deleteUser(user) >> user
|
||||
1 * authorizer.getAccessPolicies() >> []
|
||||
0 * _
|
||||
result?.equals(user)
|
||||
|
||||
|
@ -551,7 +581,7 @@ class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
|
|||
@Unroll
|
||||
def "DeleteUser: failure"() {
|
||||
given:
|
||||
def authorizer = Mock AbstractPolicyBasedAuthorizer
|
||||
def authorizer = mockAuthorizer()
|
||||
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
|
||||
|
||||
when:
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue