You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by kd...@apache.org on 2018/09/22 02:11:29 UTC
[30/51] [partial] nifi-registry git commit: NIFIREG-201 Refactoring
project structure to better isolate extensions
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/xsd/authorizations.xsd
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/xsd/authorizations.xsd b/nifi-registry-core/nifi-registry-framework/src/main/xsd/authorizations.xsd
new file mode 100644
index 0000000..2c8f805
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/xsd/authorizations.xsd
@@ -0,0 +1,87 @@
+<?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:enumeration value="D"/>
+ </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/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/xsd/authorizers.xsd
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/xsd/authorizers.xsd b/nifi-registry-core/nifi-registry-framework/src/main/xsd/authorizers.xsd
new file mode 100644
index 0000000..ed2a293
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/xsd/authorizers.xsd
@@ -0,0 +1,68 @@
+<?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">
+
+ <!-- user group providers type -->
+ <xs:complexType name="UserGroupProvider">
+ <xs:sequence>
+ <xs:element name="identifier" type="xs:string"/>
+ <xs:element name="class" type="xs:string"/>
+ <xs:element name="property" type="Prop" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- access policy provider type -->
+ <xs:complexType name="AccessPolicyProvider">
+ <xs:sequence>
+ <xs:element name="identifier" type="xs:string"/>
+ <xs:element name="class" type="xs:string"/>
+ <xs:element name="property" type="Prop" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- authorizers type -->
+ <xs:complexType name="Authorizer">
+ <xs:sequence>
+ <xs:element name="identifier" type="xs:string"/>
+ <xs:element name="class" type="xs:string"/>
+ <xs:element name="classpath" type="xs:string" minOccurs="0"/>
+ <xs:element name="property" type="Prop" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- Name/Value properties-->
+ <xs:complexType name="Prop">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="name" type="xs:string"></xs:attribute>
+ <xs:attribute name="encryption" type="xs:string"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <!-- 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>
+ </xs:element>
+</xs:schema>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/xsd/identity-providers.xsd
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/xsd/identity-providers.xsd b/nifi-registry-core/nifi-registry-framework/src/main/xsd/identity-providers.xsd
new file mode 100644
index 0000000..bcca014
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/xsd/identity-providers.xsd
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <!-- role -->
+ <xs:complexType name="Provider">
+ <xs:sequence>
+ <xs:element name="identifier" type="NonEmptyStringType"/>
+ <xs:element name="class" type="NonEmptyStringType"/>
+ <xs:element name="property" type="Property" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- Name/Value properties-->
+ <xs:complexType name="Property">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="name" type="NonEmptyStringType"/>
+ <xs:attribute name="encryption" type="xs:string"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:simpleType name="NonEmptyStringType">
+ <xs:restriction base="xs:string">
+ <xs:minLength value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- login identity provider -->
+ <xs:element name="identityProviders">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="provider" type="Provider" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/xsd/providers.xsd
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/xsd/providers.xsd b/nifi-registry-core/nifi-registry-framework/src/main/xsd/providers.xsd
new file mode 100644
index 0000000..ce82dcc
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/xsd/providers.xsd
@@ -0,0 +1,51 @@
+<?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">
+
+ <!-- Provider type -->
+ <xs:complexType name="Provider">
+ <xs:sequence>
+ <xs:element name="class" type="NonEmptyStringType"/>
+ <xs:element name="property" type="Property" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- Name/Value properties-->
+ <xs:complexType name="Property">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="name" type="NonEmptyStringType"></xs:attribute>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+
+ <xs:simpleType name="NonEmptyStringType">
+ <xs:restriction base="xs:string">
+ <xs:minLength value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <!-- providers -->
+ <xs:element name="providers">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="flowPersistenceProvider" type="Provider" minOccurs="1" maxOccurs="1" />
+ <xs:element name="eventHookProvider" type="Provider" minOccurs="0" maxOccurs="unbounded" />
+ </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/6f26290d/nifi-registry-core/nifi-registry-framework/src/main/xsd/tenants.xsd
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/xsd/tenants.xsd b/nifi-registry-core/nifi-registry-framework/src/main/xsd/tenants.xsd
new file mode 100644
index 0000000..c1193c3
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/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/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/security/authorization/AuthorizerFactorySpec.groovy
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/security/authorization/AuthorizerFactorySpec.groovy b/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/security/authorization/AuthorizerFactorySpec.groovy
new file mode 100644
index 0000000..932d483
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/security/authorization/AuthorizerFactorySpec.groovy
@@ -0,0 +1,129 @@
+/*
+ * 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.security.authorization
+
+import org.apache.nifi.registry.extension.ExtensionManager
+import org.apache.nifi.registry.properties.NiFiRegistryProperties
+import org.apache.nifi.registry.security.authorization.file.FileAccessPolicyProvider
+import org.apache.nifi.registry.security.authorization.file.FileUserGroupProvider
+import org.apache.nifi.registry.security.authorization.resource.ResourceFactory
+import spock.lang.Specification
+
+class AuthorizerFactorySpec extends Specification {
+
+ def mockProperties = Mock(NiFiRegistryProperties)
+ def mockExtensionManager = Mock(ExtensionManager)
+
+ AuthorizerFactory authorizerFactory
+
+ // runs before every feature method
+ def setup() {
+ mockExtensionManager.getExtensionClassLoader(_) >> this.getClass().getClassLoader()
+ mockProperties.getPropertyKeys() >> new HashSet<String>() // Called by IdentityMappingUtil.getIdentityMappings()
+
+ authorizerFactory = new AuthorizerFactory(mockProperties, mockExtensionManager, null)
+ }
+
+ // runs after every feature method
+ def cleanup() {
+ authorizerFactory = null
+ }
+
+ // runs before the first feature method
+ def setupSpec() {}
+
+ // runs after the last feature method
+ def cleanupSpec() {}
+
+ def "create default authorizer"() {
+
+ setup: "properties indicate nifi-registry is unsecured"
+ mockProperties.getProperty(NiFiRegistryProperties.WEB_HTTPS_PORT) >> ""
+
+ when: "getAuthorizer() is first called"
+ def authorizer = authorizerFactory.getAuthorizer()
+
+ then: "the default authorizer is returned"
+ authorizer != null
+
+ and: "any authorization request made to that authorizer is approved"
+ def authorizationResult = authorizer.authorize(getTestAuthorizationRequest())
+ authorizationResult.result == AuthorizationResult.Result.Approved
+
+ }
+
+ def "create file-backed authorizer"() {
+
+ setup:
+ setMockPropsAuthorizersConfig("src/test/resources/security/authorizers-good-file-providers.xml", "managed-authorizer")
+
+ when: "getAuthorizer() is first called"
+ def authorizer = authorizerFactory.getAuthorizer()
+
+ then: "an authorizer is returned with the expected providers"
+ authorizer != null
+ authorizer instanceof ManagedAuthorizer
+ def apProvider = ((ManagedAuthorizer) authorizer).getAccessPolicyProvider()
+ apProvider instanceof ConfigurableAccessPolicyProvider
+ def ugProvider = ((ConfigurableAccessPolicyProvider) apProvider).getUserGroupProvider()
+ ugProvider instanceof ConfigurableUserGroupProvider
+
+ }
+
+ def "invalid authorizer configuration fails"() {
+
+ when: "a bad configuration is provided and getAuthorizer() is called"
+ setMockPropsAuthorizersConfig(authorizersConfigFile, selectedAuthorizer)
+ authorizerFactory = new AuthorizerFactory(mockProperties, mockExtensionManager, null)
+ authorizerFactory.getAuthorizer()
+
+ then: "expect an exception"
+ def e = thrown AuthorizerFactoryException
+ e.message =~ expectedExceptionMessage || e.getCause().getMessage() =~ expectedExceptionMessage
+
+ where:
+ authorizersConfigFile | selectedAuthorizer | expectedExceptionMessage
+ "src/test/resources/security/authorizers-good-file-providers.xml" | "" | "When running securely, the authorizer identifier must be specified in the nifi-registry.properties file."
+ "src/test/resources/security/authorizers-good-file-providers.xml" | "non-existent-authorizer" | "The specified authorizer 'non-existent-authorizer' could not be found."
+ "src/test/resources/security/authorizers-bad-ug-provider-ids.xml" | "managed-authorizer" | "Duplicate User Group Provider identifier in Authorizers configuration"
+ "src/test/resources/security/authorizers-bad-ap-provider-ids.xml" | "managed-authorizer" | "Duplicate Access Policy Provider identifier in Authorizers configuration"
+ "src/test/resources/security/authorizers-bad-authorizer-ids.xml" | "managed-authorizer" | "Duplicate Authorizer identifier in Authorizers configuration"
+ "src/test/resources/security/authorizers-bad-composite.xml" | "managed-authorizer" | "Duplicate provider in Composite User Group Provider configuration"
+ "src/test/resources/security/authorizers-bad-configurable-composite.xml" | "managed-authorizer" | "Duplicate provider in Composite Configurable User Group Provider configuration"
+
+ }
+
+ // Helper methods
+
+ private void setMockPropsAuthorizersConfig(String filePath, String authorizer = "managed-authorizer") {
+ mockProperties.getProperty(NiFiRegistryProperties.WEB_HTTPS_PORT) >> "443"
+ mockProperties.getSslPort() >> 443 // required to be non-null to create authorizer
+ mockProperties.getProperty(NiFiRegistryProperties.SECURITY_AUTHORIZERS_CONFIGURATION_FILE) >> filePath
+ mockProperties.getAuthorizersConfigurationFile() >> new File(filePath)
+ mockProperties.getProperty(NiFiRegistryProperties.SECURITY_AUTHORIZER) >> authorizer
+ }
+
+ private static AuthorizationRequest getTestAuthorizationRequest() {
+ return new AuthorizationRequest.Builder()
+ .resource(ResourceFactory.getBucketsResource())
+ .action(RequestAction.WRITE)
+ .accessAttempt(false)
+ .anonymous(true)
+ .build()
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy b/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
new file mode 100644
index 0000000..149ec36
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
@@ -0,0 +1,615 @@
+/*
+ * 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.service
+
+import org.apache.nifi.registry.authorization.AccessPolicy
+import org.apache.nifi.registry.authorization.User
+import org.apache.nifi.registry.authorization.UserGroup
+import org.apache.nifi.registry.bucket.Bucket
+import org.apache.nifi.registry.security.authorization.AccessPolicy as AuthAccessPolicy
+import org.apache.nifi.registry.security.authorization.AuthorizableLookup
+import org.apache.nifi.registry.security.authorization.ConfigurableAccessPolicyProvider
+import org.apache.nifi.registry.security.authorization.ConfigurableUserGroupProvider
+import org.apache.nifi.registry.security.authorization.Group
+import org.apache.nifi.registry.security.authorization.RequestAction
+import org.apache.nifi.registry.security.authorization.StandardManagedAuthorizer
+import org.apache.nifi.registry.security.authorization.User as AuthUser
+import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException
+import org.apache.nifi.registry.security.authorization.resource.Authorizable
+import org.apache.nifi.registry.security.authorization.resource.ResourceType
+import spock.lang.Specification
+
+class AuthorizationServiceSpec extends Specification {
+
+ def registryService = Mock(RegistryService)
+ def authorizableLookup = Mock(AuthorizableLookup)
+ def userGroupProvider = Mock(ConfigurableUserGroupProvider)
+ def accessPolicyProvider = Mock(ConfigurableAccessPolicyProvider)
+
+ AuthorizationService authorizationService
+
+ def setup() {
+ accessPolicyProvider.getUserGroupProvider() >> userGroupProvider
+ def authorizer = new StandardManagedAuthorizer(accessPolicyProvider, userGroupProvider)
+ authorizationService = new AuthorizationService(authorizableLookup, authorizer, registryService)
+ }
+
+ // ----- User tests -------------------------------------------------------
+
+ def "create user"() {
+
+ setup:
+ userGroupProvider.addUser(!null as AuthUser) >> {
+ AuthUser u -> new AuthUser.Builder().identifier(u.identifier).identity(u.identity).build()
+ }
+ userGroupProvider.getGroups() >> new HashSet<Group>() // needed for converting user to DTO
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>() // needed for converting user to DTO
+
+ when: "new user is created successfully"
+ def user = new User(null, "username")
+ User createdUser = authorizationService.createUser(user)
+
+ then: "created user has been assigned an identifier"
+ with(createdUser) {
+ identifier != null
+ identity == "username"
+ }
+
+ }
+
+ def "list users"() {
+
+ setup:
+ userGroupProvider.getUsers() >> [
+ new AuthUser.Builder().identifier("user1").identity("username1").build(),
+ new AuthUser.Builder().identifier("user2").identity("username2").build(),
+ new AuthUser.Builder().identifier("user3").identity("username3").build(),
+ ]
+ userGroupProvider.getGroups() >> new HashSet<Group>()
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+ when: "list of users is queried"
+ def users = authorizationService.getUsers()
+
+ then: "users are successfully returned as list of DTO objects"
+ users != null
+ users.size() == 3
+ with(users[0]) {
+ identifier == "user1"
+ identity == "username1"
+ }
+ with(users[1]) {
+ identifier == "user2"
+ identity == "username2"
+ }
+ with(users[2]) {
+ identifier == "user3"
+ identity == "username3"
+ }
+
+ }
+
+ def "get user"() {
+
+ setup:
+ userGroupProvider.getGroups() >> new HashSet<Group>()
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+
+ when: "get user for existing user identifier"
+ userGroupProvider.getUser("userId") >> new AuthUser.Builder().identifier("userId").identity("username").build()
+ def user1 = authorizationService.getUser("userId")
+
+ then: "user is returned converted to DTO"
+ with(user1) {
+ identifier == "userId"
+ identity == "username"
+ }
+
+
+ when: "get user for non-existent user identifier"
+ userGroupProvider.getUser("nonExistentUserId") >> null
+ userGroupProvider.getGroup("nonExistentUserId") >> null
+ def user2 = authorizationService.getUser("nonExistentUserId")
+
+ then: "no user is returned"
+ user2 == null
+
+ }
+
+ def "update user"() {
+
+ setup:
+ userGroupProvider.updateUser(!null as AuthUser) >> {
+ AuthUser u -> new AuthUser.Builder().identifier(u.identifier).identity(u.identity).build()
+ }
+ userGroupProvider.getGroups() >> new HashSet<Group>()
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+
+ when: "user is updated"
+ def user = authorizationService.updateUser(new User("userId", "username"))
+
+ then: "updated user is returned"
+ with(user) {
+ identifier == "userId"
+ identity == "username"
+ }
+
+ }
+
+ def "delete user"() {
+
+ setup:
+ userGroupProvider.getUser("userId") >> new AuthUser.Builder().identifier("userId").identity("username").build()
+ userGroupProvider.deleteUser("userId") >> new AuthUser.Builder().identifier("userId").identity("username").build()
+ userGroupProvider.getGroups() >> new HashSet<Group>()
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+
+ when: "user is deleted"
+ def user = authorizationService.deleteUser("userId")
+
+ then: "deleted user is returned converted to DTO"
+ with(user) {
+ identifier == "userId"
+ identity == "username"
+ }
+
+ }
+
+ // ----- User Group tests -------------------------------------------------
+
+ def "create user group"() {
+
+ setup:
+ userGroupProvider.addGroup(!null as Group) >> {
+ Group g -> new Group.Builder().identifier(g.identifier).name(g.name).build()
+ }
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>() // needed for converting to DTO
+
+ when: "new group is created successfully"
+ def group = new UserGroup(null, "groupName")
+ UserGroup createdGroup = authorizationService.createUserGroup(group)
+
+ then: "created group has been assigned an identifier"
+ with(createdGroup) {
+ identifier != null
+ identity == "groupName"
+ }
+
+ }
+
+ def "list user groups"() {
+
+ setup:
+ userGroupProvider.getGroups() >> [
+ new Group.Builder().identifier("groupId1").name("groupName1").build(),
+ new Group.Builder().identifier("groupId2").name("groupName2").build(),
+ new Group.Builder().identifier("groupId3").name("groupName3").build(),
+ ]
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+ when: "list of groups is queried"
+ def groups = authorizationService.getUserGroups()
+
+ then: "groups are successfully returned as list of DTO objects"
+ groups != null
+ groups.size() == 3
+ with(groups[0]) {
+ identifier == "groupId1"
+ identity == "groupName1"
+ }
+ with(groups[1]) {
+ identifier == "groupId2"
+ identity == "groupName2"
+ }
+ with(groups[2]) {
+ identifier == "groupId3"
+ identity == "groupName3"
+ }
+
+ }
+
+ def "get user group"() {
+
+ setup:
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+
+ when: "get group for existing user identifier"
+ userGroupProvider.getGroup("groupId") >> new Group.Builder().identifier("groupId").name ("groupName").build()
+ def g1 = authorizationService.getUserGroup("groupId")
+
+ then: "group is returned converted to DTO"
+ with(g1) {
+ identifier == "groupId"
+ identity == "groupName"
+ }
+
+
+ when: "get group for non-existent group identifier"
+ userGroupProvider.getUser("nonExistentId") >> null
+ userGroupProvider.getGroup("nonExistentId") >> null
+ def g2 = authorizationService.getUserGroup("nonExistentId")
+
+ then: "no group is returned"
+ g2 == null
+
+ }
+
+ def "update user group"() {
+
+ setup:
+ userGroupProvider.updateGroup(!null as Group) >> {
+ Group g -> new Group.Builder().identifier(g.identifier).name(g.name).build()
+ }
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+
+ when: "group is updated"
+ def group = authorizationService.updateUserGroup(new UserGroup("id", "name"))
+
+ then: "updated group is returned converted to DTO"
+ with(group) {
+ identifier == "id"
+ identity == "name"
+ }
+
+ }
+
+ def "delete user group"() {
+
+ setup:
+ userGroupProvider.getGroup("id") >> new Group.Builder().identifier("id").name("name").build()
+ userGroupProvider.deleteGroup("id") >> new Group.Builder().identifier("id").name("name").build()
+ accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
+
+
+ when: "group is deleted"
+ def group = authorizationService.deleteUserGroup("id")
+
+ then: "deleted user is returned"
+ with(group) {
+ identifier == "id"
+ identity == "name"
+ }
+
+ }
+
+ // ----- Access Policy tests ----------------------------------------------
+
+ def "create access policy"() {
+
+ setup:
+ accessPolicyProvider.addAccessPolicy(!null as AuthAccessPolicy) >> {
+ AuthAccessPolicy p -> new AuthAccessPolicy.Builder()
+ .identifier(p.identifier)
+ .resource(p.resource)
+ .action(p.action)
+ .addGroups(p.groups)
+ .addUsers(p.users)
+ .build()
+ }
+ accessPolicyProvider.isConfigurable(_ as AuthAccessPolicy) >> true
+
+
+ when: "new access policy is created successfully"
+ def createdPolicy = authorizationService.createAccessPolicy(new AccessPolicy([resource: "/resource", action: "read"]))
+
+ then: "created policy has been assigned an identifier"
+ with(createdPolicy) {
+ identifier != null
+ resource == "/resource"
+ action == "read"
+ configurable == true
+ }
+
+ }
+
+ def "list access policies"() {
+
+ setup:
+ accessPolicyProvider.getAccessPolicies() >> [
+ new AuthAccessPolicy.Builder().identifier("ap1").resource("r1").action(RequestAction.READ).build(),
+ new AuthAccessPolicy.Builder().identifier("ap2").resource("r2").action(RequestAction.WRITE).build()
+ ]
+
+ when: "list access polices is queried"
+ def policies = authorizationService.getAccessPolicies()
+
+ then: "access policies are successfully returned as list of DTO objects"
+ policies != null
+ policies.size() == 2
+ with(policies[0]) {
+ identifier == "ap1"
+ resource == "r1"
+ action == RequestAction.READ.toString()
+ }
+ with(policies[1]) {
+ identifier == "ap2"
+ resource == "r2"
+ action == RequestAction.WRITE.toString()
+ }
+
+ }
+
+ def "get access policy"() {
+
+ when: "get policy for existing identifier"
+ accessPolicyProvider.getAccessPolicy("id") >> new AuthAccessPolicy.Builder()
+ .identifier("id")
+ .resource("/resource")
+ .action(RequestAction.READ)
+ .build()
+ def p1 = authorizationService.getAccessPolicy("id")
+
+ then: "policy is returned converted to DTO"
+ with(p1) {
+ identifier == "id"
+ resource == "/resource"
+ action == RequestAction.READ.toString()
+ }
+
+
+ when: "get policy for non-existent identifier"
+ accessPolicyProvider.getAccessPolicy("nonExistentId") >> null
+ def p2 = authorizationService.getAccessPolicy("nonExistentId")
+
+ then: "no policy is returned"
+ p2 == null
+
+ }
+
+
+ def "update access policy"() {
+
+ setup:
+ def users = [
+ "user1": "alice",
+ "user2": "bob",
+ "user3": "charlie" ]
+ def groups = [
+ "group1": "users",
+ "group2": "devs",
+ "group3": "admins" ]
+ def policies = [
+ "policy1": [
+ "resource": "/resource1",
+ "action": "read",
+ "users": [ "user1" ],
+ "groups": []
+ ]
+ ]
+ def mapDtoUser = { String id -> new User(id, users[id])}
+ def mapDtoGroup = { String id -> new UserGroup(id, groups[id])}
+ def mapAuthUser = { String id -> new AuthUser.Builder().identifier(id).identity(users[id]).build() }
+ def mapAuthGroup = { String id -> new Group.Builder().identifier(id).name(groups[id]).build() }
+ def mapAuthAccessPolicy = {
+ String id -> return new AuthAccessPolicy.Builder()
+ .identifier(id)
+ .resource(policies[id]["resource"] as String)
+ .action(RequestAction.valueOfValue(policies[id]["action"] as String))
+ .addUsers(policies[id]["users"] as Set<String>)
+ .addGroups(policies[id]["groups"] as Set<String>)
+ .build()
+ }
+ userGroupProvider.getUser(!null as String) >> { String id -> users.containsKey(id) ? mapAuthUser(id) : null }
+ userGroupProvider.getGroup(!null as String) >> { String id -> groups.containsKey(id) ? mapAuthGroup(id) : null }
+ userGroupProvider.getUsers() >> {
+ def authUsers = []
+ users.each{ k, v -> authUsers.add(new AuthUser.Builder().identifier(k).identity(v).build()) }
+ return authUsers
+ }
+ userGroupProvider.getGroups() >> {
+ def authGroups = []
+ users.each{ k, v -> authGroups.add(new Group.Builder().identifier(k).name(v).build()) }
+ return authGroups
+ }
+ accessPolicyProvider.getAccessPolicy(!null as String) >> { String id -> policies.containsKey(id) ? mapAuthAccessPolicy(id) : null }
+ accessPolicyProvider.updateAccessPolicy(!null as AuthAccessPolicy) >> {
+ AuthAccessPolicy p -> new AuthAccessPolicy.Builder()
+ .identifier(p.identifier)
+ .resource(p.resource)
+ .action(p.action)
+ .addGroups(p.groups)
+ .addUsers(p.users)
+ .build()
+ }
+ accessPolicyProvider.isConfigurable(_ as AuthAccessPolicy) >> true
+
+
+ when: "policy is updated"
+ def policy = new AccessPolicy([identifier: "policy1", resource: "/resource1", action: "read"])
+ policy.addUsers([mapDtoUser("user1"), mapDtoUser("user2")])
+ policy.addUserGroups([mapDtoGroup("group1")])
+ def p1 = authorizationService.updateAccessPolicy(policy)
+
+ then: "updated group is returned converted to DTO"
+ p1 != null
+ p1.users.size() == 2
+ def sortedUsers = p1.users.sort{it.identifier}
+ with(sortedUsers[0]) {
+ identifier == "user1"
+ identity == "alice"
+ }
+ with(sortedUsers[1]) {
+ identifier == "user2"
+ identity == "bob"
+ }
+ p1.userGroups.size() == 1
+ with(p1.userGroups[0]) {
+ identifier == "group1"
+ identity == "users"
+ }
+
+
+ when: "attempt to change policy resource and action"
+ def p2 = authorizationService.updateAccessPolicy(new AccessPolicy([identifier: "policy1", resource: "/newResource", action: "write"]))
+
+ then: "resource and action are unchanged"
+ with(p2) {
+ identifier == "policy1"
+ resource == "/resource1"
+ action == "read"
+ }
+
+ }
+
+ def "delete access policy"() {
+
+ setup:
+ userGroupProvider.getGroups() >> new HashSet<Group>()
+ userGroupProvider.getUsers() >> new HashSet<AuthUser>()
+ accessPolicyProvider.getAccessPolicy("id") >> {
+ String id -> new AuthAccessPolicy.Builder()
+ .identifier("id")
+ .resource("/resource")
+ .action(RequestAction.READ)
+ .addGroups(new HashSet<String>())
+ .addUsers(new HashSet<String>())
+ .build()
+ }
+ accessPolicyProvider.deleteAccessPolicy(!null as String) >> {
+ String id -> new AuthAccessPolicy.Builder()
+ .identifier(id)
+ .resource("/resource")
+ .action(RequestAction.READ)
+ .addGroups(new HashSet<String>())
+ .addUsers(new HashSet<String>())
+ .build()
+ }
+
+ when: "access policy is deleted"
+ def policy = authorizationService.deleteAccessPolicy("id")
+
+ then: "deleted policy is returned"
+ with(policy) {
+ identifier == "id"
+ resource == "/resource"
+ action == RequestAction.READ.toString()
+ }
+
+ }
+
+ // ----- Resource tests ---------------------------------------------------
+
+ def "get resources"() {
+
+ setup:
+ def buckets = [
+ "b1": [
+ "name": "Bucket #1",
+ "description": "An initial bucket for testing",
+ "createdTimestamp": 1
+ ],
+ "b2": [
+ "name": "Bucket #2",
+ "description": "A second bucket for testing",
+ "createdTimestamp": 2
+ ],
+ ]
+ def mapBucket = {
+ String id -> new Bucket([
+ identifier: id,
+ name: buckets[id]["name"] as String,
+ description: buckets[id]["description"] as String]) }
+
+ registryService.getBuckets() >> {[ mapBucket("b1"), mapBucket("b2") ]}
+
+ when:
+ def resources = authorizationService.getResources()
+
+ then:
+ resources != null
+ resources.size() == 8
+ def sortedResources = resources.sort{it.identifier}
+ sortedResources[0].identifier == "/actuator"
+ sortedResources[1].identifier == "/buckets"
+ sortedResources[2].identifier == "/buckets/b1"
+ sortedResources[3].identifier == "/buckets/b2"
+ sortedResources[4].identifier == "/policies"
+ sortedResources[5].identifier == "/proxy"
+ sortedResources[6].identifier == "/swagger"
+ sortedResources[7].identifier == "/tenants"
+
+ }
+
+ def "get authorized resources"() {
+
+ setup:
+ def buckets = [
+ "b1": [
+ "name": "Bucket #1",
+ "description": "An initial bucket for testing",
+ "createdTimestamp": 1
+ ],
+ "b2": [
+ "name": "Bucket #2",
+ "description": "A second bucket for testing",
+ "createdTimestamp": 2
+ ],
+ ]
+ def mapBucket = {
+ String id -> new Bucket([
+ identifier: id,
+ name: buckets[id]["name"] as String,
+ description: buckets[id]["description"] as String]) }
+
+ registryService.getBuckets() >> {[ mapBucket("b1"), mapBucket("b2") ]}
+
+ def authorized = Mock(Authorizable)
+ authorized.authorize(_, _, _) >> { return }
+ def denied = Mock(Authorizable)
+ denied.authorize(_, _, _) >> { throw new AccessDeniedException("") }
+
+ authorizableLookup.getAuthorizableByResource("/actuator") >> denied
+ authorizableLookup.getAuthorizableByResource("/buckets") >> authorized
+ authorizableLookup.getAuthorizableByResource("/buckets/b1") >> authorized
+ authorizableLookup.getAuthorizableByResource("/buckets/b2") >> denied
+ authorizableLookup.getAuthorizableByResource("/policies") >> authorized
+ authorizableLookup.getAuthorizableByResource("/proxy") >> denied
+ authorizableLookup.getAuthorizableByResource("/swagger") >> denied
+ authorizableLookup.getAuthorizableByResource("/tenants") >> authorized
+
+
+ when:
+ def resources = authorizationService.getAuthorizedResources(RequestAction.READ)
+
+ then:
+ resources != null
+ resources.size() == 4
+ def sortedResources = resources.sort{it.identifier}
+ sortedResources[0].identifier == "/buckets"
+ sortedResources[1].identifier == "/buckets/b1"
+ sortedResources[2].identifier == "/policies"
+ sortedResources[3].identifier == "/tenants"
+
+
+ when:
+ def filteredResources = authorizationService.getAuthorizedResources(RequestAction.READ, ResourceType.Bucket)
+
+ then:
+ filteredResources != null
+ filteredResources.size() == 2
+ def sortedFilteredResources = filteredResources.sort{it.identifier}
+ sortedFilteredResources[0].identifier == "/buckets"
+ sortedFilteredResources[1].identifier == "/buckets/b1"
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/DatabaseBaseTest.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/DatabaseBaseTest.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/DatabaseBaseTest.java
new file mode 100644
index 0000000..02b04c0
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/DatabaseBaseTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.db;
+
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
+import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = DatabaseTestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
+@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
+public abstract class DatabaseBaseTest {
+
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/DatabaseTestApplication.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/DatabaseTestApplication.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/DatabaseTestApplication.java
new file mode 100644
index 0000000..0ce3812
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/DatabaseTestApplication.java
@@ -0,0 +1,52 @@
+/*
+ * 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.db;
+
+import org.apache.nifi.registry.properties.NiFiRegistryProperties;
+import org.mockito.Mockito;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.FilterType;
+
+/**
+ * Sets up the application context for database repository tests.
+ *
+ * The @SpringBootTest annotation on the repository tests will find this class by working up the package hierarchy.
+ * This class must be in the "db" package in order to find the entities in "db.entity" and repositories in "db.repository".
+ *
+ * The DataSourceFactory is excluded so that Spring Boot will load an in-memory H2 database.
+ */
+@SpringBootApplication
+@ComponentScan(
+ excludeFilters = {
+ @ComponentScan.Filter(
+ type = FilterType.ASSIGNABLE_TYPE,
+ value = DataSourceFactory.class)
+ })
+public class DatabaseTestApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(DatabaseTestApplication.class, args);
+ }
+
+ @Bean
+ public NiFiRegistryProperties createNiFiRegistryProperties() {
+ return Mockito.mock(NiFiRegistryProperties.class);
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/TestDatabaseKeyService.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/TestDatabaseKeyService.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/TestDatabaseKeyService.java
new file mode 100644
index 0000000..d0ab56a
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/TestDatabaseKeyService.java
@@ -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.registry.db;
+
+import org.apache.nifi.registry.security.key.Key;
+import org.apache.nifi.registry.security.key.KeyService;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class TestDatabaseKeyService extends DatabaseBaseTest {
+
+ @Autowired
+ private KeyService keyService;
+
+ @Test
+ public void testGetKeyByIdWhenExists() {
+ final Key existingKey = keyService.getKey("1");
+ assertNotNull(existingKey);
+ assertEquals("1", existingKey.getId());
+ assertEquals("unit_test_tenant_identity", existingKey.getIdentity());
+ assertEquals("0123456789abcdef", existingKey.getKey());
+ }
+
+ @Test
+ public void testGetKeyByIdWhenDoesNotExist() {
+ final Key existingKey = keyService.getKey("2");
+ assertNull(existingKey);
+ }
+
+ @Test
+ public void testGetOrCreateKeyWhenExists() {
+ final Key existingKey = keyService.getOrCreateKey("unit_test_tenant_identity");
+ assertNotNull(existingKey);
+ assertEquals("1", existingKey.getId());
+ assertEquals("unit_test_tenant_identity", existingKey.getIdentity());
+ assertEquals("0123456789abcdef", existingKey.getKey());
+ }
+
+ @Test
+ public void testGetOrCreateKeyWhenDoesNotExist() {
+ final Key createdKey = keyService.getOrCreateKey("does-not-exist");
+ assertNotNull(createdKey);
+ assertNotNull(createdKey.getId());
+ assertEquals("does-not-exist", createdKey.getIdentity());
+ assertNotNull(createdKey.getKey());
+ }
+
+ @Test
+ public void testDeleteKeyWhenExists() {
+ final Key existingKey = keyService.getKey("1");
+ assertNotNull(existingKey);
+
+ keyService.deleteKey(existingKey.getIdentity());
+
+ final Key deletedKey = keyService.getKey("1");
+ assertNull(deletedKey);
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/TestDatabaseMetadataService.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/TestDatabaseMetadataService.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/TestDatabaseMetadataService.java
new file mode 100644
index 0000000..35ba757
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/TestDatabaseMetadataService.java
@@ -0,0 +1,386 @@
+/*
+ * 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.db;
+
+import org.apache.nifi.registry.db.entity.BucketEntity;
+import org.apache.nifi.registry.db.entity.BucketItemEntity;
+import org.apache.nifi.registry.db.entity.BucketItemEntityType;
+import org.apache.nifi.registry.db.entity.FlowEntity;
+import org.apache.nifi.registry.db.entity.FlowSnapshotEntity;
+import org.apache.nifi.registry.service.MetadataService;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.UUID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class TestDatabaseMetadataService extends DatabaseBaseTest {
+
+ @Autowired
+ private MetadataService metadataService;
+
+ //----------------- Buckets ---------------------------------
+
+ @Test
+ public void testCreateAndGetBucket() {
+ final BucketEntity b = new BucketEntity();
+ b.setId("testBucketId");
+ b.setName("testBucketName");
+ b.setDescription("testBucketDesc");
+ b.setCreated(new Date());
+
+ metadataService.createBucket(b);
+
+ final BucketEntity createdBucket = metadataService.getBucketById(b.getId());
+ assertNotNull(createdBucket);
+ assertEquals(b.getId(), createdBucket.getId());
+ assertEquals(b.getName(), createdBucket.getName());
+ assertEquals(b.getDescription(), createdBucket.getDescription());
+ assertEquals(b.getCreated(), createdBucket.getCreated());
+ }
+
+ @Test
+ public void testGetBucketDoesNotExist() {
+ final BucketEntity bucket = metadataService.getBucketById("does-not-exist");
+ assertNull(bucket);
+ }
+
+ @Test
+ public void testGetBucketsByName() {
+ final List<BucketEntity> buckets = metadataService.getBucketsByName("Bucket 1");
+ assertNotNull(buckets);
+ assertEquals(1, buckets.size());
+ assertEquals("Bucket 1", buckets.get(0).getName());
+ }
+
+ @Test
+ public void testGetBucketsByNameNoneFound() {
+ final List<BucketEntity> buckets = metadataService.getBucketsByName("Bucket XYZ");
+ assertNotNull(buckets);
+ assertEquals(0, buckets.size());
+ }
+
+ @Test
+ public void testUpdateBucket() {
+ final BucketEntity bucket = metadataService.getBucketById("1");
+ assertNotNull(bucket);
+
+ final String updatedName = bucket.getName() + " UPDATED";
+ final String updatedDesc = bucket.getDescription() + "DESC";
+
+ bucket.setName(updatedName);
+ bucket.setDescription(updatedDesc);
+
+ metadataService.updateBucket(bucket);
+
+ final BucketEntity updatedBucket = metadataService.getBucketById(bucket.getId());
+ assertNotNull(updatedName);
+ assertEquals(updatedName, updatedBucket.getName());
+ assertEquals(updatedDesc, updatedBucket.getDescription());
+ }
+
+ @Test
+ public void testDeleteBucketNoChildren() {
+ final BucketEntity bucket = metadataService.getBucketById("6");
+ assertNotNull(bucket);
+
+ metadataService.deleteBucket(bucket);
+
+ final BucketEntity deletedBucket = metadataService.getBucketById("6");
+ assertNull(deletedBucket);
+ }
+
+ @Test
+ public void testDeleteBucketWithChildren() {
+ final BucketEntity bucket = metadataService.getBucketById("1");
+ assertNotNull(bucket);
+
+ metadataService.deleteBucket(bucket);
+
+ final BucketEntity deletedBucket = metadataService.getBucketById("1");
+ assertNull(deletedBucket);
+ }
+
+ @Test
+ public void testGetBucketsForIds() {
+ final List<BucketEntity> buckets = metadataService.getBuckets(new HashSet<>(Arrays.asList("1", "2")));
+ assertNotNull(buckets);
+ assertEquals(2, buckets.size());
+ assertEquals("1", buckets.get(0).getId());
+ assertEquals("2", buckets.get(1).getId());
+ }
+
+ @Test
+ public void testGetAllBuckets() {
+ final List<BucketEntity> buckets = metadataService.getAllBuckets();
+ assertNotNull(buckets);
+ assertEquals(6, buckets.size());
+ }
+
+ //----------------- BucketItems ---------------------------------
+
+ @Test
+ public void testGetBucketItemsForBucket() {
+ final BucketEntity bucket = metadataService.getBucketById("1");
+ assertNotNull(bucket);
+
+ final List<BucketItemEntity> items = metadataService.getBucketItems(bucket.getId());
+ assertNotNull(items);
+ assertEquals(2, items.size());
+
+ items.stream().forEach(i -> assertNotNull(i.getBucketName()));
+ }
+
+ @Test
+ public void testGetBucketItemsForBuckets() {
+ final List<BucketItemEntity> items = metadataService.getBucketItems(new HashSet<>(Arrays.asList("1", "2")));
+ assertNotNull(items);
+ assertEquals(3, items.size());
+
+ items.stream().forEach(i -> assertNotNull(i.getBucketName()));
+ }
+
+ @Test
+ public void testGetItemsWithCounts() {
+ final List<BucketItemEntity> items = metadataService.getBucketItems(new HashSet<>(Arrays.asList("1", "2")));
+ assertNotNull(items);
+
+ // 3 items across all buckets
+ assertEquals(3, items.size());
+
+ final BucketItemEntity item1 = items.stream().filter(i -> i.getId().equals("1")).findFirst().orElse(null);
+ assertNotNull(item1);
+ assertEquals(BucketItemEntityType.FLOW, item1.getType());
+
+ final FlowEntity flowEntity = (FlowEntity) item1;
+ assertEquals(3, flowEntity.getSnapshotCount());
+
+ items.stream().forEach(i -> assertNotNull(i.getBucketName()));
+ }
+
+ @Test
+ public void testGetItemsWithCountsFilteredByBuckets() {
+ final List<BucketItemEntity> items = metadataService.getBucketItems(Collections.singleton("1"));
+ assertNotNull(items);
+
+ // only 2 items in bucket 1
+ assertEquals(2, items.size());
+
+ final BucketItemEntity item1 = items.stream().filter(i -> i.getId().equals("1")).findFirst().orElse(null);
+ assertNotNull(item1);
+ assertEquals(BucketItemEntityType.FLOW, item1.getType());
+
+ final FlowEntity flowEntity = (FlowEntity) item1;
+ assertEquals(3, flowEntity.getSnapshotCount());
+
+ items.stream().forEach(i -> assertNotNull(i.getBucketName()));
+ }
+
+ //----------------- Flows ---------------------------------
+
+ @Test
+ public void testGetFlowByIdWhenExists() {
+ final FlowEntity flow = metadataService.getFlowById("1");
+ assertNotNull(flow);
+ assertEquals("1", flow.getId());
+ assertEquals("1", flow.getBucketId());
+ }
+
+ @Test
+ public void testGetFlowByIdWhenDoesNotExist() {
+ final FlowEntity flow = metadataService.getFlowById("does-not-exist");
+ assertNull(flow);
+ }
+
+ @Test
+ public void testCreateFlow() {
+ final String bucketId = "1";
+
+ final FlowEntity flow = new FlowEntity();
+ flow.setId(UUID.randomUUID().toString());
+ flow.setBucketId(bucketId);
+ flow.setName("Test Flow 1");
+ flow.setDescription("Description for Test Flow 1");
+ flow.setCreated(new Date());
+ flow.setModified(new Date());
+ flow.setType(BucketItemEntityType.FLOW);
+
+ metadataService.createFlow(flow);
+
+ final FlowEntity createdFlow = metadataService.getFlowById(flow.getId());
+ assertNotNull(flow);
+ assertEquals(flow.getId(), createdFlow.getId());
+ assertEquals(flow.getBucketId(), createdFlow.getBucketId());
+ assertEquals(flow.getName(), createdFlow.getName());
+ assertEquals(flow.getDescription(), createdFlow.getDescription());
+ assertEquals(flow.getCreated(), createdFlow.getCreated());
+ assertEquals(flow.getModified(), createdFlow.getModified());
+ assertEquals(flow.getType(), createdFlow.getType());
+ }
+
+ @Test
+ public void testGetFlowByIdWithSnapshotCount() {
+ final FlowEntity flowEntity = metadataService.getFlowByIdWithSnapshotCounts("1");
+ assertNotNull(flowEntity);
+ assertEquals(3, flowEntity.getSnapshotCount());
+ }
+
+ @Test
+ public void testGetFlowsByBucket() {
+ final BucketEntity bucketEntity = metadataService.getBucketById("1");
+ final List<FlowEntity> flows = metadataService.getFlowsByBucket(bucketEntity.getId());
+ assertEquals(2, flows.size());
+
+ final FlowEntity flowEntity = flows.stream().filter(f -> f.getId().equals("1")).findFirst().orElse(null);
+ assertNotNull(flowEntity);
+ assertEquals(3, flowEntity.getSnapshotCount());
+ }
+
+ @Test
+ public void testGetFlowsByName() {
+ final List<FlowEntity> flows = metadataService.getFlowsByName("Flow 1");
+ assertNotNull(flows);
+ assertEquals(2, flows.size());
+ assertEquals("Flow 1", flows.get(0).getName());
+ assertEquals("Flow 1", flows.get(1).getName());
+ }
+
+ @Test
+ public void testGetFlowsByNameByBucket() {
+ final List<FlowEntity> flows = metadataService.getFlowsByName("2","Flow 1");
+ assertNotNull(flows);
+ assertEquals(1, flows.size());
+ assertEquals("Flow 1", flows.get(0).getName());
+ assertEquals("2", flows.get(0).getBucketId());
+ }
+
+ @Test
+ public void testUpdateFlow() {
+ final FlowEntity flow = metadataService.getFlowById("1");
+ assertNotNull(flow);
+
+ final Date originalModified = flow.getModified();
+
+ flow.setName(flow.getName() + " UPDATED");
+ flow.setDescription(flow.getDescription() + " UPDATED");
+
+ metadataService.updateFlow(flow);
+
+ final FlowEntity updatedFlow = metadataService.getFlowById( "1");
+ assertNotNull(flow);
+ assertEquals(flow.getName(), updatedFlow.getName());
+ assertEquals(flow.getDescription(), updatedFlow.getDescription());
+ assertEquals(flow.getModified().getTime(), updatedFlow.getModified().getTime());
+ assertTrue(updatedFlow.getModified().getTime() > originalModified.getTime());
+ }
+
+ @Test
+ public void testDeleteFlowWithSnapshots() {
+ final FlowEntity flow = metadataService.getFlowById( "1");
+ assertNotNull(flow);
+
+ metadataService.deleteFlow(flow);
+
+ final FlowEntity deletedFlow = metadataService.getFlowById("1");
+ assertNull(deletedFlow);
+ }
+
+ //----------------- FlowSnapshots ---------------------------------
+
+ @Test
+ public void testGetFlowSnapshot() {
+ final FlowSnapshotEntity entity = metadataService.getFlowSnapshot( "1", 1);
+ assertNotNull(entity);
+ assertEquals("1", entity.getFlowId());
+ assertEquals(1, entity.getVersion().intValue());
+ }
+
+ @Test
+ public void testGetFlowSnapshotDoesNotExist() {
+ final FlowSnapshotEntity entity = metadataService.getFlowSnapshot( "DOES-NOT-EXIST", 1);
+ assertNull(entity);
+ }
+
+ @Test
+ public void testCreateFlowSnapshot() {
+ final FlowSnapshotEntity flowSnapshot = new FlowSnapshotEntity();
+ flowSnapshot.setFlowId("1");
+ flowSnapshot.setVersion(4);
+ flowSnapshot.setCreated(new Date());
+ flowSnapshot.setCreatedBy("test-user");
+ flowSnapshot.setComments("Comments");
+
+ metadataService.createFlowSnapshot(flowSnapshot);
+
+ final FlowSnapshotEntity createdFlowSnapshot = metadataService.getFlowSnapshot(flowSnapshot.getFlowId(), flowSnapshot.getVersion());
+ assertNotNull(createdFlowSnapshot);
+ assertEquals(flowSnapshot.getFlowId(), createdFlowSnapshot.getFlowId());
+ assertEquals(flowSnapshot.getVersion(), createdFlowSnapshot.getVersion());
+ assertEquals(flowSnapshot.getComments(), createdFlowSnapshot.getComments());
+ assertEquals(flowSnapshot.getCreated(), createdFlowSnapshot.getCreated());
+ assertEquals(flowSnapshot.getCreatedBy(), createdFlowSnapshot.getCreatedBy());
+ }
+
+ @Test
+ public void testGetLatestSnapshot() {
+ final FlowSnapshotEntity latest = metadataService.getLatestSnapshot("1");
+ assertNotNull(latest);
+ assertEquals("1", latest.getFlowId());
+ assertEquals(3, latest.getVersion().intValue());
+ }
+
+ @Test
+ public void testGetLatestSnapshotDoesNotExist() {
+ final FlowSnapshotEntity latest = metadataService.getLatestSnapshot("DOES-NOT-EXIST");
+ assertNull(latest);
+ }
+
+ @Test
+ public void testGetFlowSnapshots() {
+ final List<FlowSnapshotEntity> flowSnapshots = metadataService.getSnapshots( "1");
+ assertNotNull(flowSnapshots);
+ assertEquals(3, flowSnapshots.size());
+ }
+
+ @Test
+ public void testGetFlowSnapshotsNoneFound() {
+ final List<FlowSnapshotEntity> flowSnapshots = metadataService.getSnapshots( "2");
+ assertNotNull(flowSnapshots);
+ assertEquals(0, flowSnapshots.size());
+ }
+
+ @Test
+ public void testDeleteFlowSnapshot() {
+ final FlowSnapshotEntity entity = metadataService.getFlowSnapshot( "1", 1);
+ assertNotNull(entity);
+
+ metadataService.deleteFlowSnapshot(entity);
+
+ final FlowSnapshotEntity deletedEntity = metadataService.getFlowSnapshot( "1", 1);
+ assertNull(deletedEntity);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/migration/TestLegacyDatabaseService.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/migration/TestLegacyDatabaseService.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/migration/TestLegacyDatabaseService.java
new file mode 100644
index 0000000..df37e8e
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/migration/TestLegacyDatabaseService.java
@@ -0,0 +1,141 @@
+/*
+ * 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.db.migration;
+
+import org.apache.nifi.registry.db.entity.BucketItemEntityType;
+import org.flywaydb.core.Flyway;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.boot.jdbc.DataSourceBuilder;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import javax.sql.DataSource;
+import java.util.Date;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test running the legacy Flyway migrations against an in-memory H2 and then using the LegacyDatabaseService to
+ * retrieve data. Purposely not using Spring test annotations here to avoid interfering with the normal DB context/flyway.
+ */
+public class TestLegacyDatabaseService {
+
+ private DataSource dataSource;
+ private JdbcTemplate jdbcTemplate;
+ private Flyway flyway;
+
+ private BucketEntityV1 bucketEntityV1;
+ private FlowEntityV1 flowEntityV1;
+ private FlowSnapshotEntityV1 flowSnapshotEntityV1;
+
+ @Before
+ public void setup() {
+ dataSource = DataSourceBuilder.create()
+ .url("jdbc:h2:mem:legacydb")
+ .driverClassName("org.h2.Driver")
+ .build();
+
+ jdbcTemplate = new JdbcTemplate(dataSource);
+
+ flyway = new Flyway();
+ flyway.setDataSource(dataSource);
+ flyway.setLocations("db/original");
+ flyway.migrate();
+
+ bucketEntityV1 = new BucketEntityV1();
+ bucketEntityV1.setId("1");
+ bucketEntityV1.setName("Bucket1");
+ bucketEntityV1.setDescription("This is bucket 1");
+ bucketEntityV1.setCreated(new Date());
+
+ jdbcTemplate.update("INSERT INTO bucket (ID, NAME, DESCRIPTION, CREATED) VALUES (?, ?, ?, ?)",
+ bucketEntityV1.getId(),
+ bucketEntityV1.getName(),
+ bucketEntityV1.getDescription(),
+ bucketEntityV1.getCreated());
+
+ flowEntityV1 = new FlowEntityV1();
+ flowEntityV1.setId("1");
+ flowEntityV1.setBucketId(bucketEntityV1.getId());
+ flowEntityV1.setName("Flow1");
+ flowEntityV1.setDescription("This is flow1");
+ flowEntityV1.setCreated(new Date());
+ flowEntityV1.setModified(new Date());
+
+ jdbcTemplate.update("INSERT INTO bucket_item (ID, NAME, DESCRIPTION, CREATED, MODIFIED, ITEM_TYPE, BUCKET_ID) VALUES (?, ?, ?, ?, ?, ?, ?)",
+ flowEntityV1.getId(),
+ flowEntityV1.getName(),
+ flowEntityV1.getDescription(),
+ flowEntityV1.getCreated(),
+ flowEntityV1.getModified(),
+ BucketItemEntityType.FLOW.toString(),
+ flowEntityV1.getBucketId());
+
+ jdbcTemplate.update("INSERT INTO flow (ID) VALUES (?)", flowEntityV1.getId());
+
+ flowSnapshotEntityV1 = new FlowSnapshotEntityV1();
+ flowSnapshotEntityV1.setFlowId(flowEntityV1.getId());
+ flowSnapshotEntityV1.setVersion(1);
+ flowSnapshotEntityV1.setComments("This is v1");
+ flowSnapshotEntityV1.setCreated(new Date());
+ flowSnapshotEntityV1.setCreatedBy("user1");
+
+ jdbcTemplate.update("INSERT INTO flow_snapshot (FLOW_ID, VERSION, CREATED, CREATED_BY, COMMENTS) VALUES (?, ?, ?, ?, ?)",
+ flowSnapshotEntityV1.getFlowId(),
+ flowSnapshotEntityV1.getVersion(),
+ flowSnapshotEntityV1.getCreated(),
+ flowSnapshotEntityV1.getCreatedBy(),
+ flowSnapshotEntityV1.getComments());
+ }
+
+ @Test
+ public void testGetLegacyData() {
+ final LegacyDatabaseService service = new LegacyDatabaseService(dataSource);
+
+ final List<BucketEntityV1> buckets = service.getAllBuckets();
+ assertEquals(1, buckets.size());
+
+ final BucketEntityV1 b = buckets.stream().findFirst().get();
+ assertEquals(bucketEntityV1.getId(), b.getId());
+ assertEquals(bucketEntityV1.getName(), b.getName());
+ assertEquals(bucketEntityV1.getDescription(), b.getDescription());
+ assertEquals(bucketEntityV1.getCreated(), b.getCreated());
+
+ final List<FlowEntityV1> flows = service.getAllFlows();
+ assertEquals(1, flows.size());
+
+ final FlowEntityV1 f = flows.stream().findFirst().get();
+ assertEquals(flowEntityV1.getId(), f.getId());
+ assertEquals(flowEntityV1.getName(), f.getName());
+ assertEquals(flowEntityV1.getDescription(), f.getDescription());
+ assertEquals(flowEntityV1.getCreated(), f.getCreated());
+ assertEquals(flowEntityV1.getModified(), f.getModified());
+ assertEquals(flowEntityV1.getBucketId(), f.getBucketId());
+
+ final List<FlowSnapshotEntityV1> flowSnapshots = service.getAllFlowSnapshots();
+ assertEquals(1, flowSnapshots.size());
+
+ final FlowSnapshotEntityV1 fs = flowSnapshots.stream().findFirst().get();
+ assertEquals(flowSnapshotEntityV1.getFlowId(), fs.getFlowId());
+ assertEquals(flowSnapshotEntityV1.getVersion(), fs.getVersion());
+ assertEquals(flowSnapshotEntityV1.getComments(), fs.getComments());
+ assertEquals(flowSnapshotEntityV1.getCreatedBy(), fs.getCreatedBy());
+ assertEquals(flowSnapshotEntityV1.getCreated(), fs.getCreated());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/migration/TestLegacyEntityMapper.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/migration/TestLegacyEntityMapper.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/migration/TestLegacyEntityMapper.java
new file mode 100644
index 0000000..3de297f
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/migration/TestLegacyEntityMapper.java
@@ -0,0 +1,79 @@
+/*
+ * 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.db.migration;
+
+import org.apache.nifi.registry.db.entity.BucketEntity;
+import org.apache.nifi.registry.db.entity.FlowEntity;
+import org.apache.nifi.registry.db.entity.FlowSnapshotEntity;
+import org.junit.Test;
+
+import java.util.Date;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class TestLegacyEntityMapper {
+
+ @Test
+ public void testMapLegacyEntities() {
+ final BucketEntityV1 bucketEntityV1 = new BucketEntityV1();
+ bucketEntityV1.setId("1");
+ bucketEntityV1.setName("Bucket1");
+ bucketEntityV1.setDescription("This is bucket 1");
+ bucketEntityV1.setCreated(new Date());
+
+ final BucketEntity bucketEntity = LegacyEntityMapper.createBucketEntity(bucketEntityV1);
+ assertNotNull(bucketEntity);
+ assertEquals(bucketEntityV1.getId(), bucketEntity.getId());
+ assertEquals(bucketEntityV1.getName(), bucketEntity.getName());
+ assertEquals(bucketEntityV1.getDescription(), bucketEntity.getDescription());
+ assertEquals(bucketEntityV1.getCreated(), bucketEntity.getCreated());
+
+ final FlowEntityV1 flowEntityV1 = new FlowEntityV1();
+ flowEntityV1.setId("1");
+ flowEntityV1.setBucketId(bucketEntityV1.getId());
+ flowEntityV1.setName("Flow1");
+ flowEntityV1.setDescription("This is flow1");
+ flowEntityV1.setCreated(new Date());
+ flowEntityV1.setModified(new Date());
+
+ final FlowEntity flowEntity = LegacyEntityMapper.createFlowEntity(flowEntityV1);
+ assertNotNull(flowEntity);
+ assertEquals(flowEntityV1.getId(), flowEntity.getId());
+ assertEquals(flowEntityV1.getBucketId(), flowEntity.getBucketId());
+ assertEquals(flowEntityV1.getName(), flowEntity.getName());
+ assertEquals(flowEntityV1.getDescription(), flowEntity.getDescription());
+ assertEquals(flowEntityV1.getCreated(), flowEntity.getCreated());
+ assertEquals(flowEntityV1.getModified(), flowEntity.getModified());
+
+ final FlowSnapshotEntityV1 flowSnapshotEntityV1 = new FlowSnapshotEntityV1();
+ flowSnapshotEntityV1.setFlowId(flowEntityV1.getId());
+ flowSnapshotEntityV1.setVersion(1);
+ flowSnapshotEntityV1.setComments("This is v1");
+ flowSnapshotEntityV1.setCreated(new Date());
+ flowSnapshotEntityV1.setCreatedBy("user1");
+
+ final FlowSnapshotEntity flowSnapshotEntity = LegacyEntityMapper.createFlowSnapshotEntity(flowSnapshotEntityV1);
+ assertNotNull(flowSnapshotEntity);
+ assertEquals(flowSnapshotEntityV1.getFlowId(), flowSnapshotEntity.getFlowId());
+ assertEquals(flowSnapshotEntityV1.getVersion(), flowSnapshotEntity.getVersion());
+ assertEquals(flowSnapshotEntityV1.getComments(), flowSnapshotEntity.getComments());
+ assertEquals(flowSnapshotEntityV1.getCreatedBy(), flowSnapshotEntity.getCreatedBy());
+ assertEquals(flowSnapshotEntityV1.getCreated(), flowSnapshotEntity.getCreated());
+ }
+
+}