http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/IdentifierUtil.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/IdentifierUtil.java b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/IdentifierUtil.java new file mode 100644 index 0000000..b698cd0 --- /dev/null +++ b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/IdentifierUtil.java @@ -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.registry.authorization.file; + +import org.apache.commons.lang3.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() {} +}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/UserGroupHolder.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/UserGroupHolder.java b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/UserGroupHolder.java new file mode 100644 index 0000000..3055c19 --- /dev/null +++ b/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/UserGroupHolder.java @@ -0,0 +1,241 @@ +/* + * 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.registry.authorization.file; + + +import org.apache.nifi.registry.authorization.file.tenants.generated.Groups; +import org.apache.nifi.registry.authorization.file.tenants.generated.Tenants; +import org.apache.nifi.registry.authorization.file.tenants.generated.Users; +import org.apache.nifi.registry.authorization.Group; +import org.apache.nifi.registry.authorization.User; + +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.registry.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.registry.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.registry.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); + } + +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api-impl/src/main/xsd/authorizations.xsd ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api-impl/src/main/xsd/authorizations.xsd b/nifi-registry-security-api-impl/src/main/xsd/authorizations.xsd new file mode 100644 index 0000000..0ab27a9 --- /dev/null +++ b/nifi-registry-security-api-impl/src/main/xsd/authorizations.xsd @@ -0,0 +1,86 @@ +<?xml version="1.0"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + <xs:complexType name="Policy"> + <xs:sequence> + <xs:element name="group" minOccurs="0" maxOccurs="unbounded" > + <xs:complexType> + <xs:attribute name="identifier"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element name="user" minOccurs="0" maxOccurs="unbounded" > + <xs:complexType> + <xs:attribute name="identifier"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="identifier"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="resource"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="action"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:enumeration value="R"/> + <xs:enumeration value="W"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> + + <xs:complexType name="Policies"> + <xs:sequence> + <xs:element name="policy" type="Policy" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + + <!-- top-level authorizations element --> + <xs:element name="authorizations"> + <xs:complexType> + <xs:sequence> + <xs:element name="policies" type="Policies" minOccurs="0" maxOccurs="1" /> + </xs:sequence> + </xs:complexType> + </xs:element> + +</xs:schema> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api-impl/src/main/xsd/tenants.xsd ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api-impl/src/main/xsd/tenants.xsd b/nifi-registry-security-api-impl/src/main/xsd/tenants.xsd new file mode 100644 index 0000000..c1193c3 --- /dev/null +++ b/nifi-registry-security-api-impl/src/main/xsd/tenants.xsd @@ -0,0 +1,96 @@ +<?xml version="1.0"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + <!-- group --> + <xs:complexType name="Group"> + <xs:sequence> + <xs:element name="user" minOccurs="0" maxOccurs="unbounded" > + <xs:complexType> + <xs:attribute name="identifier"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="identifier"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="name"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> + + <!-- groups --> + <xs:complexType name="Groups"> + <xs:sequence> + <xs:element name="group" type="Group" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + + <!-- user --> + <xs:complexType name="User"> + <xs:attribute name="identifier"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="identity"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + <xs:pattern value=".*[^\s].*"/> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + </xs:complexType> + + <!-- users --> + <xs:complexType name="Users"> + <xs:sequence> + <xs:element name="user" type="User" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + + <!-- top-level authorizations element --> + <xs:element name="tenants"> + <xs:complexType> + <xs:sequence> + <xs:element name="groups" type="Groups" minOccurs="0" maxOccurs="1" /> + <xs:element name="users" type="Users" minOccurs="0" maxOccurs="1" /> + </xs:sequence> + </xs:complexType> + </xs:element> + +</xs:schema> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/pom.xml ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/pom.xml b/nifi-registry-security-api/pom.xml new file mode 100644 index 0000000..8bb9eb7 --- /dev/null +++ b/nifi-registry-security-api/pom.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You under the Apache License, Version 2.0 + ~ (the "License"); you may not use this file except in compliance with + ~ the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>nifi-registry</artifactId> + <groupId>org.apache.nifi.registry</groupId> + <version>0.0.1-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>nifi-registry-security-api</artifactId> + + <dependencies> + <dependency> + <groupId>org.apache.nifi.registry</groupId> + <artifactId>nifi-registry-utils</artifactId> + <version>0.0.1-SNAPSHOT</version> + </dependency> + </dependencies> + +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicy.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicy.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicy.java new file mode 100644 index 0000000..7f2bfa4 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicy.java @@ -0,0 +1,367 @@ +/* + * 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.registry.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. + */ +public class AccessPolicy { + + private final String identifier; + + private final String resource; + + private final Set<String> users; + + private final Set<String> groups; + + private final RequestAction action; + + private AccessPolicy(final Builder builder) { + this.identifier = builder.identifier; + this.resource = builder.resource; + this.action = builder.action; + this.users = Collections.unmodifiableSet(new HashSet<>(builder.users)); + this.groups = Collections.unmodifiableSet(new HashSet<>(builder.groups)); + + if (this.identifier == null || this.identifier.trim().isEmpty()) { + throw new IllegalArgumentException("Identifier can not be null or empty"); + } + + if (this.resource == null) { + throw new IllegalArgumentException("Resource can not be null"); + } + + if (this.action == null) { + throw new IllegalArgumentException("Action can not be null"); + } + } + + /** + * @return the identifier for this policy + */ + public String getIdentifier() { + return identifier; + } + + /** + * @return the resource for this policy + */ + public String getResource() { + return resource; + } + + /** + * @return an unmodifiable set of user ids for this policy + */ + public Set<String> getUsers() { + return users; + } + + /** + * @return an unmodifiable set of group ids for this policy + */ + public Set<String> getGroups() { + return groups; + } + + /** + * @return the action for this policy + */ + public RequestAction getAction() { + return action; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + final AccessPolicy other = (AccessPolicy) obj; + return Objects.equals(this.identifier, other.identifier); + } + + @Override + public int hashCode() { + return Objects.hashCode(this.identifier); + } + + @Override + public String toString() { + return String.format("identifier[%s], resource[%s], users[%s], groups[%s], action[%s]", + getIdentifier(), getResource(), getUsers(), getGroups(), getAction()); + } + + /** + * Builder for Access Policies. + */ + public static class Builder { + + private String identifier; + private String resource; + private RequestAction action; + private Set<String> users = new HashSet<>(); + private Set<String> groups = new HashSet<>(); + private final boolean fromPolicy; + + /** + * Default constructor for building a new AccessPolicy. + */ + public Builder() { + this.fromPolicy = false; + } + + /** + * Initializes the builder with the state of the provided policy. When using this constructor + * the identifier field of the builder can not be changed and will result in an IllegalStateException + * if attempting to do so. + * + * @param other the existing access policy to initialize from + */ + public Builder(final AccessPolicy other) { + if (other == null) { + throw new IllegalArgumentException("Can not initialize builder with a null access policy"); + } + + this.identifier = other.getIdentifier(); + this.resource = other.getResource(); + this.action = other.getAction(); + this.users.clear(); + this.users.addAll(other.getUsers()); + this.groups.clear(); + this.groups.addAll(other.getGroups()); + this.fromPolicy = true; + } + + /** + * Sets the identifier of the builder. + * + * @param identifier the identifier to set + * @return the builder + * @throws IllegalStateException if this method is called when this builder was constructed from an existing Policy + */ + public Builder identifier(final String identifier) { + if (fromPolicy) { + throw new IllegalStateException( + "Identifier can not be changed when initialized from an existing policy"); + } + + this.identifier = identifier; + 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. + * + * @param resource the resource to set + * @return the builder + */ + public Builder resource(final String resource) { + this.resource = resource; + return this; + } + + /** + * Adds all the users from the provided set to the builder's set of users. + * + * @param users the users to add + * @return the builder + */ + public Builder addUsers(final Set<String> users) { + if (users != null) { + this.users.addAll(users); + } + return this; + } + + /** + * Adds the given user to the builder's set of users. + * + * @param user the user to add + * @return the builder + */ + public Builder addUser(final String user) { + if (user != null) { + this.users.add(user); + } + return this; + } + + /** + * Removes all users in the provided set from the builder's set of users. + * + * @param users the users to remove + * @return the builder + */ + public Builder removeUsers(final Set<String> users) { + if (users != null) { + this.users.removeAll(users); + } + return this; + } + + /** + * Removes the provided user from the builder's set of users. + * + * @param user the user to remove + * @return the builder + */ + public Builder removeUser(final String user) { + if (user != null) { + this.users.remove(user); + } + return this; + } + + /** + * Clears the builder's set of users so that it is non-null and size == 0. + * + * @return the builder + */ + public Builder clearUsers() { + this.users.clear(); + return this; + } + + /** + * Adds all the groups from the provided set to the builder's set of groups. + * + * @param groups the groups to add + * @return the builder + */ + public Builder addGroups(final Set<String> groups) { + if (groups != null) { + this.groups.addAll(groups); + } + return this; + } + + /** + * Adds the given group to the builder's set of groups. + * + * @param group the group to add + * @return the builder + */ + public Builder addGroup(final String group) { + if (group != null) { + this.groups.add(group); + } + return this; + } + + /** + * Removes all groups in the provided set from the builder's set of groups. + * + * @param groups the groups to remove + * @return the builder + */ + public Builder removeGroups(final Set<String> groups) { + if (groups != null) { + this.groups.removeAll(groups); + } + return this; + } + + /** + * Removes the provided groups from the builder's set of groups. + * + * @param group the group to remove + * @return the builder + */ + public Builder removeGroup(final String group) { + if (group != null) { + this.groups.remove(group); + } + return this; + } + + /** + * Clears the builder's set of groups so that it is non-null and size == 0. + * + * @return the builder + */ + public Builder clearGroups() { + this.groups.clear(); + return this; + } + + /** + * Sets the action for this builder. + * + * @param action the action to set + * @return the builder + */ + public Builder action(final RequestAction action) { + this.action = action; + return this; + } + + /** + * @return a new AccessPolicy constructed from the state of the builder + */ + public AccessPolicy build() { + return new AccessPolicy(this); + } + } + +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProvider.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProvider.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProvider.java new file mode 100644 index 0000000..d7e44b2 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProvider.java @@ -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.registry.authorization; + +import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.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; +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProviderInitializationContext.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProviderInitializationContext.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProviderInitializationContext.java new file mode 100644 index 0000000..756bf32 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProviderInitializationContext.java @@ -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.registry.authorization; + +/** + * Initialization content for AccessPolicyProviders. + */ +public interface AccessPolicyProviderInitializationContext extends UserGroupProviderInitializationContext { + + /** + * The lookup for accessing other configured AccessPolicyProviders. + * + * @return The AccessPolicyProvider lookup + */ + AccessPolicyProviderLookup getAccessPolicyProviderLookup(); +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProviderLookup.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProviderLookup.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProviderLookup.java new file mode 100644 index 0000000..ff5b9d4 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProviderLookup.java @@ -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.registry.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); +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationAuditor.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationAuditor.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationAuditor.java new file mode 100644 index 0000000..4f017c5 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationAuditor.java @@ -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.registry.authorization; + +public interface AuthorizationAuditor { + + /** + * Audits an authorization request. Will be invoked for any Approved or Denied results. ResourceNotFound + * will either re-attempt authorization using a parent resource or will generate a failure result and + * audit that. + * + * @param request the request for authorization + * @param result the authorization result + */ + void auditAccessAttempt(final AuthorizationRequest request, final AuthorizationResult result); +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationRequest.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationRequest.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationRequest.java new file mode 100644 index 0000000..5c506ef --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationRequest.java @@ -0,0 +1,245 @@ +/* + * 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.registry.authorization; + +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; + +/** + * Represents an authorization request for a given user/entity performing an action against a resource within some userContext. + */ +public class AuthorizationRequest { + + public static final String DEFAULT_EXPLANATION = "Unable to perform the desired action."; + + private final Resource resource; + private final Resource requestedResource; + private final String identity; + private final Set<String> groups; + private final RequestAction action; + private final boolean isAccessAttempt; + private final boolean isAnonymous; + private final Map<String, String> userContext; + private final Map<String, String> resourceContext; + private final Supplier<String> explanationSupplier; + + private AuthorizationRequest(final Builder builder) { + Objects.requireNonNull(builder.resource, "The resource is required when creating an authorization request"); + Objects.requireNonNull(builder.action, "The action is required when creating an authorization request"); + Objects.requireNonNull(builder.isAccessAttempt, "Whether this request is an access attempt is request"); + Objects.requireNonNull(builder.isAnonymous, "Whether this request is being performed by an anonymous user is required"); + + 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; + this.userContext = builder.userContext == null ? null : Collections.unmodifiableMap(builder.userContext); + this.resourceContext = builder.resourceContext == null ? null : Collections.unmodifiableMap(builder.resourceContext); + this.explanationSupplier = () -> { + final String explanation = builder.explanationSupplier.get(); + + // ensure the specified supplier returns non null + if (explanation == null) { + return DEFAULT_EXPLANATION; + } else { + return explanation; + } + }; + + if (builder.requestedResource == null) { + this.requestedResource = builder.resource; + } else { + this.requestedResource = builder.requestedResource; + } + } + + /** + * The Resource being authorized. Not null. + * + * @return The resource + */ + public Resource getResource() { + return resource; + } + + /** + * The original Resource being requested. In cases with inherited policies, this will be a ancestor resource of + * of the current resource. The initial request, and cases without inheritance, the requested resource will be + * the same as the current resource. + * + * @return The requested resource + */ + public Resource getRequestedResource() { + return requestedResource; + } + + /** + * The identity accessing the Resource. May be null if the user could not authenticate. + * + * @return The identity + */ + public String getIdentity() { + 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. + * + * @return if this is a direct access attempt + */ + public boolean isAccessAttempt() { + return isAccessAttempt; + } + + /** + * Whether the entity accessing is anonymous. + * + * @return whether the entity is anonymous + */ + public boolean isAnonymous() { + return isAnonymous; + } + + /** + * The action being taken against the Resource. Not null. + * + * @return The action + */ + public RequestAction getAction() { + return action; + } + + /** + * The userContext of the user request to make additional access decisions. May be null. + * + * @return The userContext of the user request + */ + public Map<String, String> getUserContext() { + return userContext; + } + + /** + * The event attributes to make additional access decisions for provenance events. May be null. + * + * @return The event attributes + */ + public Map<String, String> getResourceContext() { + return resourceContext; + } + + /** + * A supplier for the explanation if access is denied. Non null. + * + * @return The explanation supplier if access is denied + */ + public Supplier<String> getExplanationSupplier() { + return explanationSupplier; + } + + /** + * AuthorizationRequest builder. + */ + public static final class Builder { + + private Resource resource; + private Resource requestedResource; + private String identity; + private Set<String> groups; + private Boolean isAnonymous; + private Boolean isAccessAttempt; + private RequestAction action; + private Map<String, String> userContext; + private Map<String, String> resourceContext; + private Supplier<String> explanationSupplier = () -> DEFAULT_EXPLANATION; + + public Builder resource(final Resource resource) { + this.resource = resource; + return this; + } + + public Builder requestedResource(final Resource requestedResource) { + this.requestedResource = requestedResource; + return this; + } + + public Builder identity(final String identity) { + this.identity = identity; + 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; + } + + public Builder accessAttempt(final Boolean isAccessAttempt) { + this.isAccessAttempt = isAccessAttempt; + return this; + } + + public Builder action(final RequestAction action) { + this.action = action; + return this; + } + + public Builder userContext(final Map<String, String> userContext) { + if (userContext != null) { + this.userContext = new HashMap<>(userContext); + } + return this; + } + + public Builder resourceContext(final Map<String, String> resourceContext) { + if (resourceContext != null) { + this.resourceContext = new HashMap<>(resourceContext); + } + return this; + } + + public Builder explanationSupplier(final Supplier<String> explanationSupplier) { + if (explanationSupplier != null) { + this.explanationSupplier = explanationSupplier; + } + return this; + } + + public AuthorizationRequest build() { + return new AuthorizationRequest(this); + } + } +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationResult.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationResult.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationResult.java new file mode 100644 index 0000000..3cfd056 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationResult.java @@ -0,0 +1,103 @@ +/* + * 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.registry.authorization; + +/** + * Represents a decision whether authorization is granted. + */ +public class AuthorizationResult { + + public enum Result { + Approved, + Denied, + ResourceNotFound + } + + private static final AuthorizationResult APPROVED = new AuthorizationResult(Result.Approved, null); + private static final AuthorizationResult RESOURCE_NOT_FOUND = new AuthorizationResult(Result.ResourceNotFound, "Not authorized for the requested resource."); + + private final Result result; + private final String explanation; + + /** + * Creates a new AuthorizationResult with the specified result and explanation. + * + * @param result of the authorization + * @param explanation for the authorization attempt + */ + private AuthorizationResult(Result result, String explanation) { + if (Result.Denied.equals(result) && explanation == null) { + throw new IllegalArgumentException("An explanation is required when the authorization request is denied."); + } + + if (Result.ResourceNotFound.equals(result) && explanation == null) { + throw new IllegalArgumentException("An explanation is required when the authorization request is resource not found."); + } + + this.result = result; + this.explanation = explanation; + } + + /** + * @return Whether or not the request is approved + */ + public Result getResult() { + return result; + } + + /** + * @return If the request is denied, the reason why. Null otherwise + */ + public String getExplanation() { + return explanation; + } + + /** + * @return a new approved AuthorizationResult + */ + public static AuthorizationResult approved() { + return APPROVED; + } + + /** + * Resource not found will indicate that there are no specific authorization rules for this resource. + * @return a new resource not found AuthorizationResult + */ + public static AuthorizationResult resourceNotFound() { + return RESOURCE_NOT_FOUND; + } + + /** + * Creates a new denied AuthorizationResult with a message indicating 'Access is denied'. + * + * @return a new denied AuthorizationResult + */ + public static AuthorizationResult denied() { + return denied(AuthorizationRequest.DEFAULT_EXPLANATION); + } + + /** + * Creates a new denied AuthorizationResult with the specified explanation. + * + * @param explanation for why it was denied + * @return a new denied AuthorizationResult with the specified explanation + * @throws IllegalArgumentException if explanation is null + */ + public static AuthorizationResult denied(String explanation) { + return new AuthorizationResult(Result.Denied, explanation); + } +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Authorizer.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Authorizer.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Authorizer.java new file mode 100644 index 0000000..3f805d5 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Authorizer.java @@ -0,0 +1,63 @@ +/* + * 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.registry.authorization; + +import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.authorization.exception.AuthorizerDestructionException; + +/** + * Authorizes user requests. + */ +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 + * @throws AuthorizationAccessException if unable to access the policies + */ + AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException; + + /** + * Called immediately after instance creation for implementers to perform additional setup + * + * @param initializationContext in which to initialize + */ + void initialize(AuthorizerInitializationContext 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; + +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerConfigurationContext.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerConfigurationContext.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerConfigurationContext.java new file mode 100644 index 0000000..fb1b206 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerConfigurationContext.java @@ -0,0 +1,48 @@ +/* + * 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.registry.authorization; + +import org.apache.nifi.registry.util.PropertyValue; + +import java.util.Map; + +/** + * + */ +public interface AuthorizerConfigurationContext { + + /** + * @return identifier for the authorizer + */ + String getIdentifier(); + + /** + * Retrieves all properties the component currently understands regardless + * of whether a value has been set for them or not. If no value is present + * then its value is null and thus any registered default for the property + * descriptor applies. + * + * @return Map of all properties + */ + Map<String, String> getProperties(); + + /** + * @param property to lookup the descriptor and value of + * @return the value the component currently understands for the given PropertyDescriptor + */ + PropertyValue getProperty(String property); +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerInitializationContext.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerInitializationContext.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerInitializationContext.java new file mode 100644 index 0000000..c4ef14f --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerInitializationContext.java @@ -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.registry.authorization; + +/** + * Initialization content for Authorizers. + */ +public interface AuthorizerInitializationContext extends AccessPolicyProviderInitializationContext { + + /** + * The lookup for accessing other configured Authorizers. + * + * @return The Authorizer lookup + */ + AuthorizerLookup getAuthorizerLookup(); +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerLookup.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerLookup.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerLookup.java new file mode 100644 index 0000000..4d7641b --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerLookup.java @@ -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.registry.authorization; + +/** + * + */ +public interface AuthorizerLookup { + + /** + * Looks up the Authorizer with the specified identifier + * + * @param identifier The identifier of the Authorizer + * @return The Authorizer + */ + Authorizer getAuthorizer(String identifier); +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ConfigurableAccessPolicyProvider.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ConfigurableAccessPolicyProvider.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ConfigurableAccessPolicyProvider.java new file mode 100644 index 0000000..b563c40 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ConfigurableAccessPolicyProvider.java @@ -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.registry.authorization; + +import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.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; + + /** + * Determines whether the specified access policy is configurable. Provides the opportunity for a ConfigurableAccessPolicyProvider to prevent + * editing of a specific access policy. By default, all known access policies are configurable. + * + * @param accessPolicy the access policy + * @return is configurable + */ + default boolean isConfigurable(AccessPolicy accessPolicy) { + if (accessPolicy == null) { + throw new IllegalArgumentException("Access policy cannot be null"); + } + + return getAccessPolicy(accessPolicy.getIdentifier()) != null; + } + + /** + * 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; + + /** + * Deletes the policy with the specified identifier. + * + * @param accessPolicyIdentifier 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(String accessPolicyIdentifier) throws AuthorizationAccessException; +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/785cb81f/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ConfigurableUserGroupProvider.java ---------------------------------------------------------------------- diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ConfigurableUserGroupProvider.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ConfigurableUserGroupProvider.java new file mode 100644 index 0000000..83a7124 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ConfigurableUserGroupProvider.java @@ -0,0 +1,163 @@ +/* + * 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.registry.authorization; + +import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.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; + + /** + * Determines whether the specified user is configurable. Provides the opportunity for a ConfigurableUserGroupProvider to prevent + * editing of a specific user. By default, all known users are configurable. + * + * @param user the user + * @return is configurable + */ + default boolean isConfigurable(User user) { + if (user == null) { + throw new IllegalArgumentException("User cannot be null"); + } + + return getUser(user.getIdentifier()) != null; + } + + /** + * 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; + + /** + * Deletes the user for the given ID. + * + * @param userIdentifier 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(String userIdentifier) 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; + + /** + * Determines whether the specified group is configurable. Provides the opportunity for a ConfigurableUserGroupProvider to prevent + * editing of a specific group. By default, all known groups are configurable. + * + * @param group the group + * @return is configurable + */ + default boolean isConfigurable(Group group) { + if (group == null) { + throw new IllegalArgumentException("Group cannot be null"); + } + + return getGroup(group.getIdentifier()) != null; + } + + /** + * 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; + + /** + * Deletes the given group. + * + * @param groupIdentifier 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(String groupIdentifier) throws AuthorizationAccessException; +}
