From 21514c1b81b57adc6d44426ef1dfcefaa588e192 Mon Sep 17 00:00:00 2001 From: Kevin Doran Date: Thu, 12 Oct 2017 13:54:34 -0400 Subject: [PATCH 1/3] NIFIREG-33 Add LDAP and JWT auth support - Adds LdapIdentityProvider for authentication - Adds /access/token endpoint for generating JWT for users that can authenticate with a configured IdenitiyProvider - Adds JwtAuthenticationProvider for authentication - Adds KeyService for key generation and tracking for signing JWTs - Adds LdapUserGroupProvider for authorization - Adds LDAP integration tests - Refactors nifi-registry-security-api-impl into nifi-registry-framework - Refactors all security related packages, such as o.a.n.r.authorization and o.a.n.r.authentication, under org.apache.nifi.registry.security --- .../client/NiFiRegistryClientConfig.java | 8 +- nifi-registry-framework/pom.xml | 80 +- .../nifi/registry/db/DatabaseKeyService.java | 117 +++ .../nifi/registry/db/entity/KeyEntity.java | 60 ++ .../repository/KeyRepository.java} | 22 +- .../registry/extension/ExtensionManager.java | 8 +- .../AbstractPolicyBasedAuthorizer.java | 27 +- .../authorization/AuthorizableLookup.java | 4 +- .../authorization/AuthorizeAccess.java | 2 +- .../AuthorizerCapabilityDetection.java | 11 +- .../authorization/AuthorizerFactory.java} | 82 +- .../AuthorizerFactoryException.java | 2 +- ...ompositeConfigurableUserGroupProvider.java | 20 +- .../CompositeUserGroupProvider.java | 15 +- .../StandardAuthorizableLookup.java | 14 +- ...tandardAuthorizerConfigurationContext.java | 2 +- ...andardAuthorizerInitializationContext.java | 5 +- .../StandardManagedAuthorizer.java | 24 +- .../authorization/UsersAndAccessPolicies.java | 2 +- .../file/AuthorizationsHolder.java | 18 +- .../file/FileAccessPolicyProvider.java | 40 +- .../authorization/file/FileAuthorizer.java | 36 +- .../file/FileUserGroupProvider.java | 100 +-- .../authorization/file/IdentifierUtil.java | 2 +- .../authorization/file/UserGroupHolder.java | 18 +- .../resource/AccessPolicyAuthorizable.java | 18 +- .../authorization/resource/Authorizable.java | 24 +- ...ePolicyPermissionsThroughBaseResource.java | 2 +- .../resource/ResourceFactory.java | 4 +- .../authorization/resource/ResourceType.java | 2 +- .../authorization/user/NiFiUser.java | 2 +- .../authorization/user/NiFiUserDetails.java | 2 +- .../authorization/user/NiFiUserUtils.java | 2 +- .../authorization/user/StandardNiFiUser.java | 2 +- .../nifi/registry/security/key/Key.java | 69 ++ .../registry/security/key/KeyService.java | 46 ++ .../security/ldap/IdentityStrategy.java | 22 + .../ldap/LdapAuthenticationStrategy.java | 24 + .../security/ldap/LdapIdentityProvider.java | 348 ++++++++ .../security/ldap/LdapsSocketFactory.java | 106 +++ .../security/ldap/ReferralStrategy.java | 35 + .../ldap/tenants/LdapUserGroupProvider.java | 750 ++++++++++++++++++ .../security/ldap/tenants/SearchScope.java | 28 + .../security/ldap/tenants/TenantHolder.java | 165 ++++ .../nifi/registry/security/util/XmlUtils.java | 44 + .../service/AuthorizationService.java | 144 ++-- .../registry/service/DataModelMapper.java | 18 + ...urity.authentication.LoginIdentityProvider | 2 +- ...ecurity.authorization.AccessPolicyProvider | 2 +- ...egistry.security.authorization.Authorizer} | 3 +- ....security.authorization.UserGroupProvider} | 6 +- .../resources/db/migration/V1__Initial.sql | 7 + .../src/main/xsd/authorizations.xsd | 0 .../src/main/xsd/tenants.xsd | 0 .../registry/db/DatabaseKeyServiceSpec.groovy | 85 ++ .../service/AuthorizationServiceSpec.groovy | 22 +- .../db/repository/TestKeyRepository.java | 106 +++ .../db/migration/V999999.1__test-setup.sql | 8 +- .../properties/NiFiRegistryProperties.java | 12 + .../src/main/resources/conf/authorizers.xml | 106 ++- nifi-registry-security-api-impl/pom.xml | 100 --- ...i.registry.authorization.UserGroupProvider | 15 - .../AuthenticationResponse.java | 65 ++ .../authentication/LoginCredentials.java | 39 + .../authentication/LoginIdentityProvider.java | 61 ++ ...nIdentityProviderConfigurationContext.java | 48 ++ ...IdentityProviderInitializationContext.java | 27 + .../LoginIdentityProviderLookup.java | 23 + .../LoginIdentityProviderContext.java | 35 + .../exception/IdentityAccessException.java | 33 + .../InvalidLoginCredentialsException.java | 33 + .../exception/ProviderCreationException.java | 39 + .../ProviderDestructionException.java | 39 + .../authorization/AccessPolicy.java | 2 +- .../authorization/AccessPolicyProvider.java | 8 +- ...ssPolicyProviderInitializationContext.java | 2 +- .../AccessPolicyProviderLookup.java | 2 +- .../authorization/AuthorizationAuditor.java | 2 +- .../authorization/AuthorizationRequest.java | 2 +- .../authorization/AuthorizationResult.java | 2 +- .../authorization/Authorizer.java | 8 +- .../AuthorizerConfigurationContext.java | 2 +- .../AuthorizerInitializationContext.java | 2 +- .../authorization/AuthorizerLookup.java | 2 +- .../ConfigurableAccessPolicyProvider.java | 6 +- .../ConfigurableUserGroupProvider.java | 6 +- .../{ => security}/authorization/Group.java | 2 +- .../authorization/ManagedAuthorizer.java | 6 +- .../authorization/RequestAction.java | 2 +- .../authorization/Resource.java | 2 +- .../{ => security}/authorization/User.java | 2 +- .../authorization/UserAndGroups.java | 2 +- .../authorization/UserContextKeys.java | 2 +- .../authorization/UserGroupProvider.java | 8 +- ...serGroupProviderInitializationContext.java | 2 +- .../UserGroupProviderLookup.java | 2 +- .../annotation/AuthorizerContext.java | 2 +- .../exception/AccessDeniedException.java | 2 +- .../AuthorizationAccessException.java | 2 +- .../AuthorizerCreationException.java | 2 +- .../AuthorizerDestructionException.java | 2 +- .../UninheritableAuthorizationsException.java | 2 +- .../security/util/CertificateUtils.java | 2 + .../security/util/SslContextFactory.java | 249 ++++++ nifi-registry-web-api/pom.xml | 38 +- .../web/api/AccessPolicyResource.java | 29 +- .../nifi/registry/web/api/AccessResource.java | 207 +++-- .../api/AuthorizableApplicationResource.java | 10 +- .../registry/web/api/BucketFlowResource.java | 4 +- .../nifi/registry/web/api/BucketResource.java | 8 +- .../nifi/registry/web/api/FlowResource.java | 2 +- .../registry/web/api/HttpStatusMessages.java | 1 + .../nifi/registry/web/api/ItemResource.java | 4 +- .../registry/web/api/ResourceResource.java | 8 +- .../nifi/registry/web/api/TenantResource.java | 15 +- .../mapper/AccessDeniedExceptionMapper.java | 6 +- .../AuthorizationAccessExceptionMapper.java | 2 +- .../InvalidAuthenticationExceptionMapper.java | 2 +- .../web/mapper/NotAllowedExceptionMapper.java | 47 ++ .../NiFiRegistrySecurityConfig.java | 131 +-- .../LoginIdentityProviderFactory.java | 256 ++++++ .../NiFiAnonymousUserFilter.java | 8 +- .../NiFiAuthenticationFilter.java | 6 +- .../NiFiAuthenticationProvider.java | 12 +- .../NiFiAuthenticationRequestToken.java | 2 +- .../ProxiedEntitiesUtils.java | 6 +- ...nIdentityProviderConfigurationContext.java | 52 ++ ...IdentityProviderInitializationContext.java | 45 ++ .../InvalidAuthenticationException.java | 2 +- .../exception}/UntrustedProxyException.java | 2 +- .../jwt/JwtAuthenticationFilter.java | 58 ++ .../jwt/JwtAuthenticationProvider.java | 69 ++ .../jwt/JwtAuthenticationRequestToken.java} | 34 +- .../authentication/jwt/JwtService.java | 160 ++++ .../token/LoginAuthenticationToken.java | 2 +- .../token/NiFiAuthenticationToken.java | 2 +- .../x509/SubjectDnX509PrincipalExtractor.java | 4 +- .../x509/X509AuthenticationFilter.java | 6 +- .../x509/X509AuthenticationProvider.java | 39 +- .../x509/X509AuthenticationRequestToken.java | 4 +- .../x509/X509CertificateExtractor.java | 4 +- .../x509/X509CertificateValidator.java | 4 +- .../x509/X509IdentityProvider.java | 11 +- .../src/main/xsd/identity-providers.xsd | 50 ++ ...va => NiFiRegistryTestApiApplication.java} | 14 +- .../SecureLdapTestApiApplication.java | 56 ++ .../nifi/registry/web/api/SecureFileIT.java | 4 +- .../nifi/registry/web/api/SecureLdapIT.java | 230 ++++++ .../registry/web/api/UnsecuredITBase.java | 4 +- .../application-ITSecureLdap.properties | 48 ++ .../conf/secure-file/authorizers.xml | 6 +- .../conf/secure-ldap/authorizers.xml | 243 ++++++ .../conf/secure-ldap/identity-providers.xml | 90 +++ .../nifi-registry-client.properties | 25 + .../conf/secure-ldap/nifi-registry.properties | 35 + .../conf/secure-ldap/test-ldap-data.ldif | 261 ++++++ pom.xml | 2 +- 157 files changed, 5426 insertions(+), 775 deletions(-) create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/DatabaseKeyService.java create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/entity/KeyEntity.java rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{authorization/AuthorizerFactory.java => db/repository/KeyRepository.java} (66%) rename {nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry => nifi-registry-framework/src/main/java/org/apache/nifi/registry/security}/authorization/AbstractPolicyBasedAuthorizer.java (95%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AuthorizableLookup.java (94%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AuthorizeAccess.java (93%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AuthorizerCapabilityDetection.java (84%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{authorization/StandardAuthorizerFactory.java => security/authorization/AuthorizerFactory.java} (91%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AuthorizerFactoryException.java (95%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/CompositeConfigurableUserGroupProvider.java (86%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/CompositeUserGroupProvider.java (86%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/StandardAuthorizableLookup.java (93%) rename {nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry => nifi-registry-framework/src/main/java/org/apache/nifi/registry/security}/authorization/StandardAuthorizerConfigurationContext.java (96%) rename {nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry => nifi-registry-framework/src/main/java/org/apache/nifi/registry/security}/authorization/StandardAuthorizerInitializationContext.java (97%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/StandardManagedAuthorizer.java (88%) rename {nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry => nifi-registry-framework/src/main/java/org/apache/nifi/registry/security}/authorization/UsersAndAccessPolicies.java (97%) rename {nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry => nifi-registry-framework/src/main/java/org/apache/nifi/registry/security}/authorization/file/AuthorizationsHolder.java (89%) rename {nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry => nifi-registry-framework/src/main/java/org/apache/nifi/registry/security}/authorization/file/FileAccessPolicyProvider.java (95%) rename {nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry => nifi-registry-framework/src/main/java/org/apache/nifi/registry/security}/authorization/file/FileAuthorizer.java (89%) rename {nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry => nifi-registry-framework/src/main/java/org/apache/nifi/registry/security}/authorization/file/FileUserGroupProvider.java (83%) rename {nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry => nifi-registry-framework/src/main/java/org/apache/nifi/registry/security}/authorization/file/IdentifierUtil.java (95%) rename {nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry => nifi-registry-framework/src/main/java/org/apache/nifi/registry/security}/authorization/file/UserGroupHolder.java (90%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/resource/AccessPolicyAuthorizable.java (88%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/resource/Authorizable.java (93%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/resource/EnforcePolicyPermissionsThroughBaseResource.java (95%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/resource/ResourceFactory.java (98%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/resource/ResourceType.java (95%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/user/NiFiUser.java (96%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/user/NiFiUserDetails.java (97%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/user/NiFiUserUtils.java (98%) rename nifi-registry-framework/src/main/java/org/apache/nifi/registry/{ => security}/authorization/user/StandardNiFiUser.java (98%) create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/key/Key.java create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/key/KeyService.java create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/IdentityStrategy.java create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/LdapAuthenticationStrategy.java create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/LdapIdentityProvider.java create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/LdapsSocketFactory.java create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/ReferralStrategy.java create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProvider.java create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/SearchScope.java create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/TenantHolder.java create mode 100644 nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/util/XmlUtils.java rename nifi-registry-security-api-impl/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.Authorizer => nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authentication.LoginIdentityProvider (92%) rename nifi-registry-security-api-impl/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.AccessPolicyProvider => nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.AccessPolicyProvider (91%) rename nifi-registry-framework/src/main/resources/META-INF/services/{org.apache.nifi.registry.authorization.Authorizer => org.apache.nifi.registry.security.authorization.Authorizer} (84%) rename nifi-registry-framework/src/main/resources/META-INF/services/{org.apache.nifi.registry.authorization.UserGroupProvider => org.apache.nifi.registry.security.authorization.UserGroupProvider} (71%) rename {nifi-registry-security-api-impl => nifi-registry-framework}/src/main/xsd/authorizations.xsd (100%) rename {nifi-registry-security-api-impl => nifi-registry-framework}/src/main/xsd/tenants.xsd (100%) create mode 100644 nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/db/DatabaseKeyServiceSpec.groovy create mode 100644 nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/repository/TestKeyRepository.java delete mode 100644 nifi-registry-security-api-impl/pom.xml delete mode 100644 nifi-registry-security-api-impl/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.UserGroupProvider create mode 100644 nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/AuthenticationResponse.java create mode 100644 nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginCredentials.java create mode 100644 nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProvider.java create mode 100644 nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProviderConfigurationContext.java create mode 100644 nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProviderInitializationContext.java create mode 100644 nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProviderLookup.java create mode 100644 nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/annotation/LoginIdentityProviderContext.java create mode 100644 nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/IdentityAccessException.java create mode 100644 nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/InvalidLoginCredentialsException.java create mode 100644 nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/ProviderCreationException.java create mode 100644 nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/ProviderDestructionException.java rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AccessPolicy.java (99%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AccessPolicyProvider.java (91%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AccessPolicyProviderInitializationContext.java (95%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AccessPolicyProviderLookup.java (95%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AuthorizationAuditor.java (95%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AuthorizationRequest.java (99%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AuthorizationResult.java (98%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/Authorizer.java (87%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AuthorizerConfigurationContext.java (96%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AuthorizerInitializationContext.java (95%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/AuthorizerLookup.java (95%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/ConfigurableAccessPolicyProvider.java (95%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/ConfigurableUserGroupProvider.java (96%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/Group.java (99%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/ManagedAuthorizer.java (91%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/RequestAction.java (97%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/Resource.java (95%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/User.java (99%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/UserAndGroups.java (95%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/UserContextKeys.java (94%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/UserGroupProvider.java (92%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/UserGroupProviderInitializationContext.java (95%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/UserGroupProviderLookup.java (95%) rename {nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry => nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security}/authorization/annotation/AuthorizerContext.java (94%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/exception/AccessDeniedException.java (95%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/exception/AuthorizationAccessException.java (94%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/exception/AuthorizerCreationException.java (95%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/exception/AuthorizerDestructionException.java (95%) rename nifi-registry-security-api/src/main/java/org/apache/nifi/registry/{ => security}/authorization/exception/UninheritableAuthorizationsException.java (94%) create mode 100644 nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/SslContextFactory.java create mode 100644 nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotAllowedExceptionMapper.java rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/{ => security}/NiFiRegistrySecurityConfig.java (57%) create mode 100644 nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/LoginIdentityProviderFactory.java rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/NiFiAnonymousUserFilter.java (82%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/NiFiAuthenticationFilter.java (95%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/NiFiAuthenticationProvider.java (88%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/NiFiAuthenticationRequestToken.java (95%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/ProxiedEntitiesUtils.java (96%) create mode 100644 nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/StandardLoginIdentityProviderConfigurationContext.java create mode 100644 nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/StandardLoginIdentityProviderInitializationContext.java rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication/exception}/InvalidAuthenticationException.java (94%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication/exception}/UntrustedProxyException.java (93%) create mode 100644 nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtAuthenticationFilter.java create mode 100644 nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtAuthenticationProvider.java rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{token/OtpAuthenticationToken.java => authentication/jwt/JwtAuthenticationRequestToken.java} (55%) create mode 100644 nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/token/LoginAuthenticationToken.java (98%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/token/NiFiAuthenticationToken.java (96%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/x509/SubjectDnX509PrincipalExtractor.java (90%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/x509/X509AuthenticationFilter.java (91%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/x509/X509AuthenticationProvider.java (82%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/x509/X509AuthenticationRequestToken.java (94%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/x509/X509CertificateExtractor.java (93%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/x509/X509CertificateValidator.java (93%) rename nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/{ => authentication}/x509/X509IdentityProvider.java (89%) create mode 100644 nifi-registry-web-api/src/main/xsd/identity-providers.xsd rename nifi-registry-web-api/src/test/java/org/apache/nifi/registry/{NiFiRegistryApiTestApplication.java => NiFiRegistryTestApiApplication.java} (79%) create mode 100644 nifi-registry-web-api/src/test/java/org/apache/nifi/registry/SecureLdapTestApiApplication.java create mode 100644 nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java create mode 100644 nifi-registry-web-api/src/test/resources/application-ITSecureLdap.properties create mode 100644 nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml create mode 100644 nifi-registry-web-api/src/test/resources/conf/secure-ldap/identity-providers.xml create mode 100644 nifi-registry-web-api/src/test/resources/conf/secure-ldap/nifi-registry-client.properties create mode 100644 nifi-registry-web-api/src/test/resources/conf/secure-ldap/nifi-registry.properties create mode 100644 nifi-registry-web-api/src/test/resources/conf/secure-ldap/test-ldap-data.ldif diff --git a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryClientConfig.java b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryClientConfig.java index 6d5ddb11e..de77b51d5 100644 --- a/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryClientConfig.java +++ b/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/NiFiRegistryClientConfig.java @@ -20,8 +20,10 @@ import org.apache.nifi.registry.security.util.KeystoreType; import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import java.io.File; import java.io.FileInputStream; @@ -111,11 +113,13 @@ public SSLContext getSslContext() { trustManagerFactory = null; } - if (keyManagerFactory != null && trustManagerFactory != null) { + if (keyManagerFactory != null || trustManagerFactory != null) { try { // initialize the ssl context + KeyManager[] keyManagers = keyManagerFactory != null ? keyManagerFactory.getKeyManagers() : null; + TrustManager[] trustManagers = trustManagerFactory != null ? trustManagerFactory.getTrustManagers() : null; final SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom()); + sslContext.init(keyManagers, trustManagers, new SecureRandom()); sslContext.getDefaultSSLParameters().setNeedClientAuth(true); return sslContext; diff --git a/nifi-registry-framework/pom.xml b/nifi-registry-framework/pom.xml index 117918812..95381f56a 100644 --- a/nifi-registry-framework/pom.xml +++ b/nifi-registry-framework/pom.xml @@ -44,7 +44,11 @@ xjc + + src/main/xsd/providers.xsd + org.apache.nifi.registry.provider.generated + false @@ -53,7 +57,36 @@ xjc - org.apache.nifi.registry.authorization.generated + + src/main/xsd/authorizers.xsd + + org.apache.nifi.registry.security.authorization.generated + false + + + + authorizations + + xjc + + + + src/main/xsd/authorizations.xsd + + org.apache.nifi.registry.security.authorization.file.generated + false + + + + tenants + + xjc + + + + src/main/xsd/tenants.xsd + + org.apache.nifi.registry.security.authorization.file.tenants.generated false @@ -63,7 +96,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - **/authorization/generated/*.java,**/provider/generated/*.java, + **/generated/*.java @@ -120,7 +153,7 @@ org.apache.nifi.registry - nifi-registry-security-api-impl + nifi-registry-security-utils 0.0.1-SNAPSHOT @@ -128,6 +161,42 @@ spring-boot-starter-security ${spring.boot.version} + + org.springframework.security + spring-security-ldap + ${spring.security.version} + + + org.springframework.security + spring-security-core + + + org.springframework + spring-beans + + + org.springframework + spring-context + + + org.springframework + spring-core + + + org.springframework + spring-tx + + + commons-logging + commons-logging + + + + + org.bouncycastle + bcprov-jdk15on + 1.55 + commons-io commons-io @@ -199,10 +268,5 @@ 2.2.2 test - - org.apache.nifi.registry - nifi-registry-security-utils - 0.0.1-SNAPSHOT - diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/DatabaseKeyService.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/DatabaseKeyService.java new file mode 100644 index 000000000..eb552518f --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/DatabaseKeyService.java @@ -0,0 +1,117 @@ +/* + * 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.KeyEntity; +import org.apache.nifi.registry.db.repository.KeyRepository; +import org.apache.nifi.registry.security.key.Key; +import org.apache.nifi.registry.security.key.KeyService; +import org.apache.nifi.registry.service.DataModelMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.UUID; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +@Service +public class DatabaseKeyService implements KeyService { + + private static final Logger logger = LoggerFactory.getLogger(DatabaseKeyService.class); + + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + private final Lock readLock = lock.readLock(); + private final Lock writeLock = lock.writeLock(); + + private KeyRepository keyRepository; + + @Autowired + public DatabaseKeyService(KeyRepository keyRepository) { + this.keyRepository = keyRepository; + } + + @Override + public Key getKey(String id) { + if (id == null) { + throw new IllegalArgumentException("Id cannot be null"); + } + + Key key = null; + readLock.lock(); + try { + KeyEntity keyEntity = keyRepository.findOne(id); + if (keyEntity != null) { + key = DataModelMapper.map(keyEntity); + } else { + logger.debug("No signing key found with id='" + id + "'"); + } + } finally { + readLock.unlock(); + } + return key; + } + + @Override + public Key getOrCreateKey(String identity) { + if (identity == null) { + throw new IllegalArgumentException("Identity cannot be null"); + } + + Key key; + writeLock.lock(); + try { + final KeyEntity existingKeyEntity = keyRepository.findOneByTenantIdentity(identity); + if (existingKeyEntity == null) { + logger.debug("No key found with identity='" + identity + "'. Creating new key."); + + final KeyEntity newKeyEntity = new KeyEntity(); + newKeyEntity.setId(UUID.randomUUID().toString()); + newKeyEntity.setTenantIdentity(identity); + newKeyEntity.setKeyValue(UUID.randomUUID().toString()); + + final KeyEntity savedKeyEntity = keyRepository.save(newKeyEntity); + + key = DataModelMapper.map(savedKeyEntity); + } else { + key = DataModelMapper.map(existingKeyEntity); + } + } finally { + writeLock.unlock(); + } + return key; + } + + @Override + public void deleteKey(String identity) { + if (identity == null) { + throw new IllegalArgumentException("Identity cannot be null"); + } + + Key key; + writeLock.lock(); + try { + logger.debug("Deleting key with identity='" + identity + "'."); + keyRepository.deleteByTenantIdentity(identity); + } finally { + writeLock.unlock(); + } + + } + +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/entity/KeyEntity.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/entity/KeyEntity.java new file mode 100644 index 000000000..e3f7e3a71 --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/entity/KeyEntity.java @@ -0,0 +1,60 @@ +/* + * 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.entity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "SIGNING_KEY") +public class KeyEntity { + + @Id + private String id; + + @Column(unique=true) + private String tenantIdentity; + + private String keyValue; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTenantIdentity() { + return tenantIdentity; + } + + public void setTenantIdentity(String tenantIdentity) { + this.tenantIdentity = tenantIdentity; + } + + public String getKeyValue() { + return keyValue; + } + + public void setKeyValue(String keyValue) { + this.keyValue = keyValue; + } + +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactory.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/repository/KeyRepository.java similarity index 66% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactory.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/repository/KeyRepository.java index 9cace732c..1f31776b2 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactory.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/repository/KeyRepository.java @@ -14,20 +14,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.db.repository; -public interface AuthorizerFactory { +import org.apache.nifi.registry.db.entity.KeyEntity; +import org.springframework.data.repository.CrudRepository; - /** - * Initialize the factory. - * - * @throws AuthorizerFactoryException if an error occurs during initialization - */ - void initialize() throws AuthorizerFactoryException; +/** + * Spring Data Repository for KeyEntity. + */ +public interface KeyRepository extends CrudRepository { + + KeyEntity findOneByTenantIdentity(String identity); - /** - * @return the configured Authorizer - */ - Authorizer getAuthorizer() throws AuthorizerFactoryException; + void deleteByTenantIdentity(String identity); } diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/extension/ExtensionManager.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/extension/ExtensionManager.java index d3703a478..27e8b9143 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/extension/ExtensionManager.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/extension/ExtensionManager.java @@ -17,9 +17,10 @@ package org.apache.nifi.registry.extension; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.authorization.AccessPolicyProvider; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authentication.LoginIdentityProvider; +import org.apache.nifi.registry.security.authorization.AccessPolicyProvider; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; import org.apache.nifi.registry.flow.FlowPersistenceProvider; import org.apache.nifi.registry.properties.NiFiRegistryProperties; import org.slf4j.Logger; @@ -54,6 +55,7 @@ public class ExtensionManager { classes.add(UserGroupProvider.class); classes.add(AccessPolicyProvider.class); classes.add(Authorizer.class); + classes.add(LoginIdentityProvider.class); EXTENSION_CLASSES = Collections.unmodifiableList(classes); } diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/AbstractPolicyBasedAuthorizer.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AbstractPolicyBasedAuthorizer.java similarity index 95% rename from nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/AbstractPolicyBasedAuthorizer.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AbstractPolicyBasedAuthorizer.java index 718ecc727..734a98300 100644 --- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/AbstractPolicyBasedAuthorizer.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AbstractPolicyBasedAuthorizer.java @@ -14,12 +14,27 @@ * 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 org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException; +package org.apache.nifi.registry.security.authorization; + +import org.apache.nifi.registry.security.authorization.AccessPolicy; +import org.apache.nifi.registry.security.authorization.AccessPolicyProvider; +import org.apache.nifi.registry.security.authorization.AccessPolicyProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.AuthorizationRequest; +import org.apache.nifi.registry.security.authorization.AuthorizationResult; +import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; +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.ManagedAuthorizer; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.User; +import org.apache.nifi.registry.security.authorization.UserAndGroups; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestructionException; +import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizableLookup.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizableLookup.java similarity index 94% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizableLookup.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizableLookup.java index e5016d3f3..f5bbd4d73 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizableLookup.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizableLookup.java @@ -14,9 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; -import org.apache.nifi.registry.authorization.resource.Authorizable; +import org.apache.nifi.registry.security.authorization.resource.Authorizable; public interface AuthorizableLookup { diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizeAccess.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizeAccess.java similarity index 93% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizeAccess.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizeAccess.java index e6efb51c3..94dc3be8a 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizeAccess.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizeAccess.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; public interface AuthorizeAccess { void authorize(AuthorizableLookup lookup); diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerCapabilityDetection.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerCapabilityDetection.java similarity index 84% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerCapabilityDetection.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerCapabilityDetection.java index e6cf79faa..5252c7f82 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerCapabilityDetection.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerCapabilityDetection.java @@ -14,7 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; + +import org.apache.nifi.registry.security.authorization.AccessPolicy; +import org.apache.nifi.registry.security.authorization.AccessPolicyProvider; +import org.apache.nifi.registry.security.authorization.Authorizer; +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.ManagedAuthorizer; +import org.apache.nifi.registry.security.authorization.User; public final class AuthorizerCapabilityDetection { diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerFactory.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java similarity index 91% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerFactory.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java index cf979dde7..022147840 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerFactory.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java @@ -14,21 +14,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.authorization.annotation.AuthorizerContext; -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 org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException; -import org.apache.nifi.registry.authorization.generated.Authorizers; -import org.apache.nifi.registry.authorization.generated.Prop; import org.apache.nifi.registry.extension.ExtensionManager; import org.apache.nifi.registry.properties.NiFiRegistryProperties; import org.apache.nifi.registry.provider.StandardProviderFactory; +import org.apache.nifi.registry.security.authorization.annotation.AuthorizerContext; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestructionException; +import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException; +import org.apache.nifi.registry.security.authorization.generated.Authorizers; +import org.apache.nifi.registry.security.authorization.generated.Prop; +import org.apache.nifi.registry.security.util.XmlUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.xml.sax.SAXException; import javax.xml.XMLConstants; @@ -53,12 +57,13 @@ * This implementation of AuthorizerFactory in NiFi Registry is based on a combination of * NiFi's AuthorizerFactory and AuthorizerFactoryBean. */ -public class StandardAuthorizerFactory implements AuthorizerFactory, UserGroupProviderLookup, AccessPolicyProviderLookup, AuthorizerLookup{ +@Configuration("authorizerFactory") +public class AuthorizerFactory implements UserGroupProviderLookup, AccessPolicyProviderLookup, AuthorizerLookup { private static final Logger logger = LoggerFactory.getLogger(StandardProviderFactory.class); private static final String AUTHORIZERS_XSD = "/authorizers.xsd"; - private static final String JAXB_GENERATED_PATH = "org.apache.nifi.registry.authorization.generated"; + private static final String JAXB_GENERATED_PATH = "org.apache.nifi.registry.security.authorization.generated"; private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext(); /** @@ -66,7 +71,7 @@ public class StandardAuthorizerFactory implements AuthorizerFactory, UserGroupPr */ private static JAXBContext initializeJaxbContext() { try { - return JAXBContext.newInstance(JAXB_GENERATED_PATH, StandardAuthorizerFactory.class.getClassLoader()); + return JAXBContext.newInstance(JAXB_GENERATED_PATH, AuthorizerFactory.class.getClassLoader()); } catch (JAXBException e) { throw new RuntimeException("Unable to create JAXBContext.", e); } @@ -80,7 +85,8 @@ private static JAXBContext initializeJaxbContext() { private final Map accessPolicyProviders = new HashMap<>(); private final Map authorizers = new HashMap<>(); - public StandardAuthorizerFactory(final NiFiRegistryProperties properties, final ExtensionManager extensionManager) { + @Autowired + public AuthorizerFactory(final NiFiRegistryProperties properties, final ExtensionManager extensionManager) { this.properties = properties; this.extensionManager = extensionManager; @@ -116,35 +122,7 @@ public Authorizer getAuthorizer(String identifier) { } - /***** AuthorizerFactory *****/ - - @Override - public void initialize() throws AuthorizerFactoryException { -// if (authorizerHolder.get() == null) { -// final File authorizersConfigFile = properties.getAuthorizersConfigurationFile(); -// if (authorizersConfigFile.exists()) { -// try { -// // find the schema -// final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); -// final Schema schema = schemaFactory.newSchema(StandardProviderFactory.class.getResource(AUTHORIZERS_XSD)); -// -// // attempt to unmarshal -// final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller(); -// unmarshaller.setSchema(schema); -// -// // set the holder for later use -// final JAXBElement element = unmarshaller.unmarshal(new StreamSource(authorizersConfigFile), Authorizers.class); -// authorizerHolder.set(element.getValue()); -// } catch (SAXException | JAXBException e) { -// throw new AuthorizerFactoryException("Unable to load the authorizer configuration file at: " + authorizersConfigFile.getAbsolutePath(), e); -// } -// } else { -// throw new AuthorizerFactoryException("Unable to find the providers configuration file at " + authorizersConfigFile.getAbsolutePath()); -// } -// } - } - - @Override + @Bean public Authorizer getAuthorizer() throws AuthorizerFactoryException { if (authorizer == null) { if (properties.getSslPort() == null) { @@ -163,34 +141,34 @@ public Authorizer getAuthorizer() throws AuthorizerFactoryException { final Authorizers authorizerConfiguration = loadAuthorizersConfiguration(); // create each user group provider - for (final org.apache.nifi.registry.authorization.generated.UserGroupProvider userGroupProvider : authorizerConfiguration.getUserGroupProvider()) { + for (final org.apache.nifi.registry.security.authorization.generated.UserGroupProvider userGroupProvider : authorizerConfiguration.getUserGroupProvider()) { userGroupProviders.put(userGroupProvider.getIdentifier(), createUserGroupProvider(userGroupProvider.getIdentifier(), userGroupProvider.getClazz())); } // configure each user group provider - for (final org.apache.nifi.registry.authorization.generated.UserGroupProvider provider : authorizerConfiguration.getUserGroupProvider()) { + for (final org.apache.nifi.registry.security.authorization.generated.UserGroupProvider provider : authorizerConfiguration.getUserGroupProvider()) { final UserGroupProvider instance = userGroupProviders.get(provider.getIdentifier()); instance.onConfigured(loadAuthorizerConfiguration(provider.getIdentifier(), provider.getProperty())); } // create each access policy provider - for (final org.apache.nifi.registry.authorization.generated.AccessPolicyProvider accessPolicyProvider : authorizerConfiguration.getAccessPolicyProvider()) { + for (final org.apache.nifi.registry.security.authorization.generated.AccessPolicyProvider accessPolicyProvider : authorizerConfiguration.getAccessPolicyProvider()) { accessPolicyProviders.put(accessPolicyProvider.getIdentifier(), createAccessPolicyProvider(accessPolicyProvider.getIdentifier(), accessPolicyProvider.getClazz())); } // configure each access policy provider - for (final org.apache.nifi.registry.authorization.generated.AccessPolicyProvider provider : authorizerConfiguration.getAccessPolicyProvider()) { + for (final org.apache.nifi.registry.security.authorization.generated.AccessPolicyProvider provider : authorizerConfiguration.getAccessPolicyProvider()) { final AccessPolicyProvider instance = accessPolicyProviders.get(provider.getIdentifier()); instance.onConfigured(loadAuthorizerConfiguration(provider.getIdentifier(), provider.getProperty())); } // create each authorizer - for (final org.apache.nifi.registry.authorization.generated.Authorizer authorizer : authorizerConfiguration.getAuthorizer()) { + for (final org.apache.nifi.registry.security.authorization.generated.Authorizer authorizer : authorizerConfiguration.getAuthorizer()) { authorizers.put(authorizer.getIdentifier(), createAuthorizer(authorizer.getIdentifier(), authorizer.getClazz(), authorizer.getClasspath())); } // configure each authorizer - for (final org.apache.nifi.registry.authorization.generated.Authorizer provider : authorizerConfiguration.getAuthorizer()) { + for (final org.apache.nifi.registry.security.authorization.generated.Authorizer provider : authorizerConfiguration.getAuthorizer()) { final Authorizer instance = authorizers.get(provider.getIdentifier()); instance.onConfigured(loadAuthorizerConfiguration(provider.getIdentifier(), provider.getProperty())); } @@ -224,7 +202,7 @@ private Authorizers loadAuthorizersConfiguration() throws Exception { // attempt to unmarshal final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller(); unmarshaller.setSchema(schema); - final JAXBElement element = unmarshaller.unmarshal(new StreamSource(authorizersConfigurationFile), Authorizers.class); + final JAXBElement element = unmarshaller.unmarshal(XmlUtils.createSafeReader(new StreamSource(authorizersConfigurationFile)), Authorizers.class); return element.getValue(); } catch (SAXException | JAXBException e) { throw new Exception("Unable to load the authorizer configuration file at: " + authorizersConfigurationFile.getAbsolutePath(), e); @@ -325,12 +303,6 @@ private Authorizer createAuthorizer(final String identifier, final String author // call post construction lifecycle event instance.initialize(new StandardAuthorizerInitializationContext(identifier, this, this, this)); - // TODO, implement and test loading additional resources from the classpath for custom authorizer impls. -// if (StringUtils.isNotEmpty(classpathResources)) { -// URL[] urls = ClassLoaderUtils.getURLsForClasspath(classpathResources, null, true); -// authorizerClassLoader = new URLClassLoader(urls, authorizerClassLoader); -// } - return installIntegrityChecks(instance); } @@ -423,7 +395,7 @@ public void preDestruction() throws AuthorizerDestructionException { }; } - public static Authorizer installIntegrityChecks(final Authorizer baseAuthorizer) { + private static Authorizer installIntegrityChecks(final Authorizer baseAuthorizer) { if (baseAuthorizer instanceof ManagedAuthorizer) { final ManagedAuthorizer baseManagedAuthorizer = (ManagedAuthorizer) baseAuthorizer; return new ManagedAuthorizer() { diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactoryException.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactoryException.java similarity index 95% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactoryException.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactoryException.java index 45e0e2415..a47955545 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/AuthorizerFactoryException.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactoryException.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; public class AuthorizerFactoryException extends RuntimeException { diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeConfigurableUserGroupProvider.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeConfigurableUserGroupProvider.java similarity index 86% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeConfigurableUserGroupProvider.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeConfigurableUserGroupProvider.java index 966455864..dc687efee 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeConfigurableUserGroupProvider.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeConfigurableUserGroupProvider.java @@ -14,13 +14,21 @@ * 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.UninheritableAuthorizationsException; +package org.apache.nifi.registry.security.authorization; + +import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.registry.security.authorization.ConfigurableUserGroupProvider; +import org.apache.nifi.registry.security.authorization.Group; +import org.apache.nifi.registry.security.authorization.User; +import org.apache.nifi.registry.security.authorization.UserAndGroups; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.UserGroupProviderLookup; +import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException; import org.apache.nifi.registry.util.PropertyValue; -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 org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestructionException; import java.util.HashSet; import java.util.Set; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeUserGroupProvider.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserGroupProvider.java similarity index 86% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeUserGroupProvider.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserGroupProvider.java index d2f8c4e86..96e9d4176 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/CompositeUserGroupProvider.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/CompositeUserGroupProvider.java @@ -14,12 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import org.apache.commons.lang3.StringUtils; -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 org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.registry.security.authorization.Group; +import org.apache.nifi.registry.security.authorization.User; +import org.apache.nifi.registry.security.authorization.UserAndGroups; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.UserGroupProviderLookup; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestructionException; import java.util.ArrayList; import java.util.HashSet; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizableLookup.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java similarity index 93% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizableLookup.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java index 78cd63003..af4890815 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizableLookup.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizableLookup.java @@ -14,16 +14,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.authorization.resource.Authorizable; -import org.apache.nifi.registry.authorization.resource.ResourceFactory; -import org.apache.nifi.registry.authorization.resource.ResourceType; -import org.apache.nifi.registry.authorization.resource.AccessPolicyAuthorizable; +import org.apache.nifi.registry.security.authorization.Resource; +import org.apache.nifi.registry.security.authorization.resource.Authorizable; +import org.apache.nifi.registry.security.authorization.resource.ResourceFactory; +import org.apache.nifi.registry.security.authorization.resource.ResourceType; +import org.apache.nifi.registry.security.authorization.resource.AccessPolicyAuthorizable; import org.apache.nifi.registry.exception.ResourceNotFoundException; +import org.springframework.stereotype.Component; -// TODO, make this spring-wired bean +@Component public class StandardAuthorizableLookup implements AuthorizableLookup { private static final Authorizable TENANTS_AUTHORIZABLE = new Authorizable() { diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerConfigurationContext.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerConfigurationContext.java similarity index 96% rename from nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerConfigurationContext.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerConfigurationContext.java index 798f9749e..9d274f70b 100644 --- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerConfigurationContext.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerConfigurationContext.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import org.apache.nifi.registry.util.PropertyValue; import org.apache.nifi.registry.util.StandardPropertyValue; diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerInitializationContext.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerInitializationContext.java similarity index 97% rename from nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerInitializationContext.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerInitializationContext.java index e23a93e16..d643e91eb 100644 --- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/StandardAuthorizerInitializationContext.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardAuthorizerInitializationContext.java @@ -14,11 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; -/** - * - */ public class StandardAuthorizerInitializationContext implements AuthorizerInitializationContext { private final String identifier; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardManagedAuthorizer.java similarity index 88% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardManagedAuthorizer.java index 8cd4fea93..b6f984282 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/StandardManagedAuthorizer.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/StandardManagedAuthorizer.java @@ -14,14 +14,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException; +import org.apache.nifi.registry.security.authorization.AccessPolicy; +import org.apache.nifi.registry.security.authorization.AccessPolicyProvider; +import org.apache.nifi.registry.security.authorization.AccessPolicyProviderLookup; +import org.apache.nifi.registry.security.authorization.AuthorizationRequest; +import org.apache.nifi.registry.security.authorization.AuthorizationResult; +import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.registry.security.authorization.AuthorizerInitializationContext; +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.ManagedAuthorizer; +import org.apache.nifi.registry.security.authorization.User; +import org.apache.nifi.registry.security.authorization.UserAndGroups; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException; import org.apache.nifi.registry.util.PropertyValue; -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 org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestructionException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/UsersAndAccessPolicies.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/UsersAndAccessPolicies.java similarity index 97% rename from nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/UsersAndAccessPolicies.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/UsersAndAccessPolicies.java index 7e05c9c75..7675f27f2 100644 --- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/UsersAndAccessPolicies.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/UsersAndAccessPolicies.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import java.util.Set; diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/AuthorizationsHolder.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/AuthorizationsHolder.java similarity index 89% rename from nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/AuthorizationsHolder.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/AuthorizationsHolder.java index 38a1571d1..6e84f492c 100644 --- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/AuthorizationsHolder.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/AuthorizationsHolder.java @@ -14,13 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.file; +package org.apache.nifi.registry.security.authorization.file; -import org.apache.nifi.registry.authorization.file.generated.Authorizations; -import org.apache.nifi.registry.authorization.file.generated.Policies; -import org.apache.nifi.registry.authorization.AccessPolicy; -import org.apache.nifi.registry.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.file.generated.Authorizations; +import org.apache.nifi.registry.security.authorization.file.generated.Policies; +import org.apache.nifi.registry.security.authorization.AccessPolicy; +import org.apache.nifi.registry.security.authorization.RequestAction; import java.util.Collections; import java.util.HashMap; @@ -69,14 +69,14 @@ public AuthorizationsHolder(final Authorizations authorizations) { * @param policies the JAXB Policies element * @return a set of AccessPolicies corresponding to the provided Resources */ - private Set createAccessPolicies(org.apache.nifi.registry.authorization.file.generated.Policies policies) { + private Set createAccessPolicies(org.apache.nifi.registry.security.authorization.file.generated.Policies policies) { Set allPolicies = new HashSet<>(); if (policies == null || policies.getPolicy() == null) { return allPolicies; } // load the new authorizations - for (final org.apache.nifi.registry.authorization.file.generated.Policy policy : policies.getPolicy()) { + for (final org.apache.nifi.registry.security.authorization.file.generated.Policy policy : policies.getPolicy()) { final String policyIdentifier = policy.getIdentifier(); final String resourceIdentifier = policy.getResource(); @@ -86,12 +86,12 @@ private Set createAccessPolicies(org.apache.nifi.registry.authoriz .resource(resourceIdentifier); // add each user identifier - for (org.apache.nifi.registry.authorization.file.generated.Policy.User user : policy.getUser()) { + for (org.apache.nifi.registry.security.authorization.file.generated.Policy.User user : policy.getUser()) { builder.addUser(user.getIdentifier()); } // add each group identifier - for (org.apache.nifi.registry.authorization.file.generated.Policy.Group group : policy.getGroup()) { + for (org.apache.nifi.registry.security.authorization.file.generated.Policy.Group group : policy.getGroup()) { builder.addGroup(group.getIdentifier()); } diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileAccessPolicyProvider.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileAccessPolicyProvider.java similarity index 95% rename from nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileAccessPolicyProvider.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileAccessPolicyProvider.java index 47a81cc9a..74887ad01 100644 --- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileAccessPolicyProvider.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileAccessPolicyProvider.java @@ -14,29 +14,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.file; +package org.apache.nifi.registry.security.authorization.file; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.util.PropertyValue; -import org.apache.nifi.registry.authorization.annotation.AuthorizerContext; -import org.apache.nifi.registry.authorization.file.generated.Authorizations; -import org.apache.nifi.registry.authorization.file.generated.Policies; -import org.apache.nifi.registry.authorization.file.generated.Policy; +import org.apache.nifi.registry.properties.NiFiRegistryProperties; import org.apache.nifi.registry.properties.util.IdentityMapping; import org.apache.nifi.registry.properties.util.IdentityMappingUtil; -import org.apache.nifi.registry.authorization.AccessPolicy; -import org.apache.nifi.registry.authorization.AccessPolicyProviderInitializationContext; -import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException; -import org.apache.nifi.registry.authorization.AuthorizerConfigurationContext; -import org.apache.nifi.registry.authorization.exception.AuthorizerCreationException; -import org.apache.nifi.registry.authorization.exception.AuthorizerDestructionException; -import org.apache.nifi.registry.authorization.ConfigurableAccessPolicyProvider; -import org.apache.nifi.registry.authorization.RequestAction; -import org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException; -import org.apache.nifi.registry.authorization.User; -import org.apache.nifi.registry.authorization.UserGroupProvider; -import org.apache.nifi.registry.authorization.UserGroupProviderLookup; -import org.apache.nifi.registry.properties.NiFiRegistryProperties; +import org.apache.nifi.registry.security.authorization.AccessPolicy; +import org.apache.nifi.registry.security.authorization.AccessPolicyProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.registry.security.authorization.ConfigurableAccessPolicyProvider; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.User; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authorization.UserGroupProviderLookup; +import org.apache.nifi.registry.security.authorization.annotation.AuthorizerContext; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestructionException; +import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException; +import org.apache.nifi.registry.security.authorization.file.generated.Authorizations; +import org.apache.nifi.registry.security.authorization.file.generated.Policies; +import org.apache.nifi.registry.security.authorization.file.generated.Policy; +import org.apache.nifi.registry.util.PropertyValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -80,7 +80,7 @@ public class FileAccessPolicyProvider implements ConfigurableAccessPolicyProvide private static final Logger logger = LoggerFactory.getLogger(FileAccessPolicyProvider.class); private static final String AUTHORIZATIONS_XSD = "/authorizations.xsd"; - private static final String JAXB_AUTHORIZATIONS_PATH = "org.apache.nifi.registry.authorization.file.generated"; + private static final String JAXB_AUTHORIZATIONS_PATH = "org.apache.nifi.registry.security.authorization.file.generated"; private static final JAXBContext JAXB_AUTHORIZATIONS_CONTEXT = initializeJaxbContext(JAXB_AUTHORIZATIONS_PATH); diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileAuthorizer.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileAuthorizer.java similarity index 89% rename from nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileAuthorizer.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileAuthorizer.java index ef273d9f6..e7104e303 100644 --- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileAuthorizer.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileAuthorizer.java @@ -14,24 +14,24 @@ * 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.StandardAuthorizerConfigurationContext; -import org.apache.nifi.registry.authorization.annotation.AuthorizerContext; -import org.apache.nifi.registry.authorization.AbstractPolicyBasedAuthorizer; -import org.apache.nifi.registry.authorization.AccessPolicy; -import org.apache.nifi.registry.authorization.AccessPolicyProviderInitializationContext; -import org.apache.nifi.registry.authorization.AccessPolicyProviderLookup; -import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException; -import org.apache.nifi.registry.authorization.AuthorizerConfigurationContext; -import org.apache.nifi.registry.authorization.exception.AuthorizerCreationException; -import org.apache.nifi.registry.authorization.AuthorizerInitializationContext; -import org.apache.nifi.registry.authorization.Group; -import org.apache.nifi.registry.authorization.RequestAction; -import org.apache.nifi.registry.authorization.User; -import org.apache.nifi.registry.authorization.UserGroupProviderInitializationContext; -import org.apache.nifi.registry.authorization.UserGroupProviderLookup; -import org.apache.nifi.registry.authorization.UsersAndAccessPolicies; +package org.apache.nifi.registry.security.authorization.file; + +import org.apache.nifi.registry.security.authorization.StandardAuthorizerConfigurationContext; +import org.apache.nifi.registry.security.authorization.annotation.AuthorizerContext; +import org.apache.nifi.registry.security.authorization.AbstractPolicyBasedAuthorizer; +import org.apache.nifi.registry.security.authorization.AccessPolicy; +import org.apache.nifi.registry.security.authorization.AccessPolicyProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.AccessPolicyProviderLookup; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.authorization.AuthorizerInitializationContext; +import org.apache.nifi.registry.security.authorization.Group; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.User; +import org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.UserGroupProviderLookup; +import org.apache.nifi.registry.security.authorization.UsersAndAccessPolicies; import org.apache.nifi.registry.properties.NiFiRegistryProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileUserGroupProvider.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileUserGroupProvider.java similarity index 83% rename from nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileUserGroupProvider.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileUserGroupProvider.java index 54c3d969c..85d62cd71 100644 --- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/FileUserGroupProvider.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/FileUserGroupProvider.java @@ -14,26 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.file; +package org.apache.nifi.registry.security.authorization.file; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.authorization.annotation.AuthorizerContext; -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.security.authorization.annotation.AuthorizerContext; +import org.apache.nifi.registry.security.authorization.file.tenants.generated.Groups; +import org.apache.nifi.registry.security.authorization.file.tenants.generated.Tenants; +import org.apache.nifi.registry.security.authorization.file.tenants.generated.Users; import org.apache.nifi.registry.properties.util.IdentityMapping; import org.apache.nifi.registry.properties.util.IdentityMappingUtil; import org.apache.nifi.registry.util.PropertyValue; -import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException; -import org.apache.nifi.registry.authorization.AuthorizerConfigurationContext; -import org.apache.nifi.registry.authorization.exception.AuthorizerCreationException; -import org.apache.nifi.registry.authorization.exception.AuthorizerDestructionException; -import org.apache.nifi.registry.authorization.ConfigurableUserGroupProvider; -import org.apache.nifi.registry.authorization.Group; -import org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException; -import org.apache.nifi.registry.authorization.User; -import org.apache.nifi.registry.authorization.UserAndGroups; -import org.apache.nifi.registry.authorization.UserGroupProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestructionException; +import org.apache.nifi.registry.security.authorization.ConfigurableUserGroupProvider; +import org.apache.nifi.registry.security.authorization.Group; +import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException; +import org.apache.nifi.registry.security.authorization.User; +import org.apache.nifi.registry.security.authorization.UserAndGroups; +import org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext; import org.apache.nifi.registry.properties.NiFiRegistryProperties; import org.apache.nifi.registry.util.FileUtils; import org.slf4j.Logger; @@ -82,7 +82,7 @@ public class FileUserGroupProvider implements ConfigurableUserGroupProvider { private static final Logger logger = LoggerFactory.getLogger(FileUserGroupProvider.class); private static final String TENANTS_XSD = "/tenants.xsd"; - private static final String JAXB_TENANTS_PATH = "org.apache.nifi.registry.authorization.file.tenants.generated"; + private static final String JAXB_TENANTS_PATH = "org.apache.nifi.registry.security.authorization.file.tenants.generated"; private static final JAXBContext JAXB_TENANTS_CONTEXT = initializeJaxbContext(JAXB_TENANTS_PATH); @@ -186,7 +186,7 @@ public synchronized User addUser(User user) throws AuthorizationAccessException throw new IllegalArgumentException("User cannot be null"); } - final org.apache.nifi.registry.authorization.file.tenants.generated.User jaxbUser = createJAXBUser(user); + final org.apache.nifi.registry.security.authorization.file.tenants.generated.User jaxbUser = createJAXBUser(user); final UserGroupHolder holder = userGroupHolder.get(); final Tenants tenants = holder.getTenants(); @@ -216,11 +216,11 @@ public synchronized User updateUser(User user) throws AuthorizationAccessExcepti final UserGroupHolder holder = userGroupHolder.get(); final Tenants tenants = holder.getTenants(); - final List users = tenants.getUsers().getUser(); + final List users = tenants.getUsers().getUser(); // fine the User that needs to be updated - org.apache.nifi.registry.authorization.file.tenants.generated.User updateUser = null; - for (org.apache.nifi.registry.authorization.file.tenants.generated.User jaxbUser : users) { + org.apache.nifi.registry.security.authorization.file.tenants.generated.User updateUser = null; + for (org.apache.nifi.registry.security.authorization.file.tenants.generated.User jaxbUser : users) { if (user.getIdentifier().equals(jaxbUser.getIdentifier())) { updateUser = jaxbUser; break; @@ -271,10 +271,10 @@ public synchronized User deleteUser(String userIdentifier) throws AuthorizationA // for each group iterate over the user references and remove the user reference if it matches the user being deleted final Tenants tenants = holder.getTenants(); - for (org.apache.nifi.registry.authorization.file.tenants.generated.Group group : tenants.getGroups().getGroup()) { - Iterator groupUserIter = group.getUser().iterator(); + for (org.apache.nifi.registry.security.authorization.file.tenants.generated.Group group : tenants.getGroups().getGroup()) { + Iterator groupUserIter = group.getUser().iterator(); while (groupUserIter.hasNext()) { - org.apache.nifi.registry.authorization.file.tenants.generated.Group.User groupUser = groupUserIter.next(); + org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User groupUser = groupUserIter.next(); if (groupUser.getIdentifier().equals(userIdentifier)) { groupUserIter.remove(); break; @@ -283,9 +283,9 @@ public synchronized User deleteUser(String userIdentifier) throws AuthorizationA } // remove the actual user - Iterator iter = tenants.getUsers().getUser().iterator(); + Iterator iter = tenants.getUsers().getUser().iterator(); while (iter.hasNext()) { - org.apache.nifi.registry.authorization.file.tenants.generated.User jaxbUser = iter.next(); + org.apache.nifi.registry.security.authorization.file.tenants.generated.User jaxbUser = iter.next(); if (userIdentifier.equals(jaxbUser.getIdentifier())) { iter.remove(); break; @@ -314,15 +314,15 @@ public synchronized Group addGroup(Group group) throws AuthorizationAccessExcept checkGroupUsers(group, tenants.getUsers().getUser()); // create a new JAXB Group based on the incoming Group - final org.apache.nifi.registry.authorization.file.tenants.generated.Group jaxbGroup = - new org.apache.nifi.registry.authorization.file.tenants.generated.Group(); + final org.apache.nifi.registry.security.authorization.file.tenants.generated.Group jaxbGroup = + new org.apache.nifi.registry.security.authorization.file.tenants.generated.Group(); jaxbGroup.setIdentifier(group.getIdentifier()); jaxbGroup.setName(group.getName()); // add each user to the group for (String groupUser : group.getUsers()) { - org.apache.nifi.registry.authorization.file.tenants.generated.Group.User jaxbGroupUser = - new org.apache.nifi.registry.authorization.file.tenants.generated.Group.User(); + org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User jaxbGroupUser = + new org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User(); jaxbGroupUser.setIdentifier(groupUser); jaxbGroup.getUser().add(jaxbGroupUser); } @@ -370,8 +370,8 @@ public synchronized Group updateGroup(Group group) throws AuthorizationAccessExc final Tenants tenants = holder.getTenants(); // find the group that needs to be update - org.apache.nifi.registry.authorization.file.tenants.generated.Group updateGroup = null; - for (org.apache.nifi.registry.authorization.file.tenants.generated.Group jaxbGroup : tenants.getGroups().getGroup()) { + org.apache.nifi.registry.security.authorization.file.tenants.generated.Group updateGroup = null; + for (org.apache.nifi.registry.security.authorization.file.tenants.generated.Group jaxbGroup : tenants.getGroups().getGroup()) { if (jaxbGroup.getIdentifier().equals(group.getIdentifier())) { updateGroup = jaxbGroup; break; @@ -386,8 +386,8 @@ public synchronized Group updateGroup(Group group) throws AuthorizationAccessExc // reset the list of users and add each user to the group updateGroup.getUser().clear(); for (String groupUser : group.getUsers()) { - org.apache.nifi.registry.authorization.file.tenants.generated.Group.User jaxbGroupUser = - new org.apache.nifi.registry.authorization.file.tenants.generated.Group.User(); + org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User jaxbGroupUser = + new org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User(); jaxbGroupUser.setIdentifier(groupUser); updateGroup.getUser().add(jaxbGroupUser); } @@ -421,9 +421,9 @@ public synchronized Group deleteGroup(String groupIdentifier) throws Authorizati // now remove the actual group from the top-level list of groups final Tenants tenants = holder.getTenants(); - Iterator iter = tenants.getGroups().getGroup().iterator(); + Iterator iter = tenants.getGroups().getGroup().iterator(); while (iter.hasNext()) { - org.apache.nifi.registry.authorization.file.tenants.generated.Group jaxbGroup = iter.next(); + org.apache.nifi.registry.security.authorization.file.tenants.generated.Group jaxbGroup = iter.next(); if (groupIdentifier.equals(jaxbGroup.getIdentifier())) { iter.remove(); break; @@ -585,21 +585,21 @@ private void writeGroup(final XMLStreamWriter writer, final Group group) throws writer.writeEndElement(); } - private org.apache.nifi.registry.authorization.file.tenants.generated.User createJAXBUser(User user) { - final org.apache.nifi.registry.authorization.file.tenants.generated.User jaxbUser = - new org.apache.nifi.registry.authorization.file.tenants.generated.User(); + private org.apache.nifi.registry.security.authorization.file.tenants.generated.User createJAXBUser(User user) { + final org.apache.nifi.registry.security.authorization.file.tenants.generated.User jaxbUser = + new org.apache.nifi.registry.security.authorization.file.tenants.generated.User(); jaxbUser.setIdentifier(user.getIdentifier()); jaxbUser.setIdentity(user.getIdentity()); return jaxbUser; } - private Set checkGroupUsers( + private Set checkGroupUsers( final Group group, - final List users) { - final Set jaxbUsers = new HashSet<>(); + final List users) { + final Set jaxbUsers = new HashSet<>(); for (String groupUser : group.getUsers()) { boolean found = false; - for (org.apache.nifi.registry.authorization.file.tenants.generated.User jaxbUser : users) { + for (org.apache.nifi.registry.security.authorization.file.tenants.generated.User jaxbUser : users) { if (jaxbUser.getIdentifier().equals(groupUser)) { jaxbUsers.add(jaxbUser); found = true; @@ -677,13 +677,13 @@ private void populateInitialUsers(final Tenants tenants) { * @param userIdentity the user identity to find or create * @return the User from Tenants with the given identity, or a new instance that was added to Tenants */ - private org.apache.nifi.registry.authorization.file.tenants.generated.User getOrCreateUser(final Tenants tenants, final String userIdentity) { + private org.apache.nifi.registry.security.authorization.file.tenants.generated.User getOrCreateUser(final Tenants tenants, final String userIdentity) { if (StringUtils.isBlank(userIdentity)) { return null; } - org.apache.nifi.registry.authorization.file.tenants.generated.User foundUser = null; - for (org.apache.nifi.registry.authorization.file.tenants.generated.User user : tenants.getUsers().getUser()) { + org.apache.nifi.registry.security.authorization.file.tenants.generated.User foundUser = null; + for (org.apache.nifi.registry.security.authorization.file.tenants.generated.User user : tenants.getUsers().getUser()) { if (user.getIdentity().equals(userIdentity)) { foundUser = user; break; @@ -692,7 +692,7 @@ private org.apache.nifi.registry.authorization.file.tenants.generated.User getOr if (foundUser == null) { final String userIdentifier = IdentifierUtil.getIdentifier(userIdentity); - foundUser = new org.apache.nifi.registry.authorization.file.tenants.generated.User(); + foundUser = new org.apache.nifi.registry.security.authorization.file.tenants.generated.User(); foundUser.setIdentifier(userIdentifier); foundUser.setIdentity(userIdentity); tenants.getUsers().getUser().add(foundUser); @@ -708,13 +708,13 @@ private org.apache.nifi.registry.authorization.file.tenants.generated.User getOr * @param groupName the name of the group to look for * @return the Group from Tenants with the given name, or a new instance that was added to Tenants */ - private org.apache.nifi.registry.authorization.file.tenants.generated.Group getOrCreateGroup(final Tenants tenants, final String groupName) { + private org.apache.nifi.registry.security.authorization.file.tenants.generated.Group getOrCreateGroup(final Tenants tenants, final String groupName) { if (StringUtils.isBlank(groupName)) { return null; } - org.apache.nifi.registry.authorization.file.tenants.generated.Group foundGroup = null; - for (org.apache.nifi.registry.authorization.file.tenants.generated.Group group : tenants.getGroups().getGroup()) { + org.apache.nifi.registry.security.authorization.file.tenants.generated.Group foundGroup = null; + for (org.apache.nifi.registry.security.authorization.file.tenants.generated.Group group : tenants.getGroups().getGroup()) { if (group.getName().equals(groupName)) { foundGroup = group; break; @@ -723,7 +723,7 @@ private org.apache.nifi.registry.authorization.file.tenants.generated.Group getO if (foundGroup == null) { final String newGroupIdentifier = IdentifierUtil.getIdentifier(groupName); - foundGroup = new org.apache.nifi.registry.authorization.file.tenants.generated.Group(); + foundGroup = new org.apache.nifi.registry.security.authorization.file.tenants.generated.Group(); foundGroup.setIdentifier(newGroupIdentifier); foundGroup.setName(groupName); tenants.getGroups().getGroup().add(foundGroup); diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/IdentifierUtil.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/IdentifierUtil.java similarity index 95% rename from nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/IdentifierUtil.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/IdentifierUtil.java index b698cd01b..91e673f76 100644 --- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/IdentifierUtil.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/IdentifierUtil.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.file; +package org.apache.nifi.registry.security.authorization.file; import org.apache.commons.lang3.StringUtils; diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/UserGroupHolder.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/UserGroupHolder.java similarity index 90% rename from nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/UserGroupHolder.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/UserGroupHolder.java index 3055c1983..9828a4555 100644 --- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/file/UserGroupHolder.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/file/UserGroupHolder.java @@ -14,14 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.file; +package org.apache.nifi.registry.security.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 org.apache.nifi.registry.security.authorization.file.tenants.generated.Groups; +import org.apache.nifi.registry.security.authorization.file.tenants.generated.Tenants; +import org.apache.nifi.registry.security.authorization.file.tenants.generated.Users; +import org.apache.nifi.registry.security.authorization.Group; +import org.apache.nifi.registry.security.authorization.User; import java.util.Collections; import java.util.HashMap; @@ -93,7 +93,7 @@ private Set createUsers(Users users) { return allUsers; } - for (org.apache.nifi.registry.authorization.file.tenants.generated.User user : users.getUser()) { + for (org.apache.nifi.registry.security.authorization.file.tenants.generated.User user : users.getUser()) { final User.Builder builder = new User.Builder() .identity(user.getIdentity()) .identifier(user.getIdentifier()); @@ -117,12 +117,12 @@ private Set createGroups(Groups groups, return allGroups; } - for (org.apache.nifi.registry.authorization.file.tenants.generated.Group group : groups.getGroup()) { + for (org.apache.nifi.registry.security.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()) { + for (org.apache.nifi.registry.security.authorization.file.tenants.generated.Group.User groupUser : group.getUser()) { builder.addUser(groupUser.getIdentifier()); } diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/AccessPolicyAuthorizable.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/AccessPolicyAuthorizable.java similarity index 88% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/AccessPolicyAuthorizable.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/AccessPolicyAuthorizable.java index 5d9997f69..de285652e 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/AccessPolicyAuthorizable.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/AccessPolicyAuthorizable.java @@ -14,15 +14,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.resource; - -import org.apache.nifi.registry.authorization.exception.AccessDeniedException; -import org.apache.nifi.registry.authorization.AuthorizationResult; -import org.apache.nifi.registry.authorization.AuthorizationResult.Result; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.RequestAction; -import org.apache.nifi.registry.authorization.Resource; -import org.apache.nifi.registry.authorization.user.NiFiUser; +package org.apache.nifi.registry.security.authorization.resource; + +import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException; +import org.apache.nifi.registry.security.authorization.AuthorizationResult; +import org.apache.nifi.registry.security.authorization.AuthorizationResult.Result; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.Resource; +import org.apache.nifi.registry.security.authorization.user.NiFiUser; import java.util.Map; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/Authorizable.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java similarity index 93% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/Authorizable.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java index 77f0fec37..d08467e80 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/Authorizable.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/Authorizable.java @@ -14,18 +14,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.resource; - -import org.apache.nifi.registry.authorization.AuthorizationResult.Result; -import org.apache.nifi.registry.authorization.exception.AccessDeniedException; -import org.apache.nifi.registry.authorization.user.NiFiUser; -import org.apache.nifi.registry.authorization.AuthorizationAuditor; -import org.apache.nifi.registry.authorization.AuthorizationRequest; -import org.apache.nifi.registry.authorization.AuthorizationResult; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.RequestAction; -import org.apache.nifi.registry.authorization.Resource; -import org.apache.nifi.registry.authorization.UserContextKeys; +package org.apache.nifi.registry.security.authorization.resource; + +import org.apache.nifi.registry.security.authorization.AuthorizationResult.Result; +import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException; +import org.apache.nifi.registry.security.authorization.user.NiFiUser; +import org.apache.nifi.registry.security.authorization.AuthorizationAuditor; +import org.apache.nifi.registry.security.authorization.AuthorizationRequest; +import org.apache.nifi.registry.security.authorization.AuthorizationResult; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.Resource; +import org.apache.nifi.registry.security.authorization.UserContextKeys; import java.util.HashMap; import java.util.Map; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/EnforcePolicyPermissionsThroughBaseResource.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/EnforcePolicyPermissionsThroughBaseResource.java similarity index 95% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/EnforcePolicyPermissionsThroughBaseResource.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/EnforcePolicyPermissionsThroughBaseResource.java index 2d6b1a859..90fc14310 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/EnforcePolicyPermissionsThroughBaseResource.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/EnforcePolicyPermissionsThroughBaseResource.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.resource; +package org.apache.nifi.registry.security.authorization.resource; /** * Defers permissions on policies to the policies of the base authorizable. Required because we don't diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceFactory.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java similarity index 98% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceFactory.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java index 1d0b023e6..c12256cab 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceFactory.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceFactory.java @@ -14,9 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.resource; +package org.apache.nifi.registry.security.authorization.resource; -import org.apache.nifi.registry.authorization.Resource; +import org.apache.nifi.registry.security.authorization.Resource; import java.util.Objects; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceType.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceType.java similarity index 95% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceType.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceType.java index 4987a383d..a49d9734c 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/resource/ResourceType.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/resource/ResourceType.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.resource; +package org.apache.nifi.registry.security.authorization.resource; public enum ResourceType { Bucket("/buckets"), diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUser.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUser.java similarity index 96% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUser.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUser.java index 5f4924168..47127b63c 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUser.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUser.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.nifi.registry.authorization.user; +package org.apache.nifi.registry.security.authorization.user; import java.util.Set; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserDetails.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserDetails.java similarity index 97% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserDetails.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserDetails.java index 7b0da47d6..ca6ea2e8a 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserDetails.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserDetails.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.user; +package org.apache.nifi.registry.security.authorization.user; import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.GrantedAuthority; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserUtils.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserUtils.java similarity index 98% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserUtils.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserUtils.java index 2ad508ac3..b5147eadb 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/NiFiUserUtils.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/NiFiUserUtils.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.user; +package org.apache.nifi.registry.security.authorization.user; import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.Authentication; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/StandardNiFiUser.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/StandardNiFiUser.java similarity index 98% rename from nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/StandardNiFiUser.java rename to nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/StandardNiFiUser.java index 3a8a8bc83..92c227434 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/authorization/user/StandardNiFiUser.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/user/StandardNiFiUser.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.user; +package org.apache.nifi.registry.security.authorization.user; import org.apache.commons.lang3.StringUtils; diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/key/Key.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/key/Key.java new file mode 100644 index 000000000..c110fa8cd --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/key/Key.java @@ -0,0 +1,69 @@ +/* + * 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.key; + +import java.io.Serializable; + +/** + * An signing key for a NiFi user. + */ +public class Key implements Serializable { + + private String id; + private String identity; + private String key; + + /** + * The key id. + * + * @return the id + */ + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + /** + * The identity of the user this key is associated with. + * + * @return the identity + */ + public String getIdentity() { + return identity; + } + + public void setIdentity(String identity) { + this.identity = identity; + } + + /** + * The signing key. + * + * @return the signing key + */ + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/key/KeyService.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/key/KeyService.java new file mode 100644 index 000000000..3b9a7ca9f --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/key/KeyService.java @@ -0,0 +1,46 @@ +/* + * 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.key; + +/** + * Manages NiFi user keys. + */ +public interface KeyService { + + /** + * Gets a key for the specified user identity. Returns null if the user has not had a key issued + * + * @param id The key id + * @return The key or null + */ + Key getKey(String id); + + /** + * Gets a key for the specified user identity. If a key does not exist, one will be created. + * + * @param identity The user identity + * @return The key + */ + Key getOrCreateKey(String identity); + + /** + * Deletes keys for the specified identity. + * + * @param identity The user identity + */ + void deleteKey(String identity); +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/IdentityStrategy.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/IdentityStrategy.java new file mode 100644 index 000000000..135f261ef --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/IdentityStrategy.java @@ -0,0 +1,22 @@ +/* + * 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.ldap; + +public enum IdentityStrategy { + USE_DN, + USE_USERNAME; +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/LdapAuthenticationStrategy.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/LdapAuthenticationStrategy.java new file mode 100644 index 000000000..331fbc34b --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/LdapAuthenticationStrategy.java @@ -0,0 +1,24 @@ +/* + * 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.ldap; + +public enum LdapAuthenticationStrategy { + ANONYMOUS, + SIMPLE, + LDAPS, + START_TLS +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/LdapIdentityProvider.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/LdapIdentityProvider.java new file mode 100644 index 000000000..6beebc57f --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/LdapIdentityProvider.java @@ -0,0 +1,348 @@ +/* + * 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.ldap; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.security.authentication.AuthenticationResponse; +import org.apache.nifi.registry.security.authentication.LoginCredentials; +import org.apache.nifi.registry.security.authentication.LoginIdentityProvider; +import org.apache.nifi.registry.security.authentication.LoginIdentityProviderConfigurationContext; +import org.apache.nifi.registry.security.authentication.LoginIdentityProviderInitializationContext; +import org.apache.nifi.registry.security.authentication.exception.IdentityAccessException; +import org.apache.nifi.registry.security.authentication.exception.InvalidLoginCredentialsException; +import org.apache.nifi.registry.security.authentication.exception.ProviderCreationException; +import org.apache.nifi.registry.security.authentication.exception.ProviderDestructionException; +import org.apache.nifi.registry.security.util.SslContextFactory; +import org.apache.nifi.registry.security.util.SslContextFactory.ClientAuth; +import org.apache.nifi.registry.util.FormatUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ldap.AuthenticationException; +import org.springframework.ldap.core.support.AbstractTlsDirContextAuthenticationStrategy; +import org.springframework.ldap.core.support.DefaultTlsDirContextAuthenticationStrategy; +import org.springframework.ldap.core.support.LdapContextSource; +import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider; +import org.springframework.security.ldap.authentication.BindAuthenticator; +import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; +import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; +import org.springframework.security.ldap.search.LdapUserSearch; +import org.springframework.security.ldap.userdetails.LdapUserDetails; + +import javax.naming.Context; +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Abstract LDAP based implementation of a login identity provider. + */ +public class LdapIdentityProvider implements LoginIdentityProvider { + + private static final Logger logger = LoggerFactory.getLogger(LdapIdentityProvider.class); + + private AbstractLdapAuthenticationProvider provider; + private String issuer; + private long expiration; + private IdentityStrategy identityStrategy; + + @Override + public final void initialize(final LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException { + this.issuer = getClass().getSimpleName(); + } + + @Override + public final void onConfigured(final LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException { + final String rawExpiration = configurationContext.getProperty("Authentication Expiration"); + if (StringUtils.isBlank(rawExpiration)) { + throw new ProviderCreationException("The Authentication Expiration must be specified."); + } + + try { + expiration = FormatUtils.getTimeDuration(rawExpiration, TimeUnit.MILLISECONDS); + } catch (final IllegalArgumentException iae) { + throw new ProviderCreationException(String.format("The Expiration Duration '%s' is not a valid time duration", rawExpiration)); + } + + final LdapContextSource context = new LdapContextSource(); + + final Map baseEnvironment = new HashMap<>(); + + // connect/read time out + setTimeout(configurationContext, baseEnvironment, "Connect Timeout", "com.sun.jndi.ldap.connect.timeout"); + setTimeout(configurationContext, baseEnvironment, "Read Timeout", "com.sun.jndi.ldap.read.timeout"); + + // authentication strategy + final String rawAuthenticationStrategy = configurationContext.getProperty("Authentication Strategy"); + final LdapAuthenticationStrategy authenticationStrategy; + try { + authenticationStrategy = LdapAuthenticationStrategy.valueOf(rawAuthenticationStrategy); + } catch (final IllegalArgumentException iae) { + throw new ProviderCreationException(String.format("Unrecognized authentication strategy '%s'. Possible values are [%s]", + rawAuthenticationStrategy, StringUtils.join(LdapAuthenticationStrategy.values(), ", "))); + } + + switch (authenticationStrategy) { + case ANONYMOUS: + context.setAnonymousReadOnly(true); + break; + default: + final String userDn = configurationContext.getProperty("Manager DN"); + final String password = configurationContext.getProperty("Manager Password"); + + context.setUserDn(userDn); + context.setPassword(password); + + switch (authenticationStrategy) { + case SIMPLE: + context.setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy()); + break; + case LDAPS: + context.setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy()); + + // indicate a secure connection + baseEnvironment.put(Context.SECURITY_PROTOCOL, "ssl"); + + // get the configured ssl context + final SSLContext ldapsSslContext = getConfiguredSslContext(configurationContext); + if (ldapsSslContext != null) { + // initialize the ldaps socket factory prior to use + LdapsSocketFactory.initialize(ldapsSslContext.getSocketFactory()); + baseEnvironment.put("java.naming.ldap.factory.socket", LdapsSocketFactory.class.getName()); + } + break; + case START_TLS: + final AbstractTlsDirContextAuthenticationStrategy tlsAuthenticationStrategy = new DefaultTlsDirContextAuthenticationStrategy(); + + // shutdown gracefully + final String rawShutdownGracefully = configurationContext.getProperty("TLS - Shutdown Gracefully"); + if (StringUtils.isNotBlank(rawShutdownGracefully)) { + final boolean shutdownGracefully = Boolean.TRUE.toString().equalsIgnoreCase(rawShutdownGracefully); + tlsAuthenticationStrategy.setShutdownTlsGracefully(shutdownGracefully); + } + + // get the configured ssl context + final SSLContext startTlsSslContext = getConfiguredSslContext(configurationContext); + if (startTlsSslContext != null) { + tlsAuthenticationStrategy.setSslSocketFactory(startTlsSslContext.getSocketFactory()); + } + + // set the authentication strategy + context.setAuthenticationStrategy(tlsAuthenticationStrategy); + break; + } + break; + } + + // referrals + final String rawReferralStrategy = configurationContext.getProperty("Referral Strategy"); + + final ReferralStrategy referralStrategy; + try { + referralStrategy = ReferralStrategy.valueOf(rawReferralStrategy); + } catch (final IllegalArgumentException iae) { + throw new ProviderCreationException(String.format("Unrecognized referral strategy '%s'. Possible values are [%s]", + rawReferralStrategy, StringUtils.join(ReferralStrategy.values(), ", "))); + } + + // using the value as this needs to be the lowercase version while the value is configured with the enum constant + context.setReferral(referralStrategy.getValue()); + + // url + final String urls = configurationContext.getProperty("Url"); + + if (StringUtils.isBlank(urls)) { + throw new ProviderCreationException("LDAP identity provider 'Url' must be specified."); + } + + // connection + context.setUrls(StringUtils.split(urls)); + + // search criteria + final String userSearchBase = configurationContext.getProperty("User Search Base"); + final String userSearchFilter = configurationContext.getProperty("User Search Filter"); + + if (StringUtils.isBlank(userSearchBase) || StringUtils.isBlank(userSearchFilter)) { + throw new ProviderCreationException("LDAP identity provider 'User Search Base' and 'User Search Filter' must be specified."); + } + + final LdapUserSearch userSearch = new FilterBasedLdapUserSearch(userSearchBase, userSearchFilter, context); + + // bind + final BindAuthenticator authenticator = new BindAuthenticator(context); + authenticator.setUserSearch(userSearch); + + // identity strategy + final String rawIdentityStrategy = configurationContext.getProperty("Identity Strategy"); + + if (StringUtils.isBlank(rawIdentityStrategy)) { + logger.info(String.format("Identity Strategy is not configured, defaulting strategy to %s.", IdentityStrategy.USE_DN)); + + // if this value is not configured, default to use dn which was the previous implementation + identityStrategy = IdentityStrategy.USE_DN; + } else { + try { + // attempt to get the configured identity strategy + identityStrategy = IdentityStrategy.valueOf(rawIdentityStrategy); + } catch (final IllegalArgumentException iae) { + throw new ProviderCreationException(String.format("Unrecognized identity strategy '%s'. Possible values are [%s]", + rawIdentityStrategy, StringUtils.join(IdentityStrategy.values(), ", "))); + } + } + + // set the base environment is necessary + if (!baseEnvironment.isEmpty()) { + context.setBaseEnvironmentProperties(baseEnvironment); + } + + try { + // handling initializing beans + context.afterPropertiesSet(); + authenticator.afterPropertiesSet(); + } catch (final Exception e) { + throw new ProviderCreationException(e.getMessage(), e); + } + + // create the underlying provider + provider = new LdapAuthenticationProvider(authenticator); + } + + private void setTimeout(final LoginIdentityProviderConfigurationContext configurationContext, + final Map baseEnvironment, + final String configurationProperty, + final String environmentKey) { + + final String rawTimeout = configurationContext.getProperty(configurationProperty); + if (StringUtils.isNotBlank(rawTimeout)) { + try { + final Long timeout = FormatUtils.getTimeDuration(rawTimeout, TimeUnit.MILLISECONDS); + baseEnvironment.put(environmentKey, timeout.toString()); + } catch (final IllegalArgumentException iae) { + throw new ProviderCreationException(String.format("The %s '%s' is not a valid time duration", configurationProperty, rawTimeout)); + } + } + } + + private SSLContext getConfiguredSslContext(final LoginIdentityProviderConfigurationContext configurationContext) { + final String rawKeystore = configurationContext.getProperty("TLS - Keystore"); + final String rawKeystorePassword = configurationContext.getProperty("TLS - Keystore Password"); + final String rawKeystoreType = configurationContext.getProperty("TLS - Keystore Type"); + final String rawTruststore = configurationContext.getProperty("TLS - Truststore"); + final String rawTruststorePassword = configurationContext.getProperty("TLS - Truststore Password"); + final String rawTruststoreType = configurationContext.getProperty("TLS - Truststore Type"); + final String rawClientAuth = configurationContext.getProperty("TLS - Client Auth"); + final String rawProtocol = configurationContext.getProperty("TLS - Protocol"); + + // create the ssl context + final SSLContext sslContext; + try { + if (StringUtils.isBlank(rawKeystore) && StringUtils.isBlank(rawTruststore)) { + sslContext = null; + } else { + // ensure the protocol is specified + if (StringUtils.isBlank(rawProtocol)) { + throw new ProviderCreationException("TLS - Protocol must be specified."); + } + + if (StringUtils.isBlank(rawKeystore)) { + sslContext = SslContextFactory.createTrustSslContext(rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, rawProtocol); + } else if (StringUtils.isBlank(rawTruststore)) { + sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType, rawProtocol); + } else { + // determine the client auth if specified + final ClientAuth clientAuth; + if (StringUtils.isBlank(rawClientAuth)) { + clientAuth = ClientAuth.NONE; + } else { + try { + clientAuth = ClientAuth.valueOf(rawClientAuth); + } catch (final IllegalArgumentException iae) { + throw new ProviderCreationException(String.format("Unrecognized client auth '%s'. Possible values are [%s]", + rawClientAuth, StringUtils.join(ClientAuth.values(), ", "))); + } + } + + sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType, + rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, clientAuth, rawProtocol); + } + } + } catch (final KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | KeyManagementException | IOException e) { + throw new ProviderCreationException(e.getMessage(), e); + } + + return sslContext; + } + + @Override + public final AuthenticationResponse authenticate(final LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException { + if (provider == null) { + throw new IdentityAccessException("The LDAP authentication provider is not initialized."); + } + + try { + // perform the authentication + final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword()); + final Authentication authentication = provider.authenticate(token); + + // use dn if configured + if (IdentityStrategy.USE_DN.equals(identityStrategy)) { + // attempt to get the ldap user details to get the DN + if (authentication.getPrincipal() instanceof LdapUserDetails) { + final LdapUserDetails userDetails = (LdapUserDetails) authentication.getPrincipal(); + return new AuthenticationResponse(userDetails.getDn(), credentials.getUsername(), expiration, issuer); + } else { + logger.warn(String.format("Unable to determine user DN for %s, using username.", authentication.getName())); + return new AuthenticationResponse(authentication.getName(), credentials.getUsername(), expiration, issuer); + } + } else { + return new AuthenticationResponse(authentication.getName(), credentials.getUsername(), expiration, issuer); + } + } catch (final BadCredentialsException | UsernameNotFoundException | AuthenticationException e) { + throw new InvalidLoginCredentialsException(e.getMessage(), e); + } catch (final Exception e) { + // there appears to be a bug that generates a InternalAuthenticationServiceException wrapped around an AuthenticationException. this + // shouldn't be the case as they the service exception suggestions that something was wrong with the service. while the authentication + // exception suggests that username and/or credentials were incorrect. checking the cause seems to address this scenario. + final Throwable cause = e.getCause(); + if (cause instanceof AuthenticationException) { + throw new InvalidLoginCredentialsException(e.getMessage(), e); + } + + logger.error(e.getMessage()); + if (logger.isDebugEnabled()) { + logger.debug(StringUtils.EMPTY, e); + } + throw new IdentityAccessException("Unable to validate the supplied credentials. Please contact the system administrator.", e); + } + } + + @Override + public final void preDestruction() throws ProviderDestructionException { + } + +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/LdapsSocketFactory.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/LdapsSocketFactory.java new file mode 100644 index 000000000..dff95726e --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/LdapsSocketFactory.java @@ -0,0 +1,106 @@ +/* + * 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.ldap; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +/** + * SSLSocketFactory used when connecting to a Directory Server over LDAPS. + */ +public class LdapsSocketFactory extends SSLSocketFactory { + + // singleton + private static LdapsSocketFactory instance; + + // delegate + private SSLSocketFactory delegate; + + /** + * Initializes the LdapsSocketFactory with the specified SSLSocketFactory. The specified + * socket factory will be used as a delegate for all subsequent instances of this class. + * + * @param sslSocketFactory delegate socket factory + */ + public static void initialize(final SSLSocketFactory sslSocketFactory) { + instance = new LdapsSocketFactory(sslSocketFactory); + } + + /** + * Gets the LdapsSocketFactory that was previously initialized. + * + * @return socket factory + */ + public static SocketFactory getDefault() { + return instance; + } + + /** + * Creates a new LdapsSocketFactory. + * + * @param sslSocketFactory delegate socket factory + */ + private LdapsSocketFactory(final SSLSocketFactory sslSocketFactory) { + delegate = sslSocketFactory; + } + + // delegate methods + + @Override + public String[] getSupportedCipherSuites() { + return delegate.getSupportedCipherSuites(); + } + + @Override + public String[] getDefaultCipherSuites() { + return delegate.getDefaultCipherSuites(); + } + + @Override + public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException { + return delegate.createSocket(socket, string, i, bln); + } + + @Override + public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException { + return delegate.createSocket(ia, i, ia1, i1); + } + + @Override + public Socket createSocket(InetAddress ia, int i) throws IOException { + return delegate.createSocket(ia, i); + } + + @Override + public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException { + return delegate.createSocket(string, i, ia, i1); + } + + @Override + public Socket createSocket(String string, int i) throws IOException, UnknownHostException { + return delegate.createSocket(string, i); + } + + @Override + public Socket createSocket() throws IOException { + return delegate.createSocket(); + } +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/ReferralStrategy.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/ReferralStrategy.java new file mode 100644 index 000000000..4258cdede --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/ReferralStrategy.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.security.ldap; + +public enum ReferralStrategy { + + FOLLOW("follow"), + IGNORE("ignore"), + THROW("throw"); + + private final String value; + + private ReferralStrategy(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProvider.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProvider.java new file mode 100644 index 000000000..af10ece41 --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProvider.java @@ -0,0 +1,750 @@ +/* + * 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.ldap.tenants; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.properties.NiFiRegistryProperties; +import org.apache.nifi.registry.properties.util.IdentityMapping; +import org.apache.nifi.registry.properties.util.IdentityMappingUtil; +import org.apache.nifi.registry.security.authentication.exception.ProviderDestructionException; +import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.registry.security.authorization.Group; +import org.apache.nifi.registry.security.authorization.User; +import org.apache.nifi.registry.security.authorization.UserAndGroups; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.annotation.AuthorizerContext; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.ldap.LdapAuthenticationStrategy; +import org.apache.nifi.registry.security.ldap.LdapsSocketFactory; +import org.apache.nifi.registry.security.ldap.ReferralStrategy; +import org.apache.nifi.registry.security.util.SslContextFactory; +import org.apache.nifi.registry.security.util.SslContextFactory.ClientAuth; +import org.apache.nifi.registry.util.FormatUtils; +import org.apache.nifi.registry.util.PropertyValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ldap.control.PagedResultsDirContextProcessor; +import org.springframework.ldap.core.ContextSource; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DirContextOperations; +import org.springframework.ldap.core.DirContextProcessor; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.LdapTemplate.NullDirContextProcessor; +import org.springframework.ldap.core.support.AbstractContextMapper; +import org.springframework.ldap.core.support.AbstractTlsDirContextAuthenticationStrategy; +import org.springframework.ldap.core.support.DefaultTlsDirContextAuthenticationStrategy; +import org.springframework.ldap.core.support.LdapContextSource; +import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy; +import org.springframework.ldap.core.support.SingleContextSource; +import org.springframework.ldap.filter.AndFilter; +import org.springframework.ldap.filter.EqualsFilter; +import org.springframework.ldap.filter.HardcodedFilter; + +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.SearchControls; +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Abstract LDAP based implementation of a login identity provider. + */ +public class LdapUserGroupProvider implements UserGroupProvider { + + private static final Logger logger = LoggerFactory.getLogger(LdapUserGroupProvider.class); + + public static final String PROP_CONNECT_TIMEOUT = "Connect Timeout"; + public static final String PROP_READ_TIMEOUT = "Read Timeout"; + public static final String PROP_AUTHENTICATION_STRATEGY = "Authentication Strategy"; + public static final String PROP_MANAGER_DN = "Manager DN"; + public static final String PROP_MANAGER_PASSWORD = "Manager Password"; + public static final String PROP_REFERRAL_STRATEGY = "Referral Strategy"; + public static final String PROP_URL = "Url"; + public static final String PROP_PAGE_SIZE = "Page Size"; + + public static final String PROP_USER_SEARCH_BASE = "User Search Base"; + public static final String PROP_USER_OBJECT_CLASS = "User Object Class"; + public static final String PROP_USER_SEARCH_SCOPE = "User Search Scope"; + public static final String PROP_USER_SEARCH_FILTER = "User Search Filter"; + public static final String PROP_USER_IDENTITY_ATTRIBUTE = "User Identity Attribute"; + public static final String PROP_USER_GROUP_ATTRIBUTE = "User Group Name Attribute"; + + public static final String PROP_GROUP_SEARCH_BASE = "Group Search Base"; + public static final String PROP_GROUP_OBJECT_CLASS = "Group Object Class"; + public static final String PROP_GROUP_SEARCH_SCOPE = "Group Search Scope"; + public static final String PROP_GROUP_SEARCH_FILTER = "Group Search Filter"; + public static final String PROP_GROUP_NAME_ATTRIBUTE = "Group Name Attribute"; + public static final String PROP_GROUP_MEMBER_ATTRIBUTE = "Group Member Attribute"; + + public static final String PROP_SYNC_INTERVAL = "Sync Interval"; + + //private AuthorizerConfigurationContext configurationContext; + + private List identityMappings; + private NiFiRegistryProperties properties; + + private ScheduledExecutorService ldapSync; + private AtomicReference tenants = new AtomicReference<>(null); + + private String userSearchBase; + private SearchScope userSearchScope; + private String userSearchFilter; + private String userIdentityAttribute; + private String userObjectClass; + private String userGroupNameAttribute; + private boolean useDnForUserIdentity; + private boolean performUserSearch; + + private String groupSearchBase; + private SearchScope groupSearchScope; + private String groupSearchFilter; + private String groupMemberAttribute; + private String groupNameAttribute; + private String groupObjectClass; + private boolean useDnForGroupName; + private boolean performGroupSearch; + + private Integer pageSize; + + @Override + public void initialize(final UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException { + ldapSync = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { + final ThreadFactory factory = Executors.defaultThreadFactory(); + + @Override + public Thread newThread(Runnable r) { + final Thread thread = factory.newThread(r); + thread.setName(String.format("%s (%s) - background sync thread", getClass().getSimpleName(), initializationContext.getIdentifier())); + return thread; + } + }); + } + + @Override + public void onConfigured(final AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + final LdapContextSource context = new LdapContextSource(); + + final Map baseEnvironment = new HashMap<>(); + + // connect/read time out + setTimeout(configurationContext, baseEnvironment, PROP_CONNECT_TIMEOUT, "com.sun.jndi.ldap.connect.timeout"); + setTimeout(configurationContext, baseEnvironment, PROP_READ_TIMEOUT, "com.sun.jndi.ldap.read.timeout"); + + // authentication strategy + final PropertyValue rawAuthenticationStrategy = configurationContext.getProperty(PROP_AUTHENTICATION_STRATEGY); + final LdapAuthenticationStrategy authenticationStrategy; + try { + authenticationStrategy = LdapAuthenticationStrategy.valueOf(rawAuthenticationStrategy.getValue()); + } catch (final IllegalArgumentException iae) { + throw new AuthorizerCreationException(String.format("Unrecognized authentication strategy '%s'. Possible values are [%s]", + rawAuthenticationStrategy.getValue(), StringUtils.join(LdapAuthenticationStrategy.values(), ", "))); + } + + switch (authenticationStrategy) { + case ANONYMOUS: + context.setAnonymousReadOnly(true); + break; + default: + final String userDn = configurationContext.getProperty(PROP_MANAGER_DN).getValue(); + final String password = configurationContext.getProperty(PROP_MANAGER_PASSWORD).getValue(); + + context.setUserDn(userDn); + context.setPassword(password); + + switch (authenticationStrategy) { + case SIMPLE: + context.setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy()); + break; + case LDAPS: + context.setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy()); + + // indicate a secure connection + baseEnvironment.put(Context.SECURITY_PROTOCOL, "ssl"); + + // get the configured ssl context + final SSLContext ldapsSslContext = getConfiguredSslContext(configurationContext); + if (ldapsSslContext != null) { + // initialize the ldaps socket factory prior to use + LdapsSocketFactory.initialize(ldapsSslContext.getSocketFactory()); + baseEnvironment.put("java.naming.ldap.factory.socket", LdapsSocketFactory.class.getName()); + } + break; + case START_TLS: + final AbstractTlsDirContextAuthenticationStrategy tlsAuthenticationStrategy = new DefaultTlsDirContextAuthenticationStrategy(); + + // shutdown gracefully + final String rawShutdownGracefully = configurationContext.getProperty("TLS - Shutdown Gracefully").getValue(); + if (StringUtils.isNotBlank(rawShutdownGracefully)) { + final boolean shutdownGracefully = Boolean.TRUE.toString().equalsIgnoreCase(rawShutdownGracefully); + tlsAuthenticationStrategy.setShutdownTlsGracefully(shutdownGracefully); + } + + // get the configured ssl context + final SSLContext startTlsSslContext = getConfiguredSslContext(configurationContext); + if (startTlsSslContext != null) { + tlsAuthenticationStrategy.setSslSocketFactory(startTlsSslContext.getSocketFactory()); + } + + // set the authentication strategy + context.setAuthenticationStrategy(tlsAuthenticationStrategy); + break; + } + break; + } + + // referrals + final String rawReferralStrategy = configurationContext.getProperty(PROP_REFERRAL_STRATEGY).getValue(); + + final ReferralStrategy referralStrategy; + try { + referralStrategy = ReferralStrategy.valueOf(rawReferralStrategy); + } catch (final IllegalArgumentException iae) { + throw new AuthorizerCreationException(String.format("Unrecognized referral strategy '%s'. Possible values are [%s]", + rawReferralStrategy, StringUtils.join(ReferralStrategy.values(), ", "))); + } + + // using the value as this needs to be the lowercase version while the value is configured with the enum constant + context.setReferral(referralStrategy.getValue()); + + // url + final String urls = configurationContext.getProperty(PROP_URL).getValue(); + + if (StringUtils.isBlank(urls)) { + throw new AuthorizerCreationException("LDAP identity provider 'Url' must be specified."); + } + + // connection + context.setUrls(StringUtils.split(urls)); + + // raw user search base + final PropertyValue rawUserSearchBase = configurationContext.getProperty(PROP_USER_SEARCH_BASE); + final PropertyValue rawUserObjectClass = configurationContext.getProperty(PROP_USER_OBJECT_CLASS); + final PropertyValue rawUserSearchScope = configurationContext.getProperty(PROP_USER_SEARCH_SCOPE); + + // if loading the users, ensure the object class set + if (rawUserSearchBase.isSet() && !rawUserObjectClass.isSet()) { + throw new AuthorizerCreationException("LDAP user group provider 'User Object Class' must be specified when 'User Search Base' is set."); + } + + // if loading the users, ensure the search scope is set + if (rawUserSearchBase.isSet() && !rawUserSearchScope.isSet()) { + throw new AuthorizerCreationException("LDAP user group provider 'User Search Scope' must be specified when 'User Search Base' is set."); + } + + // user search criteria + userSearchBase = rawUserSearchBase.getValue(); + userObjectClass = rawUserObjectClass.getValue(); + userSearchFilter = configurationContext.getProperty(PROP_USER_SEARCH_FILTER).getValue(); + userIdentityAttribute = configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE).getValue(); + userGroupNameAttribute = configurationContext.getProperty(PROP_USER_GROUP_ATTRIBUTE).getValue(); + + try { + userSearchScope = SearchScope.valueOf(rawUserSearchScope.getValue()); + } catch (final IllegalArgumentException iae) { + throw new AuthorizerCreationException(String.format("Unrecognized user search scope '%s'. Possible values are [%s]", + rawUserSearchScope.getValue(), StringUtils.join(SearchScope.values(), ", "))); + } + + // determine user behavior + useDnForUserIdentity = StringUtils.isBlank(userIdentityAttribute); + performUserSearch = StringUtils.isNotBlank(userSearchBase); + + // raw group search criteria + final PropertyValue rawGroupSearchBase = configurationContext.getProperty(PROP_GROUP_SEARCH_BASE); + final PropertyValue rawGroupObjectClass = configurationContext.getProperty(PROP_GROUP_OBJECT_CLASS); + final PropertyValue rawGroupSearchScope = configurationContext.getProperty(PROP_GROUP_SEARCH_SCOPE); + + // if loading the groups, ensure the object class is set + if (rawGroupSearchBase.isSet() && !rawGroupObjectClass.isSet()) { + throw new AuthorizerCreationException("LDAP user group provider 'Group Object Class' must be specified when 'Group Search Base' is set."); + } + + // if loading the groups, ensure the search scope is set + if (rawGroupSearchBase.isSet() && !rawGroupSearchScope.isSet()) { + throw new AuthorizerCreationException("LDAP user group provider 'Group Search Scope' must be specified when 'Group Search Base' is set."); + } + + // group search criteria + groupSearchBase = rawGroupSearchBase.getValue(); + groupObjectClass = rawGroupObjectClass.getValue(); + groupSearchFilter = configurationContext.getProperty(PROP_GROUP_SEARCH_FILTER).getValue(); + groupNameAttribute = configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE).getValue(); + groupMemberAttribute = configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE).getValue(); + + try { + groupSearchScope = SearchScope.valueOf(rawGroupSearchScope.getValue()); + } catch (final IllegalArgumentException iae) { + throw new AuthorizerCreationException(String.format("Unrecognized group search scope '%s'. Possible values are [%s]", + rawGroupSearchScope.getValue(), StringUtils.join(SearchScope.values(), ", "))); + } + + // determine group behavior + useDnForGroupName = StringUtils.isBlank(groupNameAttribute); + performGroupSearch = StringUtils.isNotBlank(groupSearchBase); + + // ensure we are either searching users or groups (at least one must be specified) + if (!performUserSearch && !performGroupSearch) { + throw new AuthorizerCreationException("LDAP user group provider 'User Search Base' or 'Group Search Base' must be specified."); + } + + // ensure group member attribute is set if searching groups but not users + if (performGroupSearch && !performUserSearch && StringUtils.isBlank(groupMemberAttribute)) { + throw new AuthorizerCreationException("'Group Member Attribute' is required when searching groups but not users."); + } + + // get the page size if configured + final PropertyValue rawPageSize = configurationContext.getProperty(PROP_PAGE_SIZE); + if (rawPageSize.isSet() && StringUtils.isNotBlank(rawPageSize.getValue())) { + pageSize = rawPageSize.asInteger(); + } + + // extract the identity mappings from nifi.properties if any are provided + identityMappings = Collections.unmodifiableList(IdentityMappingUtil.getIdentityMappings(properties)); + + // set the base environment is necessary + if (!baseEnvironment.isEmpty()) { + context.setBaseEnvironmentProperties(baseEnvironment); + } + + try { + // handling initializing beans + context.afterPropertiesSet(); + } catch (final Exception e) { + throw new AuthorizerCreationException(e.getMessage(), e); + } + + final PropertyValue rawSyncInterval = configurationContext.getProperty(PROP_SYNC_INTERVAL); + final long syncInterval; + if (rawSyncInterval.isSet()) { + try { + syncInterval = FormatUtils.getTimeDuration(rawSyncInterval.getValue(), TimeUnit.MILLISECONDS); + } catch (final IllegalArgumentException iae) { + throw new AuthorizerCreationException(String.format("The %s '%s' is not a valid time duration", PROP_SYNC_INTERVAL, rawSyncInterval.getValue())); + } + } else { + throw new AuthorizerCreationException("The 'Sync Interval' must be specified."); + } + + try { + // perform the initial load, tenants must be loaded as the configured UserGroupProvider is supplied + // to the AccessPolicyProvider for granting initial permissions + load(context); + + // ensure the tenants were successfully synced + if (tenants.get() == null) { + throw new AuthorizerCreationException("Unable to sync users and groups."); + } + + // schedule the background thread to load the users/groups + ldapSync.scheduleWithFixedDelay(() -> load(context), syncInterval, syncInterval, TimeUnit.SECONDS); + } catch (final AuthorizationAccessException e) { + throw new AuthorizerCreationException(e); + } + } + + @Override + public Set getUsers() throws AuthorizationAccessException { + return tenants.get().getAllUsers(); + } + + @Override + public User getUser(String identifier) throws AuthorizationAccessException { + return tenants.get().getUsersById().get(identifier); + } + + @Override + public User getUserByIdentity(String identity) throws AuthorizationAccessException { + return tenants.get().getUser(identity); + } + + @Override + public Set getGroups() throws AuthorizationAccessException { + return tenants.get().getAllGroups(); + } + + @Override + public Group getGroup(String identifier) throws AuthorizationAccessException { + return tenants.get().getGroupsById().get(identifier); + } + + @Override + public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException { + final TenantHolder holder = tenants.get(); + return new UserAndGroups() { + @Override + public User getUser() { + return holder.getUser(identity); + } + + @Override + public Set getGroups() { + return holder.getGroups(identity); + } + }; + } + + /** + * Reloads the tenants. + */ + private void load(final ContextSource contextSource) { + // create the ldapTemplate based on the context source. use a single source context to use the same connection + // to support paging when configured + final SingleContextSource singleContextSource = new SingleContextSource(contextSource.getReadOnlyContext()); + final LdapTemplate ldapTemplate = new LdapTemplate(singleContextSource); + + try { + final List userList = new ArrayList<>(); + final List groupList = new ArrayList<>(); + + // group dn -> user identifiers lookup + final Map> groupDnToUserIdentifierMappings = new HashMap<>(); + + // user dn -> user lookup + final Map userDnLookup = new HashMap<>(); + + if (performUserSearch) { + // search controls + final SearchControls userControls = new SearchControls(); + userControls.setSearchScope(userSearchScope.ordinal()); + + // consider paging support for users + final DirContextProcessor userProcessor; + if (pageSize == null) { + userProcessor = new NullDirContextProcessor(); + } else { + userProcessor = new PagedResultsDirContextProcessor(pageSize); + } + + // looking for objects matching the user object class + final AndFilter userFilter = new AndFilter(); + userFilter.and(new EqualsFilter("objectClass", userObjectClass)); + + // if a filter has been provided by the user, we add it to the filter + if (StringUtils.isNotBlank(userSearchFilter)) { + userFilter.and(new HardcodedFilter(userSearchFilter)); + } + + do { + userList.addAll(ldapTemplate.search(userSearchBase, userFilter.encode(), userControls, new AbstractContextMapper() { + @Override + protected User doMapFromContext(DirContextOperations ctx) { + final String dn = ctx.getDn().toString(); + + // get the user identity + final String identity = getUserIdentity(ctx); + + // build the user + final User user = new User.Builder().identifierGenerateFromSeed(identity).identity(identity).build(); + userDnLookup.put(dn, user); + + if (StringUtils.isNotBlank(userGroupNameAttribute)) { + final Attribute attributeGroups = ctx.getAttributes().get(userGroupNameAttribute); + + if (attributeGroups == null) { + logger.warn("User group name attribute [" + userGroupNameAttribute + "] does not exist. Ignoring group membership."); + } else { + try { + final NamingEnumeration groupDns = (NamingEnumeration) attributeGroups.getAll(); + while (groupDns.hasMoreElements()) { + // store the group dn -> user identifier mapping + groupDnToUserIdentifierMappings.computeIfAbsent(groupDns.next(), g -> new HashSet<>()).add(user.getIdentifier()); + } + } catch (NamingException e) { + throw new AuthorizationAccessException("Error while retrieving user group name attribute [" + userIdentityAttribute + "]."); + } + } + } + + return user; + } + }, userProcessor)); + } while (hasMorePages(userProcessor)); + } + + if (performGroupSearch) { + final SearchControls groupControls = new SearchControls(); + groupControls.setSearchScope(groupSearchScope.ordinal()); + + // consider paging support for groups + final DirContextProcessor groupProcessor; + if (pageSize == null) { + groupProcessor = new NullDirContextProcessor(); + } else { + groupProcessor = new PagedResultsDirContextProcessor(pageSize); + } + + // looking for objects matching the group object class + AndFilter groupFilter = new AndFilter(); + groupFilter.and(new EqualsFilter("objectClass", groupObjectClass)); + + // if a filter has been provided by the user, we add it to the filter + if(StringUtils.isNotBlank(groupSearchFilter)) { + groupFilter.and(new HardcodedFilter(groupSearchFilter)); + } + + do { + groupList.addAll(ldapTemplate.search(groupSearchBase, groupFilter.encode(), groupControls, new AbstractContextMapper() { + @Override + protected Group doMapFromContext(DirContextOperations ctx) { + final String dn = ctx.getDn().toString(); + + // get the group identity + final String name = getGroupName(ctx); + + if (!StringUtils.isBlank(groupMemberAttribute)) { + Attribute attributeUsers = ctx.getAttributes().get(groupMemberAttribute); + if (attributeUsers == null) { + logger.warn("Group member attribute [" + groupMemberAttribute + "] does not exist. Ignoring group membership."); + } else { + try { + final NamingEnumeration userDns = (NamingEnumeration) attributeUsers.getAll(); + while (userDns.hasMoreElements()) { + final String userDn = userDns.next(); + + if (performUserSearch) { + // find the user by dn add the identifier to this group + final User user = userDnLookup.get(userDn); + + // ensure the user is known + if (user != null) { + groupDnToUserIdentifierMappings.computeIfAbsent(dn, g -> new HashSet<>()).add(user.getIdentifier()); + } else { + logger.warn(String.format("%s contains member %s but that user was not found while searching users. Ignoring group membership.", name, userDn)); + } + } else { + final String userIdentity; + if (useDnForUserIdentity) { + // use the dn to avoid the unnecessary look up + userIdentity = userDn; + } else { + // lookup the user to extract the user identity + userIdentity = getUserIdentity((DirContextAdapter) ldapTemplate.lookup(userDn)); + } + + // build the user + final User user = new User.Builder().identifierGenerateFromSeed(userIdentity).identity(userIdentity).build(); + + // add this user + userList.add(user); + groupDnToUserIdentifierMappings.computeIfAbsent(dn, g -> new HashSet<>()).add(user.getIdentifier()); + } + } + } catch (NamingException e) { + throw new AuthorizationAccessException("Error while retrieving group name attribute [" + groupNameAttribute + "]."); + } + } + } + + // build this group + final Group.Builder groupBuilder = new Group.Builder().identifierGenerateFromSeed(name).name(name); + + // add all users that were associated with this group dn + if (groupDnToUserIdentifierMappings.containsKey(dn)) { + groupDnToUserIdentifierMappings.remove(dn).forEach(userIdentifier -> groupBuilder.addUser(userIdentifier)); + } + + return groupBuilder.build(); + } + }, groupProcessor)); + } while (hasMorePages(groupProcessor)); + + // any remaining groupDn's were referenced by a user but not found while searching groups + groupDnToUserIdentifierMappings.forEach((groupDn, userIdentifiers) -> { + logger.warn(String.format("[%s] are members of %s but that group was not found while searching users. Ignoring group membership.", + StringUtils.join(userIdentifiers, ", "), groupDn)); + }); + } else { + // groups are not being searched so lookup any groups identified while searching users + groupDnToUserIdentifierMappings.forEach((groupDn, userIdentifiers) -> { + final String groupName; + if (useDnForGroupName) { + // use the dn to avoid the unnecessary look up + groupName = groupDn; + } else { + groupName = getGroupName((DirContextAdapter) ldapTemplate.lookup(groupDn)); + } + + // define the group + final Group.Builder groupBuilder = new Group.Builder().identifierGenerateFromSeed(groupName).name(groupName); + + // add each user + userIdentifiers.forEach(userIdentifier -> groupBuilder.addUser(userIdentifier)); + + // build the group + groupList.add(groupBuilder.build()); + }); + } + + // record the updated tenants + tenants.set(new TenantHolder(new HashSet<>(userList), new HashSet<>(groupList))); + } finally { + singleContextSource.destroy(); + } + } + + private boolean hasMorePages(final DirContextProcessor processor ) { + return processor instanceof PagedResultsDirContextProcessor && ((PagedResultsDirContextProcessor) processor).hasMore(); + } + + private String getUserIdentity(final DirContextOperations ctx) { + final String identity; + + if (useDnForUserIdentity) { + identity = ctx.getDn().toString(); + } else { + final Attribute attributeName = ctx.getAttributes().get(userIdentityAttribute); + if (attributeName == null) { + throw new AuthorizationAccessException("User identity attribute [" + userIdentityAttribute + "] does not exist."); + } + + try { + identity = (String) attributeName.get(); + } catch (NamingException e) { + throw new AuthorizationAccessException("Error while retrieving user name attribute [" + userIdentityAttribute + "]."); + } + } + + return IdentityMappingUtil.mapIdentity(identity, identityMappings); + } + + private String getGroupName(final DirContextOperations ctx) { + final String name; + + if (useDnForGroupName) { + name = ctx.getDn().toString(); + } else { + final Attribute attributeName = ctx.getAttributes().get(groupNameAttribute); + if (attributeName == null) { + throw new AuthorizationAccessException("Group identity attribute [" + groupNameAttribute + "] does not exist."); + } + + try { + name = (String) attributeName.get(); + } catch (NamingException e) { + throw new AuthorizationAccessException("Error while retrieving group name attribute [" + groupNameAttribute + "]."); + } + } + + return name; + } + + @AuthorizerContext + public void setNiFiProperties(NiFiRegistryProperties properties) { + this.properties = properties; + } + + @Override + public final void preDestruction() throws ProviderDestructionException { + ldapSync.shutdown(); + try { + if (!ldapSync.awaitTermination(10000, TimeUnit.MILLISECONDS)) { + logger.info("Failed to stop ldap sync thread in 10 sec. Terminating"); + ldapSync.shutdownNow(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private void setTimeout(final AuthorizerConfigurationContext configurationContext, + final Map baseEnvironment, + final String configurationProperty, + final String environmentKey) { + + final PropertyValue rawTimeout = configurationContext.getProperty(configurationProperty); + if (rawTimeout.isSet()) { + try { + final Long timeout = FormatUtils.getTimeDuration(rawTimeout.getValue(), TimeUnit.MILLISECONDS); + baseEnvironment.put(environmentKey, timeout.toString()); + } catch (final IllegalArgumentException iae) { + throw new AuthorizerCreationException(String.format("The %s '%s' is not a valid time duration", configurationProperty, rawTimeout)); + } + } + } + + private SSLContext getConfiguredSslContext(final AuthorizerConfigurationContext configurationContext) { + final String rawKeystore = configurationContext.getProperty("TLS - Keystore").getValue(); + final String rawKeystorePassword = configurationContext.getProperty("TLS - Keystore Password").getValue(); + final String rawKeystoreType = configurationContext.getProperty("TLS - Keystore Type").getValue(); + final String rawTruststore = configurationContext.getProperty("TLS - Truststore").getValue(); + final String rawTruststorePassword = configurationContext.getProperty("TLS - Truststore Password").getValue(); + final String rawTruststoreType = configurationContext.getProperty("TLS - Truststore Type").getValue(); + final String rawClientAuth = configurationContext.getProperty("TLS - Client Auth").getValue(); + final String rawProtocol = configurationContext.getProperty("TLS - Protocol").getValue(); + + // create the ssl context + final SSLContext sslContext; + try { + if (StringUtils.isBlank(rawKeystore) && StringUtils.isBlank(rawTruststore)) { + sslContext = null; + } else { + // ensure the protocol is specified + if (StringUtils.isBlank(rawProtocol)) { + throw new AuthorizerCreationException("TLS - Protocol must be specified."); + } + + if (StringUtils.isBlank(rawKeystore)) { + sslContext = SslContextFactory.createTrustSslContext(rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, rawProtocol); + } else if (StringUtils.isBlank(rawTruststore)) { + sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType, rawProtocol); + } else { + // determine the client auth if specified + final ClientAuth clientAuth; + if (StringUtils.isBlank(rawClientAuth)) { + clientAuth = ClientAuth.NONE; + } else { + try { + clientAuth = ClientAuth.valueOf(rawClientAuth); + } catch (final IllegalArgumentException iae) { + throw new AuthorizerCreationException(String.format("Unrecognized client auth '%s'. Possible values are [%s]", + rawClientAuth, StringUtils.join(ClientAuth.values(), ", "))); + } + } + + sslContext = SslContextFactory.createSslContext(rawKeystore, rawKeystorePassword.toCharArray(), rawKeystoreType, + rawTruststore, rawTruststorePassword.toCharArray(), rawTruststoreType, clientAuth, rawProtocol); + } + } + } catch (final KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | KeyManagementException | IOException e) { + throw new AuthorizerCreationException(e.getMessage(), e); + } + + return sslContext; + } + +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/SearchScope.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/SearchScope.java new file mode 100644 index 000000000..2e5e8a209 --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/SearchScope.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.security.ldap.tenants; + +/** + * Scope for searching a directory server. + */ +public enum SearchScope { + + OBJECT, + ONE_LEVEL, + SUBTREE; + +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/TenantHolder.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/TenantHolder.java new file mode 100644 index 000000000..7ef3a8c01 --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/ldap/tenants/TenantHolder.java @@ -0,0 +1,165 @@ +/* + * 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.ldap.tenants; + + +import org.apache.nifi.registry.security.authorization.Group; +import org.apache.nifi.registry.security.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 TenantHolder { + + private final Set allUsers; + private final Map usersById; + private final Map usersByIdentity; + + private final Set allGroups; + private final Map groupsById; + private final Map> groupsByUserIdentity; + + /** + * Creates a new holder and populates all convenience data structures. + */ + public TenantHolder(final Set allUsers, final Set allGroups) { + // create a convenience map to retrieve a user by id + final Map userByIdMap = Collections.unmodifiableMap(createUserByIdMap(allUsers)); + + // create a convenience map to retrieve a user by identity + final Map userByIdentityMap = Collections.unmodifiableMap(createUserByIdentityMap(allUsers)); + + // create a convenience map to retrieve a group by id + final Map groupByIdMap = Collections.unmodifiableMap(createGroupByIdMap(allGroups)); + + // create a convenience map to retrieve the groups for a user identity + final Map> 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 Map from user identifier to User. + * + * @param users the set of all users + * @return the Map from user identifier to User + */ + private Map createUserByIdMap(final Set users) { + Map 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 createUserByIdentityMap(final Set users) { + Map 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 createGroupByIdMap(final Set groups) { + Map 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> createGroupsByUserIdentityMap(final Set groups, final Set users) { + Map> groupsByUserIdentity = new HashMap<>(); + + for (User user : users) { + Set 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; + } + + Set getAllUsers() { + return allUsers; + } + + Map getUsersById() { + return usersById; + } + + Set getAllGroups() { + return allGroups; + } + + Map getGroupsById() { + return groupsById; + } + + public User getUser(String identity) { + if (identity == null) { + throw new IllegalArgumentException("Identity cannot be null"); + } + return usersByIdentity.get(identity); + } + + public Set getGroups(String userIdentity) { + if (userIdentity == null) { + throw new IllegalArgumentException("User Identity cannot be null"); + } + return groupsByUserIdentity.get(userIdentity); + } + +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/util/XmlUtils.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/util/XmlUtils.java new file mode 100644 index 000000000..2caa8faa4 --- /dev/null +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/util/XmlUtils.java @@ -0,0 +1,44 @@ +/* + * 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.util; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.stream.StreamSource; +import java.io.InputStream; + +public class XmlUtils { + + public static XMLStreamReader createSafeReader(InputStream inputStream) throws XMLStreamException { + if (inputStream == null) { + throw new IllegalArgumentException("The provided input stream cannot be null"); + } + return createSafeReader(new StreamSource(inputStream)); + } + + public static XMLStreamReader createSafeReader(StreamSource source) throws XMLStreamException { + if (source == null) { + throw new IllegalArgumentException("The provided source cannot be null"); + } + + XMLInputFactory xif = XMLInputFactory.newFactory(); + xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); + xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); + return xif.createXMLStreamReader(source); + } +} diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java index 48a7ae36c..2567b55c2 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java @@ -16,28 +16,28 @@ */ package org.apache.nifi.registry.service; -import org.apache.nifi.registry.authorization.AccessPolicyProvider; -import org.apache.nifi.registry.authorization.AccessPolicyProviderInitializationContext; -import org.apache.nifi.registry.authorization.AuthorizableLookup; -import org.apache.nifi.registry.authorization.AuthorizeAccess; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.AuthorizerCapabilityDetection; -import org.apache.nifi.registry.authorization.AuthorizerConfigurationContext; -import org.apache.nifi.registry.authorization.ConfigurableAccessPolicyProvider; -import org.apache.nifi.registry.authorization.ConfigurableUserGroupProvider; -import org.apache.nifi.registry.authorization.Group; -import org.apache.nifi.registry.authorization.ManagedAuthorizer; -import org.apache.nifi.registry.authorization.RequestAction; -import org.apache.nifi.registry.authorization.UserAndGroups; -import org.apache.nifi.registry.authorization.UserGroupProvider; -import org.apache.nifi.registry.authorization.UserGroupProviderInitializationContext; -import org.apache.nifi.registry.authorization.exception.AccessDeniedException; -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 org.apache.nifi.registry.authorization.resource.ResourceFactory; -import org.apache.nifi.registry.authorization.resource.ResourceType; -import org.apache.nifi.registry.authorization.user.NiFiUserUtils; +import org.apache.nifi.registry.security.authorization.AccessPolicyProvider; +import org.apache.nifi.registry.security.authorization.AccessPolicyProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.AuthorizableLookup; +import org.apache.nifi.registry.security.authorization.AuthorizeAccess; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.AuthorizerCapabilityDetection; +import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; +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.ManagedAuthorizer; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.UserAndGroups; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestructionException; +import org.apache.nifi.registry.security.authorization.resource.ResourceFactory; +import org.apache.nifi.registry.security.authorization.resource.ResourceType; +import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils; import org.apache.nifi.registry.bucket.Bucket; import org.apache.nifi.registry.model.authorization.AccessPolicy; import org.apache.nifi.registry.model.authorization.AccessPolicySummary; @@ -105,11 +105,11 @@ public void authorizeAccess(final AuthorizeAccess authorizeAccess) { public Tenant getTenant(String identifier) { this.readLock.lock(); try { - org.apache.nifi.registry.authorization.User user = userGroupProvider.getUser(identifier); + org.apache.nifi.registry.security.authorization.User user = userGroupProvider.getUser(identifier); if (user != null) { return tenantToDTO(user); } else { - org.apache.nifi.registry.authorization.Group group = userGroupProvider.getGroup(identifier); + org.apache.nifi.registry.security.authorization.Group group = userGroupProvider.getGroup(identifier); return tenantToDTO(group); } } finally { @@ -124,7 +124,7 @@ public User createUser(User user) { verifyUserGroupProviderIsConfigurable(); writeLock.lock(); try { - final org.apache.nifi.registry.authorization.User createdUser = + final org.apache.nifi.registry.security.authorization.User createdUser = ((ConfigurableUserGroupProvider) userGroupProvider).addUser(userFromDTO(user)); return userToDTO(createdUser); } finally { @@ -154,7 +154,7 @@ public User updateUser(User user) { verifyUserGroupProviderIsConfigurable(); this.writeLock.lock(); try { - final org.apache.nifi.registry.authorization.User updatedUser = + final org.apache.nifi.registry.security.authorization.User updatedUser = ((ConfigurableUserGroupProvider) userGroupProvider).updateUser(userFromDTO(user)); return userToDTO(updatedUser); } finally { @@ -181,7 +181,7 @@ public UserGroup createUserGroup(UserGroup userGroup) { verifyUserGroupProviderIsConfigurable(); writeLock.lock(); try { - final org.apache.nifi.registry.authorization.Group createdGroup = + final org.apache.nifi.registry.security.authorization.Group createdGroup = ((ConfigurableUserGroupProvider) userGroupProvider).addGroup(userGroupFromDTO(userGroup)); return userGroupToDTO(createdGroup); } finally { @@ -198,18 +198,18 @@ public List getUserGroups() { } } - private List getUserGroupsForUser(String userIdentifier) { - this.readLock.lock(); - try { - return userGroupProvider.getGroups() - .stream() - .filter(group -> group.getUsers().contains(userIdentifier)) - .map(this::userGroupToDTO) - .collect(Collectors.toList()); - } finally { - this.readLock.unlock(); - } - } +// private List getUserGroupsForUser(String userIdentifier) { +// this.readLock.lock(); +// try { +// return userGroupProvider.getGroups() +// .stream() +// .filter(group -> group.getUsers().contains(userIdentifier)) +// .map(this::userGroupToDTO) +// .collect(Collectors.toList()); +// } finally { +// this.readLock.unlock(); +// } +// } public UserGroup getUserGroup(String identifier) { this.readLock.lock(); @@ -224,7 +224,7 @@ public UserGroup updateUserGroup(UserGroup userGroup) { verifyUserGroupProviderIsConfigurable(); writeLock.lock(); try { - final org.apache.nifi.registry.authorization.Group updatedGroup = + final org.apache.nifi.registry.security.authorization.Group updatedGroup = ((ConfigurableUserGroupProvider) userGroupProvider).updateGroup(userGroupFromDTO(userGroup)); return userGroupToDTO(updatedGroup); } finally { @@ -251,7 +251,7 @@ public AccessPolicy createAccessPolicy(AccessPolicy accessPolicy) { verifyAccessPolicyProviderIsConfigurable(); writeLock.lock(); try { - org.apache.nifi.registry.authorization.AccessPolicy createdAccessPolicy = + org.apache.nifi.registry.security.authorization.AccessPolicy createdAccessPolicy = ((ConfigurableAccessPolicyProvider) accessPolicyProvider).addAccessPolicy(accessPolicyFromDTO(accessPolicy)); return accessPolicyToDTO(createdAccessPolicy); } finally { @@ -336,12 +336,12 @@ public AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) { writeLock.lock(); try { // Don't allow changing action or resource of existing policy (should only be adding/removing users/groups) - org.apache.nifi.registry.authorization.AccessPolicy currentAccessPolicy = + org.apache.nifi.registry.security.authorization.AccessPolicy currentAccessPolicy = accessPolicyProvider.getAccessPolicy(accessPolicy.getIdentifier()); accessPolicy.setResource(currentAccessPolicy.getResource()); accessPolicy.setAction(currentAccessPolicy.getAction().toString()); - org.apache.nifi.registry.authorization.AccessPolicy updatedAccessPolicy = + org.apache.nifi.registry.security.authorization.AccessPolicy updatedAccessPolicy = ((ConfigurableAccessPolicyProvider) accessPolicyProvider).updateAccessPolicy(accessPolicyFromDTO(accessPolicy)); return accessPolicyToDTO(updatedAccessPolicy); } finally { @@ -419,8 +419,8 @@ private void verifyAccessPolicyProviderIsConfigurable() { } } - private List getAllAuthorizableResources() { - final List resources = new ArrayList<>(); + private List getAllAuthorizableResources() { + final List resources = new ArrayList<>(); resources.add(ResourceFactory.getPoliciesResource()); resources.add(ResourceFactory.getTenantResource()); resources.add(ResourceFactory.getProxyResource()); @@ -436,19 +436,23 @@ private List getAllAuthorizable } private org.apache.nifi.registry.model.authorization.User userToDTO( - final org.apache.nifi.registry.authorization.User user) { + final org.apache.nifi.registry.security.authorization.User user) { if (user == null) { return null; } String userIdentifier = user.getIdentifier(); - Collection userGroups = getUserGroupsForUser(userIdentifier); + + Collection groupsContainingUser = userGroupProvider.getGroups().stream() + .filter(group -> group.getUsers().contains(userIdentifier)) + .map(AuthorizationService::tenantToDTO) + .collect(Collectors.toList()); Collection accessPolicySummaries = getAccessPolicySummariesForUser(userIdentifier); - return userToDTO(user, userGroups, accessPolicySummaries); + return userToDTO(user, groupsContainingUser, accessPolicySummaries); } private org.apache.nifi.registry.model.authorization.UserGroup userGroupToDTO( - final org.apache.nifi.registry.authorization.Group userGroup) { + final org.apache.nifi.registry.security.authorization.Group userGroup) { if (userGroup == null) { return null; } @@ -460,7 +464,7 @@ private org.apache.nifi.registry.model.authorization.UserGroup userGroupToDTO( } private org.apache.nifi.registry.model.authorization.AccessPolicy accessPolicyToDTO( - final org.apache.nifi.registry.authorization.AccessPolicy accessPolicy) { + final org.apache.nifi.registry.security.authorization.AccessPolicy accessPolicy) { if (accessPolicy == null) { return null; } @@ -476,7 +480,7 @@ private org.apache.nifi.registry.model.authorization.AccessPolicy accessPolicyTo } private org.apache.nifi.registry.model.authorization.AccessPolicySummary accessPolicyToSummaryDTO( - final org.apache.nifi.registry.authorization.AccessPolicy accessPolicy) { + final org.apache.nifi.registry.security.authorization.AccessPolicy accessPolicy) { if (accessPolicy == null) { return null; } @@ -491,7 +495,7 @@ private org.apache.nifi.registry.model.authorization.AccessPolicySummary accessP return accessPolicySummaryDTO; } - private static Resource resourceToDTO(org.apache.nifi.registry.authorization.Resource resource) { + private static Resource resourceToDTO(org.apache.nifi.registry.security.authorization.Resource resource) { if (resource == null) { return null; } @@ -501,33 +505,33 @@ private static Resource resourceToDTO(org.apache.nifi.registry.authorization.Res return resourceDto; } - private static Tenant tenantToDTO(org.apache.nifi.registry.authorization.User user) { + private static Tenant tenantToDTO(org.apache.nifi.registry.security.authorization.User user) { if (user == null) { return null; } return new Tenant(user.getIdentifier(), user.getIdentity()); } - private static Tenant tenantToDTO(org.apache.nifi.registry.authorization.Group group) { + private static Tenant tenantToDTO(org.apache.nifi.registry.security.authorization.Group group) { if (group == null) { return null; } return new Tenant(group.getIdentifier(), group.getName()); } - private static org.apache.nifi.registry.authorization.User userFromDTO( + private static org.apache.nifi.registry.security.authorization.User userFromDTO( final org.apache.nifi.registry.model.authorization.User userDTO) { if (userDTO == null) { return null; } - return new org.apache.nifi.registry.authorization.User.Builder() + return new org.apache.nifi.registry.security.authorization.User.Builder() .identifier(userDTO.getIdentifier() != null ? userDTO.getIdentifier() : UUID.randomUUID().toString()) .identity(userDTO.getIdentity()) .build(); } private static org.apache.nifi.registry.model.authorization.User userToDTO( - final org.apache.nifi.registry.authorization.User user, + final org.apache.nifi.registry.security.authorization.User user, final Collection userGroups, final Collection accessPolicies) { @@ -540,12 +544,12 @@ private static org.apache.nifi.registry.model.authorization.User userToDTO( return userDTO; } - private static org.apache.nifi.registry.authorization.Group userGroupFromDTO( + private static org.apache.nifi.registry.security.authorization.Group userGroupFromDTO( final org.apache.nifi.registry.model.authorization.UserGroup userGroupDTO) { if (userGroupDTO == null) { return null; } - org.apache.nifi.registry.authorization.Group.Builder groupBuilder = new org.apache.nifi.registry.authorization.Group.Builder() + org.apache.nifi.registry.security.authorization.Group.Builder groupBuilder = new org.apache.nifi.registry.security.authorization.Group.Builder() .identifier(userGroupDTO.getIdentifier() != null ? userGroupDTO.getIdentifier() : UUID.randomUUID().toString()) .name(userGroupDTO.getIdentity()); Set users = userGroupDTO.getUsers(); @@ -556,7 +560,7 @@ private static org.apache.nifi.registry.authorization.Group userGroupFromDTO( } private static org.apache.nifi.registry.model.authorization.UserGroup userGroupToDTO( - final org.apache.nifi.registry.authorization.Group userGroup, + final org.apache.nifi.registry.security.authorization.Group userGroup, final Collection users, final Collection accessPolicies) { if (userGroup == null) { @@ -568,10 +572,10 @@ private static org.apache.nifi.registry.model.authorization.UserGroup userGroupT return userGroupDTO; } - private static org.apache.nifi.registry.authorization.AccessPolicy accessPolicyFromDTO( + private static org.apache.nifi.registry.security.authorization.AccessPolicy accessPolicyFromDTO( final org.apache.nifi.registry.model.authorization.AccessPolicy accessPolicyDTO) { - org.apache.nifi.registry.authorization.AccessPolicy.Builder accessPolicyBuilder = - new org.apache.nifi.registry.authorization.AccessPolicy.Builder() + org.apache.nifi.registry.security.authorization.AccessPolicy.Builder accessPolicyBuilder = + new org.apache.nifi.registry.security.authorization.AccessPolicy.Builder() .identifier(accessPolicyDTO.getIdentifier() != null ? accessPolicyDTO.getIdentifier() : UUID.randomUUID().toString()) .resource(accessPolicyDTO.getResource()) .action(RequestAction.valueOfValue(accessPolicyDTO.getAction())); @@ -590,7 +594,7 @@ private static org.apache.nifi.registry.authorization.AccessPolicy accessPolicyF } private static org.apache.nifi.registry.model.authorization.AccessPolicy accessPolicyToDTO( - final org.apache.nifi.registry.authorization.AccessPolicy accessPolicy, + final org.apache.nifi.registry.security.authorization.AccessPolicy accessPolicy, final Collection userGroups, final Collection users, final Boolean isConfigurable) { @@ -613,17 +617,17 @@ private static AccessPolicyProvider createExceptionThrowingAccessPolicyProvider( return new AccessPolicyProvider() { @Override - public Set getAccessPolicies() throws AuthorizationAccessException { + public Set getAccessPolicies() throws AuthorizationAccessException { throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER); } @Override - public org.apache.nifi.registry.authorization.AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { + public org.apache.nifi.registry.security.authorization.AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER); } @Override - public org.apache.nifi.registry.authorization.AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { + public org.apache.nifi.registry.security.authorization.AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER); } @@ -631,17 +635,17 @@ public org.apache.nifi.registry.authorization.AccessPolicy getAccessPolicy(Strin public UserGroupProvider getUserGroupProvider() { return new UserGroupProvider() { @Override - public Set getUsers() throws AuthorizationAccessException { + public Set getUsers() throws AuthorizationAccessException { throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER); } @Override - public org.apache.nifi.registry.authorization.User getUser(String identifier) throws AuthorizationAccessException { + public org.apache.nifi.registry.security.authorization.User getUser(String identifier) throws AuthorizationAccessException { throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER); } @Override - public org.apache.nifi.registry.authorization.User getUserByIdentity(String identity) throws AuthorizationAccessException { + public org.apache.nifi.registry.security.authorization.User getUserByIdentity(String identity) throws AuthorizationAccessException { throw new IllegalStateException(MSG_NON_MANAGED_AUTHORIZER); } diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/DataModelMapper.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/DataModelMapper.java index 383f8192e..d83f56056 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/DataModelMapper.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/DataModelMapper.java @@ -23,8 +23,10 @@ import org.apache.nifi.registry.db.entity.FlowEntity; import org.apache.nifi.registry.db.entity.FlowSnapshotEntity; import org.apache.nifi.registry.db.entity.FlowSnapshotEntityKey; +import org.apache.nifi.registry.db.entity.KeyEntity; import org.apache.nifi.registry.flow.VersionedFlow; import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; +import org.apache.nifi.registry.security.key.Key; import java.util.Date; import java.util.LinkedHashSet; @@ -126,4 +128,20 @@ public static VersionedFlowSnapshotMetadata map(final FlowSnapshotEntity flowSna return metadata; } + public static Key map(final KeyEntity keyEntity) { + final Key key = new Key(); + key.setId(keyEntity.getId()); + key.setIdentity(keyEntity.getTenantIdentity()); + key.setKey(keyEntity.getKeyValue()); + return key; + } + + public static KeyEntity map(final Key key) { + final KeyEntity keyEntity = new KeyEntity(); + keyEntity.setId(key.getId()); + keyEntity.setTenantIdentity(key.getIdentity()); + keyEntity.setKeyValue(key.getKey()); + return keyEntity; + } + } diff --git a/nifi-registry-security-api-impl/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.Authorizer b/nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authentication.LoginIdentityProvider similarity index 92% rename from nifi-registry-security-api-impl/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.Authorizer rename to nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authentication.LoginIdentityProvider index 146e5cec6..530528f61 100644 --- a/nifi-registry-security-api-impl/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.Authorizer +++ b/nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authentication.LoginIdentityProvider @@ -12,4 +12,4 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -org.apache.nifi.registry.authorization.file.FileAuthorizer +org.apache.nifi.registry.security.ldap.LdapIdentityProvider \ No newline at end of file diff --git a/nifi-registry-security-api-impl/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.AccessPolicyProvider b/nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.AccessPolicyProvider similarity index 91% rename from nifi-registry-security-api-impl/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.AccessPolicyProvider rename to nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.AccessPolicyProvider index e1d74d7ca..f57163ffc 100644 --- a/nifi-registry-security-api-impl/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.AccessPolicyProvider +++ b/nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.AccessPolicyProvider @@ -12,4 +12,4 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -org.apache.nifi.registry.authorization.file.FileAccessPolicyProvider \ No newline at end of file +org.apache.nifi.registry.security.authorization.file.FileAccessPolicyProvider \ No newline at end of file diff --git a/nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.Authorizer b/nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.Authorizer similarity index 84% rename from nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.Authorizer rename to nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.Authorizer index 3f8707bfa..b564fbb62 100644 --- a/nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.Authorizer +++ b/nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.Authorizer @@ -12,4 +12,5 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -org.apache.nifi.registry.authorization.StandardManagedAuthorizer \ No newline at end of file +org.apache.nifi.registry.security.authorization.StandardManagedAuthorizer +org.apache.nifi.registry.security.authorization.file.FileAuthorizer \ No newline at end of file diff --git a/nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.UserGroupProvider b/nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.UserGroupProvider similarity index 71% rename from nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.UserGroupProvider rename to nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.UserGroupProvider index 9c6bafcc7..ee28c0744 100644 --- a/nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.UserGroupProvider +++ b/nifi-registry-framework/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.UserGroupProvider @@ -12,5 +12,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -org.apache.nifi.registry.authorization.CompositeUserGroupProvider -org.apache.nifi.registry.authorization.CompositeConfigurableUserGroupProvider +org.apache.nifi.registry.security.authorization.CompositeUserGroupProvider +org.apache.nifi.registry.security.authorization.CompositeConfigurableUserGroupProvider +org.apache.nifi.registry.security.authorization.file.FileUserGroupProvider +org.apache.nifi.registry.security.ldap.tenants.LdapUserGroupProvider \ No newline at end of file diff --git a/nifi-registry-framework/src/main/resources/db/migration/V1__Initial.sql b/nifi-registry-framework/src/main/resources/db/migration/V1__Initial.sql index f7640edbc..eb71ae7ac 100644 --- a/nifi-registry-framework/src/main/resources/db/migration/V1__Initial.sql +++ b/nifi-registry-framework/src/main/resources/db/migration/V1__Initial.sql @@ -43,4 +43,11 @@ CREATE TABLE FLOW_SNAPSHOT ( COMMENTS VARCHAR(4096), PRIMARY KEY (FLOW_ID, VERSION), FOREIGN KEY (FLOW_ID) REFERENCES FLOW(ID) +); + +CREATE TABLE SIGNING_KEY ( + ID VARCHAR2(50) NOT NULL, + TENANT_IDENTITY VARCHAR2(50) NOT NULL UNIQUE, + KEY_VALUE VARCHAR2(50) NOT NULL, + PRIMARY KEY (ID) ); \ No newline at end of file diff --git a/nifi-registry-security-api-impl/src/main/xsd/authorizations.xsd b/nifi-registry-framework/src/main/xsd/authorizations.xsd similarity index 100% rename from nifi-registry-security-api-impl/src/main/xsd/authorizations.xsd rename to nifi-registry-framework/src/main/xsd/authorizations.xsd diff --git a/nifi-registry-security-api-impl/src/main/xsd/tenants.xsd b/nifi-registry-framework/src/main/xsd/tenants.xsd similarity index 100% rename from nifi-registry-security-api-impl/src/main/xsd/tenants.xsd rename to nifi-registry-framework/src/main/xsd/tenants.xsd diff --git a/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/db/DatabaseKeyServiceSpec.groovy b/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/db/DatabaseKeyServiceSpec.groovy new file mode 100644 index 000000000..c3cea6a61 --- /dev/null +++ b/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/db/DatabaseKeyServiceSpec.groovy @@ -0,0 +1,85 @@ +/* + * 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.KeyEntity +import org.apache.nifi.registry.db.repository.KeyRepository +import org.apache.nifi.registry.security.key.Key +import spock.lang.Specification + +class DatabaseKeyServiceSpec extends Specification { + + def keyRepository = Mock(KeyRepository) + + DatabaseKeyService keyService + + def setup() { + keyService = new DatabaseKeyService(keyRepository) + } + + def "get key"() { + + given: "a record exists for id=key1" + keyRepository.findOne("key1") >> new KeyEntity([id: "key1", tenantIdentity: "user1", keyValue: "keyValue1"]) + + when: "getKey is called with an existing id" + Key existingKey = keyService.getKey("key1") + + then: "the existing key is returned as model object" + with(existingKey) { + id == "key1" + identity == "user1" + key == "keyValue1" + } + + when: "getKey is called for a nonexistent id" + Key nonexistentKey = keyService.getKey("key2") + + then: "null is returned" + nonexistentKey == null + + } + + def "get or create key"() { + + given: "a record exists for identity=user1" + keyRepository.findOneByTenantIdentity("user1") >> new KeyEntity([id: "key1", tenantIdentity: "user1", keyValue: "keyValue1"]) + keyRepository.findOneByTenantIdentity("user2") >> null + keyRepository.save(_ as KeyEntity) >> { KeyEntity ke -> new KeyEntity([id: ke.id, tenantIdentity: ke.tenantIdentity, keyValue: ke.keyValue])} + + when: "getOrCreateKey is called with an existing identity" + Key existingKey = keyService.getOrCreateKey("user1") + + then: "the existing key is returned as model object" + with(existingKey) { + id == "key1" + identity == "user1" + key == "keyValue1" + } + + when: "getOrCreateKey is called for a nonexistent identity" + Key newKey = keyService.getOrCreateKey("user2") + + then: "a new key is returned" + with(newKey) { + id != null + identity == "user2" + key != null + } + } + +} diff --git a/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy b/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy index 3e9948344..93fd4527f 100644 --- a/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy +++ b/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy @@ -16,17 +16,17 @@ */ package org.apache.nifi.registry.service -import org.apache.nifi.registry.authorization.AccessPolicy as AuthAccessPolicy -import org.apache.nifi.registry.authorization.AuthorizableLookup -import org.apache.nifi.registry.authorization.ConfigurableAccessPolicyProvider -import org.apache.nifi.registry.authorization.ConfigurableUserGroupProvider -import org.apache.nifi.registry.authorization.Group -import org.apache.nifi.registry.authorization.RequestAction -import org.apache.nifi.registry.authorization.StandardManagedAuthorizer -import org.apache.nifi.registry.authorization.User as AuthUser -import org.apache.nifi.registry.authorization.exception.AccessDeniedException -import org.apache.nifi.registry.authorization.resource.Authorizable -import org.apache.nifi.registry.authorization.resource.ResourceType +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 org.apache.nifi.registry.bucket.Bucket import org.apache.nifi.registry.model.authorization.AccessPolicy import org.apache.nifi.registry.model.authorization.User diff --git a/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/repository/TestKeyRepository.java b/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/repository/TestKeyRepository.java new file mode 100644 index 000000000..567b0be17 --- /dev/null +++ b/nifi-registry-framework/src/test/java/org/apache/nifi/registry/db/repository/TestKeyRepository.java @@ -0,0 +1,106 @@ +/* + * 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.repository; + +import org.apache.nifi.registry.db.DatabaseBaseTest; +import org.apache.nifi.registry.db.entity.KeyEntity; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +public class TestKeyRepository extends DatabaseBaseTest { + + @Autowired + private KeyRepository keyRepository; + + @Test + public void testCreate() { + final KeyEntity key = new KeyEntity(); + key.setId(UUID.randomUUID().toString()); + key.setTenantIdentity("user"); + key.setKeyValue(UUID.randomUUID().toString()); + + final KeyEntity createdKey = keyRepository.save(key); + assertNotNull(createdKey); + assertEquals(key.getId(), createdKey.getId()); + assertEquals(key.getTenantIdentity(), createdKey.getTenantIdentity()); + assertEquals(key.getKeyValue(), createdKey.getKeyValue()); + } + + @Test + public void testUpdate() { + final String prepopulatedKeyId = "1"; // see test-setup.sql + + final KeyEntity existingKey = keyRepository.findOne(prepopulatedKeyId); + assertNotNull(existingKey); + + final String updatedKeyValue = existingKey.getKeyValue() + " UPDATED"; + existingKey.setKeyValue(updatedKeyValue); + + keyRepository.save(existingKey); + + final KeyEntity updatedKey = keyRepository.findOne(prepopulatedKeyId); + assertNotNull(updatedKey); + assertEquals(updatedKeyValue, updatedKey.getKeyValue()); + } + + @Test + @Transactional + public void testDelete() { + final String id = "1"; + + final KeyEntity existingKey = keyRepository.findOne("1"); + assertNotNull(existingKey); + + keyRepository.delete(existingKey); + + final KeyEntity deletedKey = keyRepository.findOne("1"); + assertNull(deletedKey); + } + + @Test + public void testFindOneByTenantIdentity() { + final String prepopulatedKeyTenantIdentity = "unit_test_tenant_identity"; // see test-setup.sql + + final KeyEntity existingKey = keyRepository.findOneByTenantIdentity(prepopulatedKeyTenantIdentity); + assertNotNull(existingKey); + assertEquals("1", existingKey.getId()); + } + + @Test + @Transactional + public void testDeleteByTenantIdentity() { + final String prepopulatedKeyTenantIdentity = "unit_test_tenant_identity"; // see test-setup.sql + + final KeyEntity existingKey = keyRepository.findOneByTenantIdentity(prepopulatedKeyTenantIdentity); + assertNotNull(existingKey); + + keyRepository.delete(existingKey); + + KeyEntity deletedKey = keyRepository.findOneByTenantIdentity(prepopulatedKeyTenantIdentity); + assertNull(deletedKey); + deletedKey = keyRepository.findOne("1"); + assertNull(deletedKey); + } + +} diff --git a/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql b/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql index 26fe647e5..8d9d6fb4c 100644 --- a/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql +++ b/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql @@ -61,4 +61,10 @@ insert into flow_snapshot (flow_id, version, created, comments) values ('1', 2, parsedatetime('2017-09-11 12:58:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), 'This is flow 1 snapshot 2'); insert into flow_snapshot (flow_id, version, created, comments) - values ('1', 3, parsedatetime('2017-09-11 12:59:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), 'This is flow 1 snapshot 3'); \ No newline at end of file + values ('1', 3, parsedatetime('2017-09-11 12:59:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), 'This is flow 1 snapshot 3'); + + +-- test data for signing keys + +insert into signing_key (id, tenant_identity, key_value) + values ('1', 'unit_test_tenant_identity', '0123456789abcdef'); \ No newline at end of file diff --git a/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java b/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java index b40b78420..cfbc60136 100644 --- a/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java +++ b/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java @@ -49,6 +49,8 @@ public class NiFiRegistryProperties extends Properties { public static final String SECURITY_NEED_CLIENT_AUTH = "nifi.registry.security.needClientAuth"; public static final String SECURITY_AUTHORIZERS_CONFIGURATION_FILE = "nifi.registry.security.authorizers.configuration.file"; public static final String SECURITY_AUTHORIZER = "nifi.registry.security.authorizer"; + public static final String SECURITY_IDENTITY_PROVIDER_CONFIGURATION_FILE = "nifi.registry.security.identity.provider.configuration.file"; + public static final String SECURITY_IDENTITY_PROVIDER = "nifi.registry.security.identity.provider"; public static final String SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX = "nifi.registry.security.identity.mapping.pattern."; public static final String SECURITY_IDENTITY_MAPPING_VALUE_PREFIX = "nifi.registry.security.identity.mapping.value."; @@ -64,6 +66,7 @@ public class NiFiRegistryProperties extends Properties { public static final String DEFAULT_WAR_DIR = "./lib"; public static final String DEFAULT_PROVIDERS_CONFIGURATION_FILE = "./conf/providers.xml"; public static final String DEFAULT_SECURITY_AUTHORIZERS_CONFIGURATION_FILE = "./conf/authorizers.xml"; + public static final String DEFAULT_SECURITY_IDENTITY_PROVIDER_CONFIGURATION_FILE = "./conf/identity-providers.xml"; public int getWebThreads() { int webThreads = 200; @@ -178,6 +181,15 @@ public File getAuthorizersConfigurationFile() { } } + public File getIdentityProviderConfigurationFile() { + final String value = getProperty(SECURITY_IDENTITY_PROVIDER_CONFIGURATION_FILE); + if (StringUtils.isBlank(value)) { + return new File(DEFAULT_SECURITY_IDENTITY_PROVIDER_CONFIGURATION_FILE); + } else { + return new File(value); + } + } + public Set getExtensionsDirs() { final Set extensionDirs = new HashSet<>(); stringPropertyNames().stream().filter(key -> key.startsWith(EXTENSION_DIR_PREFIX)).forEach(key -> extensionDirs.add(getProperty(key))); diff --git a/nifi-registry-resources/src/main/resources/conf/authorizers.xml b/nifi-registry-resources/src/main/resources/conf/authorizers.xml index e4696bb0f..663a92691 100644 --- a/nifi-registry-resources/src/main/resources/conf/authorizers.xml +++ b/nifi-registry-resources/src/main/resources/conf/authorizers.xml @@ -42,11 +42,111 @@ --> file-user-group-provider - org.apache.nifi.registry.authorization.file.FileUserGroupProvider + org.apache.nifi.registry.security.authorization.file.FileUserGroupProvider ./conf/users.xml + + + file-access-policy-provider - org.apache.nifi.registry.authorization.file.FileAccessPolicyProvider + org.apache.nifi.registry.security.authorization.file.FileAccessPolicyProvider file-user-group-provider ./conf/authorizations.xml @@ -134,7 +234,7 @@ --> managed-authorizer - org.apache.nifi.registry.authorization.StandardManagedAuthorizer + org.apache.nifi.registry.security.authorization.StandardManagedAuthorizer file-access-policy-provider diff --git a/nifi-registry-security-api-impl/pom.xml b/nifi-registry-security-api-impl/pom.xml deleted file mode 100644 index efacb8854..000000000 --- a/nifi-registry-security-api-impl/pom.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - nifi-registry - org.apache.nifi.registry - 0.0.1-SNAPSHOT - - 4.0.0 - - nifi-registry-security-api-impl - jar - - - - - src/main/xsd - - - src/main/resources - - - - - org.codehaus.mojo - jaxb2-maven-plugin - - - authorizations - - xjc - - - - src/main/xsd/authorizations.xsd - - org.apache.nifi.registry.authorization.file.generated - - - - tenants - - xjc - - - - src/main/xsd/tenants.xsd - - org.apache.nifi.registry.authorization.file.tenants.generated - false - - - - - ${project.build.directory}/generated-sources/jaxb - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - **/authorization/file/generated/*.java,**/authorization/file/tenants/generated/*.java - - - - - - - - org.apache.nifi.registry - nifi-registry-properties - 0.0.1-SNAPSHOT - provided - - - org.apache.nifi.registry - nifi-registry-security-api - 0.0.1-SNAPSHOT - provided - - - - \ No newline at end of file diff --git a/nifi-registry-security-api-impl/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.UserGroupProvider b/nifi-registry-security-api-impl/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.UserGroupProvider deleted file mode 100644 index 1e0fc5ebc..000000000 --- a/nifi-registry-security-api-impl/src/main/resources/META-INF/services/org.apache.nifi.registry.authorization.UserGroupProvider +++ /dev/null @@ -1,15 +0,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. -org.apache.nifi.registry.authorization.file.FileUserGroupProvider \ No newline at end of file diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/AuthenticationResponse.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/AuthenticationResponse.java new file mode 100644 index 000000000..e6bfeb244 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/AuthenticationResponse.java @@ -0,0 +1,65 @@ +/* + * 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.authentication; + +/** + * Authentication response for a user login attempt. + */ +public class AuthenticationResponse { + + private final String identity; + private final String username; + private final long expiration; + private final String issuer; + + /** + * Creates an authentication response. The username and how long the authentication is valid in milliseconds + * + * @param identity The user identity + * @param username The username + * @param expiration The expiration in milliseconds + * @param issuer The issuer of the token + */ + public AuthenticationResponse(final String identity, final String username, final long expiration, final String issuer) { + this.identity = identity; + this.username = username; + this.expiration = expiration; + this.issuer = issuer; + } + + public String getIdentity() { + return identity; + } + + public String getUsername() { + return username; + } + + public String getIssuer() { + return issuer; + } + + /** + * Returns the expiration of a given authentication in milliseconds. + * + * @return The expiration in milliseconds + */ + public long getExpiration() { + return expiration; + } + +} diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginCredentials.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginCredentials.java new file mode 100644 index 000000000..925d36dab --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginCredentials.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.security.authentication; + +/** + * Login credentials for a user. + */ +public class LoginCredentials { + + private final String username; + private final String password; + + public LoginCredentials(String username, String password) { + this.username = username; + this.password = password; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } +} diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProvider.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProvider.java new file mode 100644 index 000000000..b74069ac7 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProvider.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.security.authentication; + +import org.apache.nifi.registry.security.authentication.exception.IdentityAccessException; +import org.apache.nifi.registry.security.authentication.exception.InvalidLoginCredentialsException; +import org.apache.nifi.registry.security.authentication.exception.ProviderCreationException; +import org.apache.nifi.registry.security.authentication.exception.ProviderDestructionException; + +/** + * Identity provider that is able to authentication a user with username/password credentials. + */ +public interface LoginIdentityProvider { + + /** + * Authenticates the specified login credentials. + * + * @param credentials the credentials + * @return The authentication response + * @throws InvalidLoginCredentialsException The login credentials were invalid + * @throws IdentityAccessException Unable to register the user due to an issue accessing the underlying storage + */ + AuthenticationResponse authenticate(LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException; + + /** + * Called immediately after instance creation for implementers to perform additional setup + * + * @param initializationContext in which to initialize + * @throws ProviderCreationException Unable to initialize + */ + void initialize(LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException; + + /** + * Called to configure the AuthorityProvider. + * + * @param configurationContext at the time of configuration + * @throws ProviderCreationException for any issues configuring the provider + */ + void onConfigured(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException; + + /** + * Called immediately before instance destruction for implementers to release resources. + * + * @throws ProviderDestructionException If pre-destruction fails. + */ + void preDestruction() throws ProviderDestructionException; +} diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProviderConfigurationContext.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProviderConfigurationContext.java new file mode 100644 index 000000000..a7f21be6e --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProviderConfigurationContext.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.security.authentication; + +import java.util.Map; + +/** + * + */ +public interface LoginIdentityProviderConfigurationContext { + + /** + * @return identifier for the authority provider + */ + 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 getProperties(); + + /** + * @param property to lookup the descriptor and value of + * @return the value the component currently understands for the given + * PropertyDescriptor. This method does not substitute default + * PropertyDescriptor values, so the value returned will be null if not set + */ + String getProperty(String property); +} diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProviderInitializationContext.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProviderInitializationContext.java new file mode 100644 index 000000000..755c2e86f --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProviderInitializationContext.java @@ -0,0 +1,27 @@ +/* + * 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.authentication; + +/** + * + */ +public interface LoginIdentityProviderInitializationContext { + + public String getIdentifier(); + + public LoginIdentityProviderLookup getAuthorityProviderLookup(); +} diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProviderLookup.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProviderLookup.java new file mode 100644 index 000000000..8720bbaa5 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/LoginIdentityProviderLookup.java @@ -0,0 +1,23 @@ +/* + * 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.authentication; + +public interface LoginIdentityProviderLookup { + + LoginIdentityProvider getLoginIdentityProvider(String identifier); + +} diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/annotation/LoginIdentityProviderContext.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/annotation/LoginIdentityProviderContext.java new file mode 100644 index 000000000..1c4d17fa0 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/annotation/LoginIdentityProviderContext.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.security.authentication.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * + */ +@Documented +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface LoginIdentityProviderContext { +} diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/IdentityAccessException.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/IdentityAccessException.java new file mode 100644 index 000000000..fae567a61 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/IdentityAccessException.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.security.authentication.exception; + +/** + * Represents the case when the identity could not be confirmed because it was unable + * to access the backing store. + */ +public class IdentityAccessException extends RuntimeException { + + public IdentityAccessException(String message, Throwable cause) { + super(message, cause); + } + + public IdentityAccessException(String message) { + super(message); + } + +} diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/InvalidLoginCredentialsException.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/InvalidLoginCredentialsException.java new file mode 100644 index 000000000..c43285772 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/InvalidLoginCredentialsException.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.security.authentication.exception; + +/** + * Represents the case when the identity could not be confirmed because the + * login credentials were invalid. + */ +public class InvalidLoginCredentialsException extends RuntimeException { + + public InvalidLoginCredentialsException(String message, Throwable cause) { + super(message, cause); + } + + public InvalidLoginCredentialsException(String message) { + super(message); + } + +} diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/ProviderCreationException.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/ProviderCreationException.java new file mode 100644 index 000000000..12844cec0 --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/ProviderCreationException.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.security.authentication.exception; + +/** + * Represents the exceptional case when an AuthorityProvider fails instantiated. + * + */ +public class ProviderCreationException extends RuntimeException { + + public ProviderCreationException() { + } + + public ProviderCreationException(String msg) { + super(msg); + } + + public ProviderCreationException(Throwable cause) { + super(cause); + } + + public ProviderCreationException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/ProviderDestructionException.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/ProviderDestructionException.java new file mode 100644 index 000000000..8a0157b6c --- /dev/null +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authentication/exception/ProviderDestructionException.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.security.authentication.exception; + +/** + * Represents the exceptional case when an AuthorityProvider fails destruction. + * + */ +public class ProviderDestructionException extends RuntimeException { + + public ProviderDestructionException() { + } + + public ProviderDestructionException(String msg) { + super(msg); + } + + public ProviderDestructionException(Throwable cause) { + super(cause); + } + + public ProviderDestructionException(String msg, Throwable cause) { + super(msg, cause); + } +} 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/security/authorization/AccessPolicy.java similarity index 99% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicy.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AccessPolicy.java index 7f2bfa4ed..aa8260b9c 100644 --- 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/security/authorization/AccessPolicy.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import java.nio.charset.StandardCharsets; import java.util.Collections; 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/security/authorization/AccessPolicyProvider.java similarity index 91% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProvider.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AccessPolicyProvider.java index d7e44b276..b942ec2d5 100644 --- 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/security/authorization/AccessPolicyProvider.java @@ -14,11 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.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 org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestructionException; import java.util.Set; 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/security/authorization/AccessPolicyProviderInitializationContext.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProviderInitializationContext.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AccessPolicyProviderInitializationContext.java index 756bf32c6..92792e9a1 100644 --- 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/security/authorization/AccessPolicyProviderInitializationContext.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; /** * Initialization content for AccessPolicyProviders. 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/security/authorization/AccessPolicyProviderLookup.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AccessPolicyProviderLookup.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AccessPolicyProviderLookup.java index ff5b9d425..679072ae5 100644 --- 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/security/authorization/AccessPolicyProviderLookup.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; /** * 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/security/authorization/AuthorizationAuditor.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationAuditor.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizationAuditor.java index 4f017c516..ae01ecec3 100644 --- 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/security/authorization/AuthorizationAuditor.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; public interface AuthorizationAuditor { 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/security/authorization/AuthorizationRequest.java similarity index 99% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationRequest.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizationRequest.java index 5c506ef80..1eb99f97a 100644 --- 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/security/authorization/AuthorizationRequest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import java.util.Collections; import java.util.HashMap; 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/security/authorization/AuthorizationResult.java similarity index 98% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizationResult.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizationResult.java index 3cfd056cb..5f9b55e86 100644 --- 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/security/authorization/AuthorizationResult.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; /** * Represents a decision whether authorization is granted. 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/security/authorization/Authorizer.java similarity index 87% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Authorizer.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/Authorizer.java index 3f805d532..3018dd3a0 100644 --- 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/security/authorization/Authorizer.java @@ -14,11 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.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 org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestructionException; /** * Authorizes user requests. 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/security/authorization/AuthorizerConfigurationContext.java similarity index 96% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerConfigurationContext.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerConfigurationContext.java index fb1b2069c..5e33f0c60 100644 --- 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/security/authorization/AuthorizerConfigurationContext.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import org.apache.nifi.registry.util.PropertyValue; 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/security/authorization/AuthorizerInitializationContext.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerInitializationContext.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerInitializationContext.java index c4ef14f4f..55854b3ec 100644 --- 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/security/authorization/AuthorizerInitializationContext.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; /** * Initialization content for Authorizers. 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/security/authorization/AuthorizerLookup.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/AuthorizerLookup.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerLookup.java index 4d7641bb4..2c429d9df 100644 --- 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/security/authorization/AuthorizerLookup.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; /** * 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/security/authorization/ConfigurableAccessPolicyProvider.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ConfigurableAccessPolicyProvider.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/ConfigurableAccessPolicyProvider.java index b563c401a..1f909a4af 100644 --- 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/security/authorization/ConfigurableAccessPolicyProvider.java @@ -14,10 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; -import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException; -import org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException; /** * Provides support for configuring AccessPolicies. 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/security/authorization/ConfigurableUserGroupProvider.java similarity index 96% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ConfigurableUserGroupProvider.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/ConfigurableUserGroupProvider.java index 83a7124a8..bd5212877 100644 --- 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/security/authorization/ConfigurableUserGroupProvider.java @@ -14,10 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; -import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException; -import org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException; /** * Provides support for configuring Users and Groups. diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Group.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/Group.java similarity index 99% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Group.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/Group.java index f22dd97fd..0ec7764cf 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Group.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/Group.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import java.nio.charset.StandardCharsets; import java.util.Collections; diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ManagedAuthorizer.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/ManagedAuthorizer.java similarity index 91% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ManagedAuthorizer.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/ManagedAuthorizer.java index da82f4e5d..50b809400 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/ManagedAuthorizer.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/ManagedAuthorizer.java @@ -14,10 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; -import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException; -import org.apache.nifi.registry.authorization.exception.UninheritableAuthorizationsException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException; public interface ManagedAuthorizer extends Authorizer { diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/RequestAction.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/RequestAction.java similarity index 97% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/RequestAction.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/RequestAction.java index a489ecc8c..adf07f2fb 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/RequestAction.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/RequestAction.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import java.util.StringJoiner; diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Resource.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/Resource.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Resource.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/Resource.java index 711f724c2..eacdffe24 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/Resource.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/Resource.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; /** * Resource in an authorization request. diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/User.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/User.java similarity index 99% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/User.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/User.java index 79f12a818..8879afef3 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/User.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/User.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import java.nio.charset.StandardCharsets; import java.util.Objects; diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserAndGroups.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserAndGroups.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserAndGroups.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserAndGroups.java index b8f150a6e..c9cd0aa6b 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserAndGroups.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserAndGroups.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; import java.util.Set; diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserContextKeys.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserContextKeys.java similarity index 94% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserContextKeys.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserContextKeys.java index daac9e8cc..8db6cfcca 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserContextKeys.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserContextKeys.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; /** * Constants for keys that can be passed in the AuthorizationRequest user context Map. diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProvider.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserGroupProvider.java similarity index 92% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProvider.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserGroupProvider.java index c0460da00..9811691bf 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProvider.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserGroupProvider.java @@ -14,11 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.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 org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizerDestructionException; import java.util.Set; diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderInitializationContext.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserGroupProviderInitializationContext.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderInitializationContext.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserGroupProviderInitializationContext.java index 6a213fa05..d2c471e86 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderInitializationContext.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserGroupProviderInitializationContext.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; /** * Initialization content for UserGroupProviders. diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderLookup.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserGroupProviderLookup.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderLookup.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserGroupProviderLookup.java index ddf61241c..df5e01c7b 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/UserGroupProviderLookup.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/UserGroupProviderLookup.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization; +package org.apache.nifi.registry.security.authorization; /** * diff --git a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/annotation/AuthorizerContext.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/annotation/AuthorizerContext.java similarity index 94% rename from nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/annotation/AuthorizerContext.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/annotation/AuthorizerContext.java index 131238c27..8d5136ebc 100644 --- a/nifi-registry-security-api-impl/src/main/java/org/apache/nifi/registry/authorization/annotation/AuthorizerContext.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/annotation/AuthorizerContext.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.annotation; +package org.apache.nifi.registry.security.authorization.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AccessDeniedException.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/AccessDeniedException.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AccessDeniedException.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/AccessDeniedException.java index 7b09a6ea5..6ab629c30 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AccessDeniedException.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/AccessDeniedException.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.exception; +package org.apache.nifi.registry.security.authorization.exception; /** * Represents any error that might occur while authorizing user requests. diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizationAccessException.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/AuthorizationAccessException.java similarity index 94% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizationAccessException.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/AuthorizationAccessException.java index 407e1821d..7f33430d8 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizationAccessException.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/AuthorizationAccessException.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.exception; +package org.apache.nifi.registry.security.authorization.exception; /** * Represents the case when an authorization decision could not be made because the Authorizer was unable to access the underlying data store. diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerCreationException.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/AuthorizerCreationException.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerCreationException.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/AuthorizerCreationException.java index 2a7ae36e2..4d34cf5cb 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerCreationException.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/AuthorizerCreationException.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.exception; +package org.apache.nifi.registry.security.authorization.exception; /** * Represents the exceptional case when an Authorizer fails instantiation. diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerDestructionException.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/AuthorizerDestructionException.java similarity index 95% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerDestructionException.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/AuthorizerDestructionException.java index 0f4a4983c..fbec6d367 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/AuthorizerDestructionException.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/AuthorizerDestructionException.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.exception; +package org.apache.nifi.registry.security.authorization.exception; /** * Represents the exceptional case when an Authorizer fails destruction. diff --git a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/UninheritableAuthorizationsException.java b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/UninheritableAuthorizationsException.java similarity index 94% rename from nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/UninheritableAuthorizationsException.java rename to nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/UninheritableAuthorizationsException.java index fe110f7a9..b3ef068cd 100644 --- a/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/authorization/exception/UninheritableAuthorizationsException.java +++ b/nifi-registry-security-api/src/main/java/org/apache/nifi/registry/security/authorization/exception/UninheritableAuthorizationsException.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.authorization.exception; +package org.apache.nifi.registry.security.authorization.exception; /** * Represents the case when the proposed authorizations are not inheritable. diff --git a/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/CertificateUtils.java b/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/CertificateUtils.java index bbc7d7cc6..b14cb6141 100644 --- a/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/CertificateUtils.java +++ b/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/CertificateUtils.java @@ -17,6 +17,8 @@ package org.apache.nifi.registry.security.util; import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.security.util.KeyStoreUtils; +import org.apache.nifi.registry.security.util.KeystoreType; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Set; diff --git a/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/SslContextFactory.java b/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/SslContextFactory.java new file mode 100644 index 000000000..9ed8ace2d --- /dev/null +++ b/nifi-registry-security-utils/src/main/java/org/apache/nifi/registry/security/util/SslContextFactory.java @@ -0,0 +1,249 @@ +/* + * 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.util; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; + +/** + * A factory for creating SSL contexts using the application's security + * properties. + * + */ +public final class SslContextFactory { + + public static enum ClientAuth { + + WANT, + REQUIRED, + NONE + } + + /** + * Creates a SSLContext instance using the given information. The password for the key is assumed to be the same + * as the password for the keystore. If this is not the case, the {@link #createSslContext(String, char[], chart[], String, String, char[], String, ClientAuth, String)} + * method should be used instead + * + * @param keystore the full path to the keystore + * @param keystorePasswd the keystore password + * @param keystoreType the type of keystore (e.g., PKCS12, JKS) + * @param truststore the full path to the truststore + * @param truststorePasswd the truststore password + * @param truststoreType the type of truststore (e.g., PKCS12, JKS) + * @param clientAuth the type of client authentication + * @param protocol the protocol to use for the SSL connection + * + * @return a SSLContext instance + * @throws KeyStoreException if any issues accessing the keystore + * @throws IOException for any problems loading the keystores + * @throws NoSuchAlgorithmException if an algorithm is found to be used but is unknown + * @throws CertificateException if there is an issue with the certificate + * @throws UnrecoverableKeyException if the key is insufficient + * @throws KeyManagementException if unable to manage the key + */ + public static SSLContext createSslContext( + final String keystore, final char[] keystorePasswd, final String keystoreType, + final String truststore, final char[] truststorePasswd, final String truststoreType, + final ClientAuth clientAuth, final String protocol) + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, + UnrecoverableKeyException, KeyManagementException { + + // Pass the keystore password as both the keystore password and the key password. + return createSslContext(keystore, keystorePasswd, keystorePasswd, keystoreType, truststore, truststorePasswd, truststoreType, clientAuth, protocol); + } + + /** + * Creates a SSLContext instance using the given information. + * + * @param keystore the full path to the keystore + * @param keystorePasswd the keystore password + * @param keystoreType the type of keystore (e.g., PKCS12, JKS) + * @param truststore the full path to the truststore + * @param truststorePasswd the truststore password + * @param truststoreType the type of truststore (e.g., PKCS12, JKS) + * @param clientAuth the type of client authentication + * @param protocol the protocol to use for the SSL connection + * + * @return a SSLContext instance + * @throws KeyStoreException if any issues accessing the keystore + * @throws IOException for any problems loading the keystores + * @throws NoSuchAlgorithmException if an algorithm is found to be used but is unknown + * @throws CertificateException if there is an issue with the certificate + * @throws UnrecoverableKeyException if the key is insufficient + * @throws KeyManagementException if unable to manage the key + */ + public static SSLContext createSslContext( + final String keystore, final char[] keystorePasswd, final char[] keyPasswd, final String keystoreType, + final String truststore, final char[] truststorePasswd, final String truststoreType, + final ClientAuth clientAuth, final String protocol) + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, + UnrecoverableKeyException, KeyManagementException { + + // prepare the keystore + final KeyStore keyStore = KeyStoreUtils.getKeyStore(keystoreType); + try (final InputStream keyStoreStream = new FileInputStream(keystore)) { + keyStore.load(keyStoreStream, keystorePasswd); + } + final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + if (keyPasswd == null) { + keyManagerFactory.init(keyStore, keystorePasswd); + } else { + keyManagerFactory.init(keyStore, keyPasswd); + } + + // prepare the truststore + final KeyStore trustStore = KeyStoreUtils.getTrustStore(truststoreType); + try (final InputStream trustStoreStream = new FileInputStream(truststore)) { + trustStore.load(trustStoreStream, truststorePasswd); + } + final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(trustStore); + + // initialize the ssl context + final SSLContext sslContext = SSLContext.getInstance(protocol); + sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom()); + if (ClientAuth.REQUIRED == clientAuth) { + sslContext.getDefaultSSLParameters().setNeedClientAuth(true); + } else if (ClientAuth.WANT == clientAuth) { + sslContext.getDefaultSSLParameters().setWantClientAuth(true); + } else { + sslContext.getDefaultSSLParameters().setWantClientAuth(false); + } + + return sslContext; + + } + + /** + * Creates a SSLContext instance using the given information. This method assumes that the key password is + * the same as the keystore password. If this is not the case, use the {@link #createSslContext(String, char[], char[], String, String)} + * method instead. + * + * @param keystore the full path to the keystore + * @param keystorePasswd the keystore password + * @param keystoreType the type of keystore (e.g., PKCS12, JKS) + * @param protocol the protocol to use for the SSL connection + * + * @return a SSLContext instance + * @throws KeyStoreException if any issues accessing the keystore + * @throws IOException for any problems loading the keystores + * @throws NoSuchAlgorithmException if an algorithm is found to be used but is unknown + * @throws CertificateException if there is an issue with the certificate + * @throws UnrecoverableKeyException if the key is insufficient + * @throws KeyManagementException if unable to manage the key + */ + public static SSLContext createSslContext( + final String keystore, final char[] keystorePasswd, final String keystoreType, final String protocol) + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, + UnrecoverableKeyException, KeyManagementException { + + // create SSL Context passing keystore password as the key password + return createSslContext(keystore, keystorePasswd, keystorePasswd, keystoreType, protocol); + } + + /** + * Creates a SSLContext instance using the given information. + * + * @param keystore the full path to the keystore + * @param keystorePasswd the keystore password + * @param keystoreType the type of keystore (e.g., PKCS12, JKS) + * @param protocol the protocol to use for the SSL connection + * + * @return a SSLContext instance + * @throws KeyStoreException if any issues accessing the keystore + * @throws IOException for any problems loading the keystores + * @throws NoSuchAlgorithmException if an algorithm is found to be used but is unknown + * @throws CertificateException if there is an issue with the certificate + * @throws UnrecoverableKeyException if the key is insufficient + * @throws KeyManagementException if unable to manage the key + */ + public static SSLContext createSslContext( + final String keystore, final char[] keystorePasswd, final char[] keyPasswd, final String keystoreType, final String protocol) + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, + UnrecoverableKeyException, KeyManagementException { + + // prepare the keystore + final KeyStore keyStore = KeyStoreUtils.getKeyStore(keystoreType); + try (final InputStream keyStoreStream = new FileInputStream(keystore)) { + keyStore.load(keyStoreStream, keystorePasswd); + } + final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + if (keyPasswd == null) { + keyManagerFactory.init(keyStore, keystorePasswd); + } else { + keyManagerFactory.init(keyStore, keyPasswd); + } + + // initialize the ssl context + final SSLContext ctx = SSLContext.getInstance(protocol); + ctx.init(keyManagerFactory.getKeyManagers(), new TrustManager[0], new SecureRandom()); + + return ctx; + + } + + /** + * Creates a SSLContext instance using the given information. + * + * @param truststore the full path to the truststore + * @param truststorePasswd the truststore password + * @param truststoreType the type of truststore (e.g., PKCS12, JKS) + * @param protocol the protocol to use for the SSL connection + * + * @return a SSLContext instance + * @throws KeyStoreException if any issues accessing the keystore + * @throws IOException for any problems loading the keystores + * @throws NoSuchAlgorithmException if an algorithm is found to be used but is unknown + * @throws CertificateException if there is an issue with the certificate + * @throws UnrecoverableKeyException if the key is insufficient + * @throws KeyManagementException if unable to manage the key + */ + public static SSLContext createTrustSslContext( + final String truststore, final char[] truststorePasswd, final String truststoreType, final String protocol) + throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, + UnrecoverableKeyException, KeyManagementException { + + // prepare the truststore + final KeyStore trustStore = KeyStoreUtils.getTrustStore(truststoreType); + try (final InputStream trustStoreStream = new FileInputStream(truststore)) { + trustStore.load(trustStoreStream, truststorePasswd); + } + final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(trustStore); + + // initialize the ssl context + final SSLContext ctx = SSLContext.getInstance(protocol); + ctx.init(new KeyManager[0], trustManagerFactory.getTrustManagers(), new SecureRandom()); + + return ctx; + + } + +} diff --git a/nifi-registry-web-api/pom.xml b/nifi-registry-web-api/pom.xml index 0174bd579..ab7dd8474 100644 --- a/nifi-registry-web-api/pom.xml +++ b/nifi-registry-web-api/pom.xml @@ -31,6 +31,9 @@ src/main/resources + + src/main/xsd + @@ -44,6 +47,28 @@ false + + org.codehaus.mojo + jaxb2-maven-plugin + + + identity-providers + + xjc + + + org.apache.nifi.registry.security.authentication.generated + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + **/authentication/generated/*.java, + + com.github.kongchen swagger-maven-plugin @@ -192,6 +217,12 @@ 18.0 + + org.apache.nifi.registry + nifi-registry-client + 0.0.1-SNAPSHOT + test + org.springframework.boot spring-boot-starter-test @@ -199,16 +230,15 @@ test - org.springframework.boot spring-boot-starter-jetty ${spring.boot.version} test - org.apache.nifi.registry - nifi-registry-client - 0.0.1-SNAPSHOT + com.unboundid + unboundid-ldapsdk + 3.2.1 test diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java index c78e84d08..7d28fca2e 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java @@ -21,6 +21,7 @@ import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.NotAllowedException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -35,11 +36,11 @@ import io.swagger.annotations.ApiParam; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.AuthorizerCapabilityDetection; -import org.apache.nifi.registry.authorization.RequestAction; -import org.apache.nifi.registry.authorization.resource.Authorizable; -import org.apache.nifi.registry.authorization.user.NiFiUserUtils; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.AuthorizerCapabilityDetection; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.resource.Authorizable; +import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils; import org.apache.nifi.registry.model.authorization.AccessPolicy; import org.apache.nifi.registry.model.authorization.AccessPolicySummary; import org.apache.nifi.registry.service.AuthorizationService; @@ -91,6 +92,7 @@ public AccessPolicyResource( @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405), @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) public Response createAccessPolicy( @Context final HttpServletRequest httpServletRequest, @@ -135,6 +137,7 @@ public Response createAccessPolicy( @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_409), + @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_409) }) public Response getAccessPolicies() { @@ -163,7 +166,8 @@ public Response getAccessPolicies() { @ApiResponses({ @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), - @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404) }) + @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405) }) public Response getAccessPolicy( @ApiParam(value = "The access policy id.", required = true) @PathParam("id") final String identifier) { @@ -202,6 +206,7 @@ public Response getAccessPolicy( @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405), @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) public Response getAccessPolicyForResource( @ApiParam(value = "The request action.", allowableValues = "read, write, delete", required = true) @@ -245,6 +250,7 @@ public Response getAccessPolicyForResource( @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405), @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) public Response updateAccessPolicy( @Context @@ -290,9 +296,10 @@ public Response updateAccessPolicy( response = AccessPolicy.class ) @ApiResponses({ - @ApiResponse(code = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found.") }) + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405) }) public Response removeAccessPolicy( @Context final HttpServletRequest httpServletRequest, @ApiParam(value = "The access policy id.", required = true) @@ -308,13 +315,13 @@ public Response removeAccessPolicy( private void verifyAuthorizerIsManaged() { if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) { - throw new IllegalStateException(AuthorizationService.MSG_NON_MANAGED_AUTHORIZER); + throw new NotAllowedException(AuthorizationService.MSG_NON_MANAGED_AUTHORIZER); } } private void verifyAuthorizerSupportsConfigurablePolicies() { if (!AuthorizerCapabilityDetection.isConfigurableAccessPolicyProvider(authorizer)) { - throw new IllegalStateException(AuthorizationService.MSG_NON_CONFIGURABLE_POLICIES); + throw new NotAllowedException(AuthorizationService.MSG_NON_CONFIGURABLE_POLICIES); } } diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java index a5c849a4a..43d187f01 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java @@ -16,38 +16,55 @@ */ package org.apache.nifi.registry.web.api; +import io.jsonwebtoken.JwtException; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; -import org.apache.commons.lang3.NotImplementedException; -import org.apache.nifi.registry.authorization.exception.AccessDeniedException; -import org.apache.nifi.registry.authorization.user.NiFiUser; -import org.apache.nifi.registry.authorization.user.NiFiUserDetails; +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.security.authentication.AuthenticationResponse; +import org.apache.nifi.registry.security.authentication.LoginCredentials; +import org.apache.nifi.registry.security.authentication.LoginIdentityProvider; +import org.apache.nifi.registry.security.authentication.exception.IdentityAccessException; +import org.apache.nifi.registry.security.authentication.exception.InvalidLoginCredentialsException; +import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException; +import org.apache.nifi.registry.security.authorization.user.NiFiUser; +import org.apache.nifi.registry.security.authorization.user.NiFiUserDetails; import org.apache.nifi.registry.exception.AdministrationException; import org.apache.nifi.registry.model.authorization.AccessStatus; -import org.apache.nifi.registry.web.security.InvalidAuthenticationException; -import org.apache.nifi.registry.web.security.ProxiedEntitiesUtils; -import org.apache.nifi.registry.web.security.UntrustedProxyException; -import org.apache.nifi.registry.web.security.token.NiFiAuthenticationToken; -import org.apache.nifi.registry.web.security.x509.X509AuthenticationProvider; -import org.apache.nifi.registry.web.security.x509.X509AuthenticationRequestToken; -import org.apache.nifi.registry.web.security.x509.X509CertificateExtractor; +import org.apache.nifi.registry.web.security.authentication.exception.InvalidAuthenticationException; +import org.apache.nifi.registry.web.security.authentication.ProxiedEntitiesUtils; +import org.apache.nifi.registry.web.security.authentication.exception.UntrustedProxyException; +import org.apache.nifi.registry.web.security.authentication.jwt.JwtAuthenticationFilter; +import org.apache.nifi.registry.web.security.authentication.jwt.JwtAuthenticationProvider; +import org.apache.nifi.registry.web.security.authentication.jwt.JwtAuthenticationRequestToken; +import org.apache.nifi.registry.web.security.authentication.jwt.JwtService; +import org.apache.nifi.registry.web.security.authentication.token.LoginAuthenticationToken; +import org.apache.nifi.registry.web.security.authentication.token.NiFiAuthenticationToken; +import org.apache.nifi.registry.web.security.authentication.x509.X509AuthenticationProvider; +import org.apache.nifi.registry.web.security.authentication.x509.X509AuthenticationRequestToken; +import org.apache.nifi.registry.web.security.authentication.x509.X509CertificateExtractor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; import javax.ws.rs.GET; +import javax.ws.rs.NotAllowedException; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.net.URI; import java.security.cert.X509Certificate; +import java.util.concurrent.TimeUnit; @Component @Path("/access") @@ -62,11 +79,24 @@ public class AccessResource extends ApplicationResource { private X509CertificateExtractor certificateExtractor; private X509AuthenticationProvider x509AuthenticationProvider; private X509PrincipalExtractor x509principalExtractor; + private LoginIdentityProvider loginIdentityProvider; + private JwtAuthenticationProvider jwtAuthenticationProvider; + private JwtService jwtService; - public AccessResource(X509CertificateExtractor certificateExtractor, X509AuthenticationProvider x509AuthenticationProvider, X509PrincipalExtractor x509principalExtractor) { + @Autowired + public AccessResource(X509CertificateExtractor certificateExtractor, + X509AuthenticationProvider x509AuthenticationProvider, + X509PrincipalExtractor x509principalExtractor, + LoginIdentityProvider loginIdentityProvider, + JwtAuthenticationProvider jwtAuthenticationProvider, + JwtService jwtService + ) { this.certificateExtractor = certificateExtractor; this.x509AuthenticationProvider = x509AuthenticationProvider; this.x509principalExtractor = x509principalExtractor; + this.loginIdentityProvider = loginIdentityProvider; + this.jwtAuthenticationProvider = jwtAuthenticationProvider; + this.jwtService = jwtService; } /** @@ -78,20 +108,20 @@ public AccessResource(X509CertificateExtractor certificateExtractor, X509Authent @GET @Consumes(MediaType.WILDCARD) @Produces(MediaType.APPLICATION_JSON) - @Path("") @ApiOperation( value = "Gets the status the client's access", response = AccessStatus.class ) @ApiResponses({ - @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), - @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), - @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), - @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405), + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) public Response getAccessStatus(@Context HttpServletRequest httpServletRequest) { // only consider user specific access over https if (!httpServletRequest.isSecure()) { - throw new IllegalStateException("User authentication/authorization is only supported when running over HTTPS."); + throw new NotAllowedException("User authentication/authorization is only supported when running over HTTPS."); } final AccessStatus accessStatus = new AccessStatus(); @@ -102,36 +132,32 @@ public Response getAccessStatus(@Context HttpServletRequest httpServletRequest) // if there is not certificate, consider a token if (certificates == null) { - // TODO - add JWT Authentication support - throw new NotImplementedException("NiFi Registry client is trying to authentication with something other than a client cert. " + - "At this time, only client certificate authentication is supported."); - -// // look for an authorization token -// final String authorization = httpServletRequest.getHeader(JwtAuthenticationFilter.AUTHORIZATION); -// -// // if there is no authorization header, we don't know the user -// if (authorization == null) { -// accessStatus.setStatus(AccessStatus.Status.UNKNOWN.name()); -// accessStatus.setMessage("No credentials supplied, unknown user."); -// } else { -// try { -// // Extract the Base64 encoded token from the Authorization header -// final String token = StringUtils.substringAfterLast(authorization, " "); -// -// final JwtAuthenticationRequestToken jwtRequest = new JwtAuthenticationRequestToken(token, httpServletRequest.getRemoteAddr()); -// final NiFiAuthenticationToken authenticationResponse = (NiFiAuthenticationToken) jwtAuthenticationProvider.authenticate(jwtRequest); -// final NiFiUser nifiUser = ((NiFiUserDetails) authenticationResponse.getDetails()).getNiFiUser(); -// -// // set the user identity -// accessStatus.setIdentity(nifiUser.getIdentity()); -// -// // attempt authorize to /flow -// accessStatus.setStatus(AccessStatus.Status.ACTIVE.name()); -// accessStatus.setMessage("You are already logged in."); -// } catch (JwtException e) { -// throw new InvalidAuthenticationException(e.getMessage(), e); -// } -// } + // look for an authorization token + final String authorization = httpServletRequest.getHeader(JwtAuthenticationFilter.AUTHORIZATION); + + // if there is no authorization header, we don't know the user + if (authorization == null) { + accessStatus.setStatus(AccessStatus.Status.UNKNOWN.name()); + accessStatus.setMessage("No credentials supplied, unknown user."); + } else { + try { + // Extract the Base64 encoded token from the Authorization header + final String token = StringUtils.substringAfterLast(authorization, " "); + + final JwtAuthenticationRequestToken jwtRequest = new JwtAuthenticationRequestToken(token, httpServletRequest.getRemoteAddr()); + final NiFiAuthenticationToken authenticationResponse = (NiFiAuthenticationToken) jwtAuthenticationProvider.authenticate(jwtRequest); + final NiFiUser nifiUser = ((NiFiUserDetails) authenticationResponse.getDetails()).getNiFiUser(); + + // set the user identity + accessStatus.setIdentity(nifiUser.getIdentity()); + + // attempt authorize to /flow + accessStatus.setStatus(AccessStatus.Status.ACTIVE.name()); + accessStatus.setMessage("You are already logged in."); + } catch (JwtException e) { + throw new InvalidAuthenticationException(e.getMessage(), e); + } + } } else { try { final X509AuthenticationRequestToken x509Request = new X509AuthenticationRequestToken( @@ -159,6 +185,91 @@ public Response getAccessStatus(@Context HttpServletRequest httpServletRequest) return generateOkResponse(accessStatus).build(); } + /** + * Creates a token for accessing the REST API via username/password. + * + * @param httpServletRequest the servlet request + * @param username the username + * @param password the password + * @return A JWT (string) + */ + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.TEXT_PLAIN) + @Path("/token") + @ApiOperation( + value = "Creates a token for accessing the REST API via username/password", + notes = "The token returned is formatted as a JSON Web Token (JWT). The token is base64 encoded and comprised of three parts. The header, " + + "the body, and the signature. The expiration of the token is a contained within the body. The token can be used in the Authorization header " + + "in the format 'Authorization: Bearer '.", + response = String.class + ) + @ApiResponses({ + @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405 + ". The NiFi Registry may not be configured to support username/password login."), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_500) }) + public Response createAccessToken( + @Context HttpServletRequest httpServletRequest, + @FormParam("username") String username, + @FormParam("password") String password) { + + // only support access tokens when communicating over HTTPS + if (!httpServletRequest.isSecure()) { + throw new NotAllowedException("Access tokens are only issued over HTTPS"); + } + + // if not configuration for login, don't consider credentials + if (loginIdentityProvider == null) { + throw new NotAllowedException("Username/Password login not supported by this NiFi"); + } + + final LoginAuthenticationToken loginAuthenticationToken; + + // ensure we have login credentials + if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { + throw new IllegalArgumentException("The username and password must be specified"); + } + + try { + // attempt to authenticate + final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(new LoginCredentials(username, password)); + long expiration = validateTokenExpiration(authenticationResponse.getExpiration(), authenticationResponse.getIdentity()); + + // create the authentication token + loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration, authenticationResponse.getIssuer()); + } catch (final InvalidLoginCredentialsException ilce) { + throw new IllegalArgumentException("The supplied username and password are not valid.", ilce); + } catch (final IdentityAccessException iae) { + throw new AdministrationException(iae.getMessage(), iae); + } + + // generate JWT for response + final String token = jwtService.generateSignedToken(loginAuthenticationToken); + + // build the response + final URI uri = URI.create(generateResourceUri("access", "token")); + return generateCreatedResponse(uri, token).build(); + } + + private long validateTokenExpiration(long proposedTokenExpiration, String identity) { + final long maxExpiration = TimeUnit.MILLISECONDS.convert(12, TimeUnit.HOURS); + final long minExpiration = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES); + + if (proposedTokenExpiration > maxExpiration) { + logger.warn(String.format("Max token expiration exceeded. Setting expiration to %s from %s for %s", maxExpiration, + proposedTokenExpiration, identity)); + proposedTokenExpiration = maxExpiration; + } else if (proposedTokenExpiration < minExpiration) { + logger.warn(String.format("Min token expiration not met. Setting expiration to %s from %s for %s", minExpiration, + proposedTokenExpiration, identity)); + proposedTokenExpiration = minExpiration; + } + + return proposedTokenExpiration; + } + } diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java index d15685d48..58ee742f3 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java @@ -16,11 +16,11 @@ */ package org.apache.nifi.registry.web.api; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.RequestAction; -import org.apache.nifi.registry.authorization.resource.Authorizable; -import org.apache.nifi.registry.authorization.resource.ResourceType; -import org.apache.nifi.registry.authorization.user.NiFiUserUtils; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.resource.Authorizable; +import org.apache.nifi.registry.security.authorization.resource.ResourceType; +import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils; import org.apache.nifi.registry.bucket.BucketItem; import org.apache.nifi.registry.model.authorization.Resource; import org.apache.nifi.registry.service.AuthorizationService; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java index f626b3d8b..f2a419dc7 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java @@ -22,8 +22,8 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.RequestAction; import org.apache.nifi.registry.bucket.BucketItem; import org.apache.nifi.registry.exception.ResourceNotFoundException; import org.apache.nifi.registry.flow.VersionedFlow; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java index 8179b708a..5835ea025 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java @@ -22,10 +22,10 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.RequestAction; -import org.apache.nifi.registry.authorization.resource.Authorizable; -import org.apache.nifi.registry.authorization.user.NiFiUserUtils; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.resource.Authorizable; +import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils; import org.apache.nifi.registry.bucket.Bucket; import org.apache.nifi.registry.bucket.BucketItem; import org.apache.nifi.registry.service.AuthorizationService; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java index 6e7f34789..ce11b0d12 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java @@ -18,7 +18,7 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import org.apache.nifi.registry.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.Authorizer; import org.apache.nifi.registry.field.Fields; import org.apache.nifi.registry.service.AuthorizationService; import org.apache.nifi.registry.service.RegistryService; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/HttpStatusMessages.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/HttpStatusMessages.java index a3ba939d2..36e310ede 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/HttpStatusMessages.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/HttpStatusMessages.java @@ -23,6 +23,7 @@ class HttpStatusMessages { static final String MESSAGE_401 = "Client could not be authenticated."; static final String MESSAGE_403 = "Client is not authorized to make this request."; static final String MESSAGE_404 = "The specified resource could not be found."; + static final String MESSAGE_405 = "The HTTP method is not allowed for the specified resource for this NiFi Registry based to its configuration."; static final String MESSAGE_409 = "NiFi Registry was unable to complete the request because it assumes a server state that is not valid."; /* 5xx messages */ diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java index 458f0f070..8b8a3ed49 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java @@ -19,8 +19,8 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.RequestAction; import org.apache.nifi.registry.bucket.BucketItem; import org.apache.nifi.registry.service.AuthorizationService; import org.apache.nifi.registry.service.RegistryService; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ResourceResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ResourceResource.java index bcef5656a..d0cb879db 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ResourceResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ResourceResource.java @@ -20,10 +20,10 @@ import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.RequestAction; -import org.apache.nifi.registry.authorization.resource.Authorizable; -import org.apache.nifi.registry.authorization.user.NiFiUserUtils; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.resource.Authorizable; +import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils; import org.apache.nifi.registry.service.AuthorizationService; import org.apache.nifi.registry.model.authorization.Resource; import org.slf4j.Logger; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java index f6fd15fb8..9d5e89695 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java @@ -22,11 +22,11 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.AuthorizerCapabilityDetection; -import org.apache.nifi.registry.authorization.RequestAction; -import org.apache.nifi.registry.authorization.resource.Authorizable; -import org.apache.nifi.registry.authorization.user.NiFiUserUtils; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.AuthorizerCapabilityDetection; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.resource.Authorizable; +import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils; import org.apache.nifi.registry.model.authorization.User; import org.apache.nifi.registry.model.authorization.UserGroup; import org.apache.nifi.registry.service.AuthorizationService; @@ -39,6 +39,7 @@ import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.NotAllowedException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -458,13 +459,13 @@ public Response removeUserGroup( private void verifyAuthorizerIsManaged() { if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) { - throw new IllegalStateException(AuthorizationService.MSG_NON_MANAGED_AUTHORIZER); + throw new NotAllowedException(AuthorizationService.MSG_NON_MANAGED_AUTHORIZER); } } private void verifyAuthorizerSupportsConfigurableUserGroups() { if (!AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer)) { - throw new IllegalStateException(AuthorizationService.MSG_NON_CONFIGURABLE_USERS); + throw new NotAllowedException(AuthorizationService.MSG_NON_CONFIGURABLE_USERS); } } diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/AccessDeniedExceptionMapper.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/AccessDeniedExceptionMapper.java index 13ed44ff7..5b9e3eeca 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/AccessDeniedExceptionMapper.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/AccessDeniedExceptionMapper.java @@ -17,9 +17,9 @@ package org.apache.nifi.registry.web.mapper; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.authorization.exception.AccessDeniedException; -import org.apache.nifi.registry.authorization.user.NiFiUser; -import org.apache.nifi.registry.authorization.user.NiFiUserUtils; +import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException; +import org.apache.nifi.registry.security.authorization.user.NiFiUser; +import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/AuthorizationAccessExceptionMapper.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/AuthorizationAccessExceptionMapper.java index cc829ce08..ff4b7ec24 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/AuthorizationAccessExceptionMapper.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/AuthorizationAccessExceptionMapper.java @@ -16,7 +16,7 @@ */ package org.apache.nifi.registry.web.mapper; -import org.apache.nifi.registry.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/InvalidAuthenticationExceptionMapper.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/InvalidAuthenticationExceptionMapper.java index 74af1df70..15bb2272f 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/InvalidAuthenticationExceptionMapper.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/InvalidAuthenticationExceptionMapper.java @@ -17,7 +17,7 @@ package org.apache.nifi.registry.web.mapper; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.web.security.InvalidAuthenticationException; +import org.apache.nifi.registry.web.security.authentication.exception.InvalidAuthenticationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotAllowedExceptionMapper.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotAllowedExceptionMapper.java new file mode 100644 index 000000000..1867196db --- /dev/null +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotAllowedExceptionMapper.java @@ -0,0 +1,47 @@ +/* + * 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.web.mapper; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.ws.rs.NotAllowedException; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +@Component +@Provider +public class NotAllowedExceptionMapper implements ExceptionMapper { + + private static final Logger logger = LoggerFactory.getLogger(NotAllowedExceptionMapper.class); + + @Override + public Response toResponse(NotAllowedException exception) { + // log the error + logger.info(String.format("%s. Returning %s response.", exception, Response.Status.METHOD_NOT_ALLOWED)); + + if (logger.isDebugEnabled()) { + logger.debug(StringUtils.EMPTY, exception); + } + + return Response.status(Response.Status.METHOD_NOT_ALLOWED).entity(exception.getMessage()).type("text/plain").build(); + } + +} diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistrySecurityConfig.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java similarity index 57% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistrySecurityConfig.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java index 7652e900d..e0eddbac0 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistrySecurityConfig.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java @@ -14,22 +14,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web; - -import org.apache.nifi.registry.authorization.AuthorizableLookup; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.AuthorizerFactory; -import org.apache.nifi.registry.authorization.StandardAuthorizableLookup; -import org.apache.nifi.registry.authorization.StandardAuthorizerFactory; -import org.apache.nifi.registry.extension.ExtensionManager; +package org.apache.nifi.registry.web.security; + import org.apache.nifi.registry.properties.NiFiRegistryProperties; -import org.apache.nifi.registry.web.security.NiFiAnonymousUserFilter; -import org.apache.nifi.registry.web.security.x509.SubjectDnX509PrincipalExtractor; -import org.apache.nifi.registry.web.security.x509.X509AuthenticationFilter; -import org.apache.nifi.registry.web.security.x509.X509AuthenticationProvider; -import org.apache.nifi.registry.web.security.x509.X509CertificateExtractor; -import org.apache.nifi.registry.web.security.x509.X509CertificateValidator; -import org.apache.nifi.registry.web.security.x509.X509IdentityProvider; +import org.apache.nifi.registry.web.security.authentication.NiFiAnonymousUserFilter; +import org.apache.nifi.registry.web.security.authentication.jwt.JwtAuthenticationFilter; +import org.apache.nifi.registry.web.security.authentication.jwt.JwtAuthenticationProvider; +import org.apache.nifi.registry.web.security.authentication.x509.X509AuthenticationFilter; +import org.apache.nifi.registry.web.security.authentication.x509.X509AuthenticationProvider; +import org.apache.nifi.registry.web.security.authentication.x509.X509CertificateExtractor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -55,24 +48,23 @@ public class NiFiRegistrySecurityConfig extends WebSecurityConfigurerAdapter { private static final Logger logger = LoggerFactory.getLogger(NiFiRegistrySecurityConfig.class); - private final NiFiRegistryProperties properties; - private final ExtensionManager extensionManager; + @Autowired private NiFiRegistryProperties properties; + @Autowired X509CertificateExtractor certificateExtractor; + @Autowired X509PrincipalExtractor principalExtractor; + @Autowired private X509AuthenticationProvider x509AuthenticationProvider; private X509AuthenticationFilter x509AuthenticationFilter; -// private JwtAuthenticationFilter jwtAuthenticationFilter; -// private JwtAuthenticationProvider jwtAuthenticationProvider; -// + @Autowired private JwtAuthenticationProvider jwtAuthenticationProvider; + private JwtAuthenticationFilter jwtAuthenticationFilter; + +// @Autowired private OtpAuthenticationProvider otpAuthenticationProvider; // private OtpAuthenticationFilter otpAuthenticationFilter; -// private OtpAuthenticationProvider otpAuthenticationProvider; private NiFiAnonymousUserFilter anonymousAuthenticationFilter; - @Autowired - public NiFiRegistrySecurityConfig(final NiFiRegistryProperties properties, final ExtensionManager extensionManager) { + public NiFiRegistrySecurityConfig() { super(true); // disable defaults - this.properties = properties; - this.extensionManager = extensionManager; } @Override @@ -82,7 +74,7 @@ public void configure(WebSecurity webSecurity) throws Exception { // the /access/download-token endpoints) webSecurity .ignoring() - .antMatchers("/access", "/access/config", "/access/token"); + .antMatchers("/access", "/access/config", "/access/token"); } @Override @@ -99,7 +91,7 @@ protected void configure(HttpSecurity http) throws Exception { http.addFilterBefore(x509AuthenticationFilter(), AnonymousAuthenticationFilter.class); // jwt - // http.addFilterBefore(jwtFilterBean(), AnonymousAuthenticationFilter.class); + http.addFilterBefore(jwtFilterBean(), AnonymousAuthenticationFilter.class); // otp // http.addFilterBefore(otpFilterBean(), AnonymousAuthenticationFilter.class); @@ -118,22 +110,21 @@ public AuthenticationManager authenticationManagerBean() throws Exception { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth - .authenticationProvider(x509AuthenticationProvider()); - /* TODO, add Jwt and Otp support */ -// .authenticationProvider(jwtAuthenticationProvider) -// .authenticationProvider(otpAuthenticationProvider); + .authenticationProvider(x509AuthenticationProvider) + .authenticationProvider(jwtAuthenticationProvider); +// .authenticationProvider(otpAuthenticationProvider); // TODO OTP support + } + + @Bean + public JwtAuthenticationFilter jwtFilterBean() throws Exception { + if (jwtAuthenticationFilter == null) { + jwtAuthenticationFilter = new JwtAuthenticationFilter(); + jwtAuthenticationFilter.setProperties(properties); + jwtAuthenticationFilter.setAuthenticationManager(authenticationManager()); + } + return jwtAuthenticationFilter; } -// @Bean // TODO JwtAuthenticationFilter -// public JwtAuthenticationFilter jwtFilterBean() throws Exception { -// if (jwtAuthenticationFilter == null) { -// jwtAuthenticationFilter = new JwtAuthenticationFilter(); -// jwtAuthenticationFilter.setProperties(properties); -// jwtAuthenticationFilter.setAuthenticationManager(authenticationManager()); -// } -// return jwtAuthenticationFilter; -// } -// // @Bean // TODO OtpAuthenticationFilter // public OtpAuthenticationFilter otpFilterBean() throws Exception { // if (otpAuthenticationFilter == null) { @@ -149,8 +140,8 @@ public X509AuthenticationFilter x509AuthenticationFilter() throws Exception { if (x509AuthenticationFilter == null) { x509AuthenticationFilter = new X509AuthenticationFilter(); x509AuthenticationFilter.setProperties(properties); - x509AuthenticationFilter.setCertificateExtractor(certificateExtractor()); - x509AuthenticationFilter.setPrincipalExtractor(principalExtractor()); + x509AuthenticationFilter.setCertificateExtractor(certificateExtractor); + x509AuthenticationFilter.setPrincipalExtractor(principalExtractor); x509AuthenticationFilter.setAuthenticationManager(authenticationManager()); } return x509AuthenticationFilter; @@ -164,58 +155,4 @@ public NiFiAnonymousUserFilter anonymousFilter() throws Exception { return anonymousAuthenticationFilter; } - @Bean - public X509CertificateExtractor certificateExtractor() { - return new X509CertificateExtractor(); - } - - public X509CertificateValidator certificateValidator() { - return new X509CertificateValidator(); - } - - @Bean - public X509PrincipalExtractor principalExtractor() { - return new SubjectDnX509PrincipalExtractor(); - } - - @Bean - public X509IdentityProvider x509IdentityProvider() { - X509IdentityProvider x509IdentityProvider = new X509IdentityProvider(); - x509IdentityProvider.setCertificateValidator(certificateValidator()); - x509IdentityProvider.setPrincipalExtractor(principalExtractor()); - return x509IdentityProvider; - } - - @Bean - public X509AuthenticationProvider x509AuthenticationProvider() { - return new X509AuthenticationProvider(x509IdentityProvider(), authorizer(), this.properties); - } - - @Bean(initMethod = "initialize") - public AuthorizerFactory authorizerFactory() { - return new StandardAuthorizerFactory(this.properties, this.extensionManager); - } - - @Bean - public Authorizer authorizer() { - return authorizerFactory().getAuthorizer(); - } - - @Bean - public AuthorizableLookup authorizableLookup() { - return new StandardAuthorizableLookup(); - } - - - // TODO, add Jwt and Otp support -// @Autowired -// public void setJwtAuthenticationProvider(JwtAuthenticationProvider jwtAuthenticationProvider) { -// this.jwtAuthenticationProvider = jwtAuthenticationProvider; -// } -// -// @Autowired -// public void setOtpAuthenticationProvider(OtpAuthenticationProvider otpAuthenticationProvider) { -// this.otpAuthenticationProvider = otpAuthenticationProvider; -// } - } diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/LoginIdentityProviderFactory.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/LoginIdentityProviderFactory.java new file mode 100644 index 000000000..a3e77b050 --- /dev/null +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/LoginIdentityProviderFactory.java @@ -0,0 +1,256 @@ +/* + * 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.web.security.authentication; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.security.authentication.LoginIdentityProvider; +import org.apache.nifi.registry.security.authentication.LoginIdentityProviderConfigurationContext; +import org.apache.nifi.registry.security.authentication.LoginIdentityProviderLookup; +import org.apache.nifi.registry.security.authentication.annotation.LoginIdentityProviderContext; +import org.apache.nifi.registry.extension.ExtensionManager; +import org.apache.nifi.registry.properties.NiFiRegistryProperties; +import org.apache.nifi.registry.security.authentication.generated.LoginIdentityProviders; +import org.apache.nifi.registry.security.authentication.generated.Property; +import org.apache.nifi.registry.security.authentication.generated.Provider; +import org.apache.nifi.registry.security.util.XmlUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.xml.sax.SAXException; + +import javax.xml.XMLConstants; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class LoginIdentityProviderFactory implements LoginIdentityProviderLookup { + + private static final Logger logger = LoggerFactory.getLogger(LoginIdentityProviderFactory.class); + private static final String LOGIN_IDENTITY_PROVIDERS_XSD = "/identity-providers.xsd"; + private static final String JAXB_GENERATED_PATH = "org.apache.nifi.registry.security.authentication.generated"; + private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext(); + + private static JAXBContext initializeJaxbContext() { + try { + return JAXBContext.newInstance(JAXB_GENERATED_PATH, LoginIdentityProviderFactory.class.getClassLoader()); + } catch (JAXBException e) { + throw new RuntimeException("Unable to create JAXBContext."); + } + } + + private NiFiRegistryProperties properties; + private ExtensionManager extensionManager; + private LoginIdentityProvider loginIdentityProvider; + private final Map loginIdentityProviders = new HashMap<>(); + + @Autowired + public LoginIdentityProviderFactory(final NiFiRegistryProperties properties, final ExtensionManager extensionManager) { + this.properties = properties; + this.extensionManager = extensionManager; + + if (this.properties == null) { + throw new IllegalStateException("NiFiRegistryProperties cannot be null"); + } + + if (this.extensionManager == null) { + throw new IllegalStateException("ExtensionManager cannot be null"); + } + } + + @Override + public LoginIdentityProvider getLoginIdentityProvider(String identifier) { + return loginIdentityProviders.get(identifier); + } + + @Bean + public LoginIdentityProvider getLoginIdentityProvider() throws Exception { + if (loginIdentityProvider == null) { + // look up the login identity provider to use + final String loginIdentityProviderIdentifier = properties.getProperty(NiFiRegistryProperties.SECURITY_IDENTITY_PROVIDER); + + // ensure the login identity provider class name was specified + if (StringUtils.isNotBlank(loginIdentityProviderIdentifier)) { + final LoginIdentityProviders loginIdentityProviderConfiguration = loadLoginIdentityProvidersConfiguration(); + + // create each login identity provider + for (final Provider provider : loginIdentityProviderConfiguration.getProvider()) { + loginIdentityProviders.put(provider.getIdentifier(), createLoginIdentityProvider(provider.getIdentifier(), provider.getClazz())); + } + + // configure each login identity provider + for (final Provider provider : loginIdentityProviderConfiguration.getProvider()) { + final LoginIdentityProvider instance = loginIdentityProviders.get(provider.getIdentifier()); + instance.onConfigured(loadLoginIdentityProviderConfiguration(provider)); + } + + // get the login identity provider instance + loginIdentityProvider = getLoginIdentityProvider(loginIdentityProviderIdentifier); + + // ensure it was found + if (loginIdentityProvider == null) { + throw new Exception(String.format("The specified login identity provider '%s' could not be found.", loginIdentityProviderIdentifier)); + } + } + } + + return loginIdentityProvider; + } + + private LoginIdentityProviders loadLoginIdentityProvidersConfiguration() throws Exception { + final File loginIdentityProvidersConfigurationFile = properties.getIdentityProviderConfigurationFile(); + + // load the users from the specified file + if (loginIdentityProvidersConfigurationFile.exists()) { + try { + // find the schema + final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + final Schema schema = schemaFactory.newSchema(LoginIdentityProviders.class.getResource(LOGIN_IDENTITY_PROVIDERS_XSD)); + + // attempt to unmarshal + XMLStreamReader xsr = XmlUtils.createSafeReader(new StreamSource(loginIdentityProvidersConfigurationFile)); + final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller(); + unmarshaller.setSchema(schema); + final JAXBElement element = unmarshaller.unmarshal(xsr, LoginIdentityProviders.class); + return element.getValue(); + } catch (SAXException | JAXBException e) { + throw new Exception("Unable to load the login identity provider configuration file at: " + loginIdentityProvidersConfigurationFile.getAbsolutePath()); + } + } else { + throw new Exception("Unable to find the login identity provider configuration file at " + loginIdentityProvidersConfigurationFile.getAbsolutePath()); + } + } + + private LoginIdentityProvider createLoginIdentityProvider(final String identifier, final String loginIdentityProviderClassName) throws Exception { + final LoginIdentityProvider instance; + + final ClassLoader classLoader = extensionManager.getExtensionClassLoader(loginIdentityProviderClassName); + if (classLoader == null) { + throw new IllegalStateException("Extension not found in any of the configured class loaders: " + loginIdentityProviderClassName); + } + + // attempt to load the class + Class rawLoginIdentityProviderClass = Class.forName(loginIdentityProviderClassName, true, classLoader); + Class loginIdentityProviderClass = rawLoginIdentityProviderClass.asSubclass(LoginIdentityProvider.class); + + // otherwise create a new instance + Constructor constructor = loginIdentityProviderClass.getConstructor(); + instance = (LoginIdentityProvider) constructor.newInstance(); + + // method injection + performMethodInjection(instance, loginIdentityProviderClass); + + // field injection + performFieldInjection(instance, loginIdentityProviderClass); + + // call post construction lifecycle event + instance.initialize(new StandardLoginIdentityProviderInitializationContext(identifier, this)); + + return instance; + } + + private LoginIdentityProviderConfigurationContext loadLoginIdentityProviderConfiguration(final Provider provider) { + final Map providerProperties = new HashMap<>(); + + for (final Property property : provider.getProperty()) { + providerProperties.put(property.getName(), property.getValue()); + } + + return new StandardLoginIdentityProviderConfigurationContext(provider.getIdentifier(), providerProperties); + } + + private void performMethodInjection(final LoginIdentityProvider instance, final Class loginIdentityProviderClass) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + + for (final Method method : loginIdentityProviderClass.getMethods()) { + if (method.isAnnotationPresent(LoginIdentityProviderContext.class)) { + // make the method accessible + final boolean isAccessible = method.isAccessible(); + method.setAccessible(true); + + try { + final Class[] argumentTypes = method.getParameterTypes(); + + // look for setters (single argument) + if (argumentTypes.length == 1) { + final Class argumentType = argumentTypes[0]; + + // look for well known types + if (NiFiRegistryProperties.class.isAssignableFrom(argumentType)) { + // nifi properties injection + method.invoke(instance, properties); + } + } + } finally { + method.setAccessible(isAccessible); + } + } + } + + final Class parentClass = loginIdentityProviderClass.getSuperclass(); + if (parentClass != null && LoginIdentityProvider.class.isAssignableFrom(parentClass)) { + performMethodInjection(instance, parentClass); + } + } + + private void performFieldInjection(final LoginIdentityProvider instance, final Class loginIdentityProviderClass) throws IllegalArgumentException, IllegalAccessException { + for (final Field field : loginIdentityProviderClass.getDeclaredFields()) { + if (field.isAnnotationPresent(LoginIdentityProviderContext.class)) { + // make the method accessible + final boolean isAccessible = field.isAccessible(); + field.setAccessible(true); + + try { + // get the type + final Class fieldType = field.getType(); + + // only consider this field if it isn't set yet + if (field.get(instance) == null) { + // look for well known types + if (NiFiRegistryProperties.class.isAssignableFrom(fieldType)) { + // nifi properties injection + field.set(instance, properties); + } + } + + } finally { + field.setAccessible(isAccessible); + } + } + } + + final Class parentClass = loginIdentityProviderClass.getSuperclass(); + if (parentClass != null && LoginIdentityProvider.class.isAssignableFrom(parentClass)) { + performFieldInjection(instance, parentClass); + } + } + +} diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiAnonymousUserFilter.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/NiFiAnonymousUserFilter.java similarity index 82% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiAnonymousUserFilter.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/NiFiAnonymousUserFilter.java index 3715bc7bb..a27fd1836 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiAnonymousUserFilter.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/NiFiAnonymousUserFilter.java @@ -14,11 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security; +package org.apache.nifi.registry.web.security.authentication; -import org.apache.nifi.registry.authorization.user.NiFiUserDetails; -import org.apache.nifi.registry.authorization.user.StandardNiFiUser; -import org.apache.nifi.registry.web.security.token.NiFiAuthenticationToken; +import org.apache.nifi.registry.security.authorization.user.NiFiUserDetails; +import org.apache.nifi.registry.security.authorization.user.StandardNiFiUser; +import org.apache.nifi.registry.web.security.authentication.token.NiFiAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiAuthenticationFilter.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/NiFiAuthenticationFilter.java similarity index 95% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiAuthenticationFilter.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/NiFiAuthenticationFilter.java index 7dfc3dc66..24af504c2 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiAuthenticationFilter.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/NiFiAuthenticationFilter.java @@ -14,11 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security; +package org.apache.nifi.registry.web.security.authentication; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.authorization.user.NiFiUserUtils; +import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils; import org.apache.nifi.registry.properties.NiFiRegistryProperties; +import org.apache.nifi.registry.web.security.authentication.exception.InvalidAuthenticationException; +import org.apache.nifi.registry.web.security.authentication.exception.UntrustedProxyException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.AuthenticationManager; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiAuthenticationProvider.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/NiFiAuthenticationProvider.java similarity index 88% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiAuthenticationProvider.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/NiFiAuthenticationProvider.java index 6bc052bed..5617e0ef9 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiAuthenticationProvider.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/NiFiAuthenticationProvider.java @@ -14,13 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security; +package org.apache.nifi.registry.web.security.authentication; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.Group; -import org.apache.nifi.registry.authorization.ManagedAuthorizer; -import org.apache.nifi.registry.authorization.UserAndGroups; -import org.apache.nifi.registry.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.Group; +import org.apache.nifi.registry.security.authorization.ManagedAuthorizer; +import org.apache.nifi.registry.security.authorization.UserAndGroups; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; import org.apache.nifi.registry.properties.NiFiRegistryProperties; import org.apache.nifi.registry.properties.util.IdentityMapping; import org.apache.nifi.registry.properties.util.IdentityMappingUtil; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiAuthenticationRequestToken.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/NiFiAuthenticationRequestToken.java similarity index 95% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiAuthenticationRequestToken.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/NiFiAuthenticationRequestToken.java index c1f44ef09..3da95c5ff 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiAuthenticationRequestToken.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/NiFiAuthenticationRequestToken.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security; +package org.apache.nifi.registry.web.security.authentication; import org.springframework.security.authentication.AbstractAuthenticationToken; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/ProxiedEntitiesUtils.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/ProxiedEntitiesUtils.java similarity index 96% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/ProxiedEntitiesUtils.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/ProxiedEntitiesUtils.java index 33015fc4b..05687f8dd 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/ProxiedEntitiesUtils.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/ProxiedEntitiesUtils.java @@ -14,11 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security; +package org.apache.nifi.registry.web.security.authentication; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.authorization.user.NiFiUser; -import org.apache.nifi.registry.authorization.user.NiFiUserUtils; +import org.apache.nifi.registry.security.authorization.user.NiFiUser; +import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/StandardLoginIdentityProviderConfigurationContext.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/StandardLoginIdentityProviderConfigurationContext.java new file mode 100644 index 000000000..3a9cdd6d4 --- /dev/null +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/StandardLoginIdentityProviderConfigurationContext.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.web.security.authentication; + +import org.apache.nifi.registry.security.authentication.LoginIdentityProviderConfigurationContext; + +import java.util.Collections; +import java.util.Map; + +/** + * + */ +public class StandardLoginIdentityProviderConfigurationContext implements LoginIdentityProviderConfigurationContext { + + private final String identifier; + private final Map properties; + + public StandardLoginIdentityProviderConfigurationContext(String identifier, Map properties) { + this.identifier = identifier; + this.properties = properties; + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public Map getProperties() { + return Collections.unmodifiableMap(properties); + } + + @Override + public String getProperty(String property) { + return properties.get(property); + } + +} diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/StandardLoginIdentityProviderInitializationContext.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/StandardLoginIdentityProviderInitializationContext.java new file mode 100644 index 000000000..e8fba2e19 --- /dev/null +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/StandardLoginIdentityProviderInitializationContext.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.web.security.authentication; + +import org.apache.nifi.registry.security.authentication.LoginIdentityProviderInitializationContext; +import org.apache.nifi.registry.security.authentication.LoginIdentityProviderLookup; + +/** + * + */ +public class StandardLoginIdentityProviderInitializationContext implements LoginIdentityProviderInitializationContext { + + private final String identifier; + private final LoginIdentityProviderLookup lookup; + + public StandardLoginIdentityProviderInitializationContext(String identifier, final LoginIdentityProviderLookup lookup) { + this.identifier = identifier; + this.lookup = lookup; + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public LoginIdentityProviderLookup getAuthorityProviderLookup() { + return lookup; + } + +} diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/InvalidAuthenticationException.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/InvalidAuthenticationException.java similarity index 94% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/InvalidAuthenticationException.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/InvalidAuthenticationException.java index c7a1aead3..016e9cb19 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/InvalidAuthenticationException.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/InvalidAuthenticationException.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security; +package org.apache.nifi.registry.web.security.authentication.exception; import org.springframework.security.core.AuthenticationException; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/UntrustedProxyException.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/UntrustedProxyException.java similarity index 93% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/UntrustedProxyException.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/UntrustedProxyException.java index fad8cc166..6245ac2ea 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/UntrustedProxyException.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/UntrustedProxyException.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security; +package org.apache.nifi.registry.web.security.authentication.exception; import org.springframework.security.core.AuthenticationException; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtAuthenticationFilter.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtAuthenticationFilter.java new file mode 100644 index 000000000..1e5c194f7 --- /dev/null +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtAuthenticationFilter.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.registry.web.security.authentication.jwt; + +import org.apache.commons.lang3.StringUtils; + +import org.apache.nifi.registry.web.security.authentication.NiFiAuthenticationFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; + +import javax.servlet.http.HttpServletRequest; + +/** + */ +public class JwtAuthenticationFilter extends NiFiAuthenticationFilter { + + private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class); + + public static final String AUTHORIZATION = "Authorization"; + public static final String BEARER = "Bearer "; + + @Override + public Authentication attemptAuthentication(final HttpServletRequest request) { + // only support jwt login when running securely + if (!request.isSecure()) { + return null; + } + + // TODO: Refactor request header extraction logic to shared utility as it is duplicated in AccessResource + + // get the principal out of the user token + final String authorization = request.getHeader(AUTHORIZATION); + + // if there is no authorization header, we don't know the user + if (authorization == null || !StringUtils.startsWith(authorization, BEARER)) { + return null; + } else { + // Extract the Base64 encoded token from the Authorization header + final String token = StringUtils.substringAfterLast(authorization, " "); + return new JwtAuthenticationRequestToken(token, request.getRemoteAddr()); + } + } +} diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtAuthenticationProvider.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtAuthenticationProvider.java new file mode 100644 index 000000000..224792359 --- /dev/null +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtAuthenticationProvider.java @@ -0,0 +1,69 @@ +/* + * 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.web.security.authentication.jwt; + +import io.jsonwebtoken.JwtException; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.user.NiFiUser; +import org.apache.nifi.registry.security.authorization.user.NiFiUserDetails; +import org.apache.nifi.registry.security.authorization.user.StandardNiFiUser; +import org.apache.nifi.registry.properties.NiFiRegistryProperties; +import org.apache.nifi.registry.web.security.authentication.exception.InvalidAuthenticationException; +import org.apache.nifi.registry.web.security.authentication.NiFiAuthenticationProvider; +import org.apache.nifi.registry.web.security.authentication.token.NiFiAuthenticationToken; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.stereotype.Component; + +/** + * + */ +@Component +public class JwtAuthenticationProvider extends NiFiAuthenticationProvider { + + private final JwtService jwtService; + + @Autowired + public JwtAuthenticationProvider(JwtService jwtService, NiFiRegistryProperties nifiProperties, Authorizer authorizer) { + super(nifiProperties, authorizer); + this.jwtService = jwtService; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + final JwtAuthenticationRequestToken request = (JwtAuthenticationRequestToken) authentication; + + try { + final String jwtPrincipal = jwtService.getAuthenticationFromToken(request.getToken()); + final String mappedIdentity = mapIdentity(jwtPrincipal); + final NiFiUser user = new StandardNiFiUser.Builder() + .identity(mappedIdentity) + .groups(getUserGroups(mappedIdentity)) + .clientAddress(request.getClientAddress()) + .build(); + return new NiFiAuthenticationToken(new NiFiUserDetails(user)); + } catch (JwtException e) { + throw new InvalidAuthenticationException(e.getMessage(), e); + } + } + + @Override + public boolean supports(Class authentication) { + return JwtAuthenticationRequestToken.class.isAssignableFrom(authentication); + } +} diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/OtpAuthenticationToken.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtAuthenticationRequestToken.java similarity index 55% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/OtpAuthenticationToken.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtAuthenticationRequestToken.java index f49e97d4d..e8af9ff4c 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/OtpAuthenticationToken.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtAuthenticationRequestToken.java @@ -14,26 +14,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security.token; +package org.apache.nifi.registry.web.security.authentication.jwt; -import org.springframework.security.authentication.AbstractAuthenticationToken; + +import org.apache.nifi.registry.web.security.authentication.NiFiAuthenticationRequestToken; /** - * This is an Authentication Token for logging in. Once a user is authenticated, they can be issued an ID token. + * This is an authentication request with a given JWT token. */ -public class OtpAuthenticationToken extends AbstractAuthenticationToken { +public class JwtAuthenticationRequestToken extends NiFiAuthenticationRequestToken { - private final String identity; + private final String token; /** - * Creates a representation of the otp authentication token for a user. + * Creates a representation of the jwt authentication request for a user. * - * @param identity The unique identifier for this user + * @param token The unique token for this user + * @param clientAddress the address of the client making the request */ - public OtpAuthenticationToken(final String identity) { - super(null); - setAuthenticated(true); - this.identity = identity; + public JwtAuthenticationRequestToken(final String token, final String clientAddress) { + super(clientAddress); + setAuthenticated(false); + this.token = token; } @Override @@ -43,14 +45,16 @@ public Object getCredentials() { @Override public Object getPrincipal() { - return identity; + return token; + } + + public String getToken() { + return token; } @Override public String toString() { - return new StringBuilder("OtpAuthenticationToken for ") - .append(getName()) - .toString(); + return ""; } } diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java new file mode 100644 index 000000000..49c17ea35 --- /dev/null +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java @@ -0,0 +1,160 @@ +/* + * 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.web.security.authentication.jwt; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.JwsHeader; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.SignatureException; +import io.jsonwebtoken.SigningKeyResolverAdapter; +import io.jsonwebtoken.UnsupportedJwtException; +import org.apache.commons.lang3.StringUtils; + +import org.apache.nifi.registry.exception.AdministrationException; +import org.apache.nifi.registry.security.key.Key; +import org.apache.nifi.registry.security.key.KeyService; +import org.apache.nifi.registry.web.security.authentication.token.LoginAuthenticationToken; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.nio.charset.StandardCharsets; +import java.util.Calendar; + +@Service +public class JwtService { + + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JwtService.class); + + private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256; + private static final String KEY_ID_CLAIM = "kid"; + private static final String USERNAME_CLAIM = "preferred_username"; + + private final KeyService keyService; + + public JwtService(final KeyService keyService) { + this.keyService = keyService; + } + + public String getAuthenticationFromToken(final String base64EncodedToken) throws JwtException { + // The library representations of the JWT should be kept internal to this service. + try { + final Jws jws = parseTokenFromBase64EncodedString(base64EncodedToken); + + if (jws == null) { + throw new JwtException("Unable to parse token"); + } + + // Additional validation that subject is present + if (StringUtils.isEmpty(jws.getBody().getSubject())) { + throw new JwtException("No subject available in token"); + } + + // TODO: Validate issuer against active registry? + if (StringUtils.isEmpty(jws.getBody().getIssuer())) { + throw new JwtException("No issuer available in token"); + } + return jws.getBody().getSubject(); + } catch (JwtException e) { + logger.debug("The Base64 encoded JWT: " + base64EncodedToken); + final String errorMessage = "There was an error validating the JWT"; + logger.error(errorMessage, e); + throw e; + } + } + + private Jws parseTokenFromBase64EncodedString(final String base64EncodedToken) throws JwtException { + try { + return Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() { + @Override + public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) { + final String identity = claims.getSubject(); + + // Get the key based on the key id in the claims + final String keyId = claims.get(KEY_ID_CLAIM, String.class); + final Key key = keyService.getKey(keyId); + + // Ensure we were able to find a key that was previously issued by this key service for this user + if (key == null || key.getKey() == null) { + throw new UnsupportedJwtException("Unable to determine signing key for " + identity + " [kid: " + keyId + "]"); + } + + return key.getKey().getBytes(StandardCharsets.UTF_8); + } + }).parseClaimsJws(base64EncodedToken); + } catch (final MalformedJwtException | UnsupportedJwtException | SignatureException | ExpiredJwtException | IllegalArgumentException | AdministrationException e) { + // TODO: Exercise all exceptions to ensure none leak key material to logs + final String errorMessage = "Unable to validate the access token."; + throw new JwtException(errorMessage, e); + } + } + + /** + * Generates a signed JWT token from the provided (Spring Security) login authentication token. + * + * @param authenticationToken an instance of the Spring Security token after login credentials have been verified against the respective information source + * @return a signed JWT containing the user identity and the identity provider, Base64-encoded + * @throws JwtException if there is a problem generating the signed token + */ + public String generateSignedToken(final LoginAuthenticationToken authenticationToken) throws JwtException { + if (authenticationToken == null) { + throw new IllegalArgumentException("Cannot generate a JWT for a null authentication token"); + } + + // Set expiration from the token + final Calendar expiration = Calendar.getInstance(); + expiration.setTimeInMillis(authenticationToken.getExpiration()); + + final Object principal = authenticationToken.getPrincipal(); + if (principal == null || StringUtils.isEmpty(principal.toString())) { + final String errorMessage = "Cannot generate a JWT for a token with an empty identity issued by " + authenticationToken.getIssuer(); + logger.error(errorMessage); + throw new JwtException(errorMessage); + } + + // Create a JWT with the specified authentication + final String identity = principal.toString(); + final String username = authenticationToken.getName(); + + try { + // Get/create the key for this user + final Key key = keyService.getOrCreateKey(identity); + final byte[] keyBytes = key.getKey().getBytes(StandardCharsets.UTF_8); + + logger.trace("Generating JWT for " + authenticationToken); + + // TODO: Implement "jti" claim with nonce to prevent replay attacks and allow blacklisting of revoked tokens + // Build the token + return Jwts.builder().setSubject(identity) + .setIssuer(authenticationToken.getIssuer()) + .setAudience(authenticationToken.getIssuer()) + .claim(USERNAME_CLAIM, username) + .claim(KEY_ID_CLAIM, key.getId()) + .setExpiration(expiration.getTime()) + .setIssuedAt(Calendar.getInstance().getTime()) + .signWith(SIGNATURE_ALGORITHM, keyBytes).compact(); + } catch (NullPointerException | AdministrationException e) { + final String errorMessage = "Could not retrieve the signing key for JWT for " + identity; + logger.error(errorMessage, e); + throw new JwtException(errorMessage, e); + } + } +} diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/LoginAuthenticationToken.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/token/LoginAuthenticationToken.java similarity index 98% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/LoginAuthenticationToken.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/token/LoginAuthenticationToken.java index 86c86346a..08f063764 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/LoginAuthenticationToken.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/token/LoginAuthenticationToken.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security.token; +package org.apache.nifi.registry.web.security.authentication.token; import org.apache.nifi.registry.security.util.CertificateUtils; import org.springframework.security.authentication.AbstractAuthenticationToken; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/NiFiAuthenticationToken.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/token/NiFiAuthenticationToken.java similarity index 96% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/NiFiAuthenticationToken.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/token/NiFiAuthenticationToken.java index 5ea9e5b0d..19e56c58f 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/token/NiFiAuthenticationToken.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/token/NiFiAuthenticationToken.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security.token; +package org.apache.nifi.registry.web.security.authentication.token; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.userdetails.UserDetails; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/SubjectDnX509PrincipalExtractor.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/SubjectDnX509PrincipalExtractor.java similarity index 90% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/SubjectDnX509PrincipalExtractor.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/SubjectDnX509PrincipalExtractor.java index f8ab1fe67..a9deae19e 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/SubjectDnX509PrincipalExtractor.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/SubjectDnX509PrincipalExtractor.java @@ -14,15 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security.x509; +package org.apache.nifi.registry.web.security.authentication.x509; import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor; +import org.springframework.stereotype.Component; import java.security.cert.X509Certificate; /** * Principal extractor for extracting a DN. */ +@Component public class SubjectDnX509PrincipalExtractor implements X509PrincipalExtractor { @Override diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509AuthenticationFilter.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509AuthenticationFilter.java similarity index 91% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509AuthenticationFilter.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509AuthenticationFilter.java index df8bbbf47..fa0fce255 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509AuthenticationFilter.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509AuthenticationFilter.java @@ -14,10 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security.x509; +package org.apache.nifi.registry.web.security.authentication.x509; -import org.apache.nifi.registry.web.security.NiFiAuthenticationFilter; -import org.apache.nifi.registry.web.security.ProxiedEntitiesUtils; +import org.apache.nifi.registry.web.security.authentication.NiFiAuthenticationFilter; +import org.apache.nifi.registry.web.security.authentication.ProxiedEntitiesUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509AuthenticationProvider.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509AuthenticationProvider.java similarity index 82% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509AuthenticationProvider.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509AuthenticationProvider.java index 5ed7859c7..3e935a235 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509AuthenticationProvider.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509AuthenticationProvider.java @@ -14,28 +14,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security.x509; +package org.apache.nifi.registry.web.security.authentication.x509; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.authorization.exception.AccessDeniedException; -import org.apache.nifi.registry.authorization.Authorizer; -import org.apache.nifi.registry.authorization.RequestAction; -import org.apache.nifi.registry.authorization.Resource; -import org.apache.nifi.registry.authorization.UserContextKeys; -import org.apache.nifi.registry.authorization.resource.Authorizable; -import org.apache.nifi.registry.authorization.resource.ResourceFactory; -import org.apache.nifi.registry.authorization.user.NiFiUser; -import org.apache.nifi.registry.authorization.user.NiFiUserDetails; -import org.apache.nifi.registry.authorization.user.StandardNiFiUser; +import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.Resource; +import org.apache.nifi.registry.security.authorization.UserContextKeys; +import org.apache.nifi.registry.security.authorization.resource.Authorizable; +import org.apache.nifi.registry.security.authorization.resource.ResourceFactory; +import org.apache.nifi.registry.security.authorization.user.NiFiUser; +import org.apache.nifi.registry.security.authorization.user.NiFiUserDetails; +import org.apache.nifi.registry.security.authorization.user.StandardNiFiUser; import org.apache.nifi.registry.properties.NiFiRegistryProperties; import org.apache.nifi.registry.web.response.AuthenticationResponse; -import org.apache.nifi.registry.web.security.InvalidAuthenticationException; -import org.apache.nifi.registry.web.security.NiFiAuthenticationProvider; -import org.apache.nifi.registry.web.security.ProxiedEntitiesUtils; -import org.apache.nifi.registry.web.security.UntrustedProxyException; -import org.apache.nifi.registry.web.security.token.NiFiAuthenticationToken; +import org.apache.nifi.registry.web.security.authentication.exception.InvalidAuthenticationException; +import org.apache.nifi.registry.web.security.authentication.NiFiAuthenticationProvider; +import org.apache.nifi.registry.web.security.authentication.ProxiedEntitiesUtils; +import org.apache.nifi.registry.web.security.authentication.exception.UntrustedProxyException; +import org.apache.nifi.registry.web.security.authentication.token.NiFiAuthenticationToken; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.HashMap; @@ -44,9 +46,7 @@ import java.util.Map; import java.util.Set; -/** - * - */ +@Component public class X509AuthenticationProvider extends NiFiAuthenticationProvider { private static final Authorizable PROXY_AUTHORIZABLE = new Authorizable() { @@ -64,6 +64,7 @@ public Resource getResource() { private X509IdentityProvider certificateIdentityProvider; private Authorizer authorizer; + @Autowired public X509AuthenticationProvider( final X509IdentityProvider certificateIdentityProvider, final Authorizer authorizer, diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509AuthenticationRequestToken.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509AuthenticationRequestToken.java similarity index 94% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509AuthenticationRequestToken.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509AuthenticationRequestToken.java index 22bc8bd55..d5aca23a2 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509AuthenticationRequestToken.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509AuthenticationRequestToken.java @@ -14,10 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security.x509; +package org.apache.nifi.registry.web.security.authentication.x509; import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.registry.web.security.NiFiAuthenticationRequestToken; +import org.apache.nifi.registry.web.security.authentication.NiFiAuthenticationRequestToken; import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor; import java.security.cert.X509Certificate; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509CertificateExtractor.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509CertificateExtractor.java similarity index 93% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509CertificateExtractor.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509CertificateExtractor.java index ad62b3741..34ceada29 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509CertificateExtractor.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509CertificateExtractor.java @@ -14,10 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security.x509; +package org.apache.nifi.registry.web.security.authentication.x509; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.security.cert.X509Certificate; @@ -25,6 +26,7 @@ /** * Extracts client certificates from Http requests. */ +@Component public class X509CertificateExtractor { private final Logger logger = LoggerFactory.getLogger(getClass()); diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509CertificateValidator.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509CertificateValidator.java similarity index 93% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509CertificateValidator.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509CertificateValidator.java index fe2c3e5b4..d748b9390 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509CertificateValidator.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509CertificateValidator.java @@ -14,10 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security.x509; +package org.apache.nifi.registry.web.security.authentication.x509; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; @@ -26,6 +27,7 @@ /** * Extracts client certificates from Http requests. */ +@Component public class X509CertificateValidator { private final Logger logger = LoggerFactory.getLogger(getClass()); diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509IdentityProvider.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509IdentityProvider.java similarity index 89% rename from nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509IdentityProvider.java rename to nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509IdentityProvider.java index 38aa0b415..692b31871 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/x509/X509IdentityProvider.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/x509/X509IdentityProvider.java @@ -14,12 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.registry.web.security.x509; +package org.apache.nifi.registry.web.security.authentication.x509; import org.apache.nifi.registry.web.response.AuthenticationResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor; +import org.springframework.stereotype.Component; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; @@ -29,6 +31,7 @@ /** * Identity provider for extract the authenticating a ServletRequest with a X509Certificate. */ +@Component public class X509IdentityProvider { private static final Logger logger = LoggerFactory.getLogger(X509IdentityProvider.class); @@ -38,6 +41,12 @@ public class X509IdentityProvider { private X509CertificateValidator certificateValidator; private X509PrincipalExtractor principalExtractor; + @Autowired + public X509IdentityProvider(X509CertificateValidator certificateValidator, X509PrincipalExtractor principalExtractor) { + this.certificateValidator = certificateValidator; + this.principalExtractor = principalExtractor; + } + /** * Authenticates the specified request by checking certificate validity. * diff --git a/nifi-registry-web-api/src/main/xsd/identity-providers.xsd b/nifi-registry-web-api/src/main/xsd/identity-providers.xsd new file mode 100644 index 000000000..12a85ca3c --- /dev/null +++ b/nifi-registry-web-api/src/main/xsd/identity-providers.xsd @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/NiFiRegistryApiTestApplication.java b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/NiFiRegistryTestApiApplication.java similarity index 79% rename from nifi-registry-web-api/src/test/java/org/apache/nifi/registry/NiFiRegistryApiTestApplication.java rename to nifi-registry-web-api/src/test/java/org/apache/nifi/registry/NiFiRegistryTestApiApplication.java index 01b026912..cfb52c8df 100644 --- a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/NiFiRegistryApiTestApplication.java +++ b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/NiFiRegistryTestApiApplication.java @@ -24,8 +24,6 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; -import java.util.Properties; - @SpringBootApplication @ComponentScan( excludeFilters = { @@ -39,21 +37,15 @@ type = FilterType.REGEX, pattern = "org\\.apache\\.nifi\\.registry\\.NiFiRegistryPropertiesFactory"), // Avoid loading NiFiRegistryPropertiesFactory }) -public class NiFiRegistryApiTestApplication extends SpringBootServletInitializer { +public class NiFiRegistryTestApiApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { - final Properties fixedProps = new Properties(); - fixedProps.setProperty("spring.jpa.hibernate.ddl-auto", "none"); - fixedProps.setProperty("spring.jpa.hibernate.naming.physical-strategy", "org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl"); - - return application - .sources(NiFiRegistryApiTestApplication.class) - .properties(fixedProps); + return application.sources(NiFiRegistryTestApiApplication.class); } public static void main(String[] args) { - SpringApplication.run(NiFiRegistryApiTestApplication.class, args); + SpringApplication.run(NiFiRegistryTestApiApplication.class, args); } } diff --git a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/SecureLdapTestApiApplication.java b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/SecureLdapTestApiApplication.java new file mode 100644 index 000000000..46ab2934e --- /dev/null +++ b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/SecureLdapTestApiApplication.java @@ -0,0 +1,56 @@ +/* + * 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; + +import org.apache.nifi.registry.db.DataSourceFactory; +import org.apache.nifi.registry.security.authorization.AuthorizerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.support.SpringBootServletInitializer; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; + +@SpringBootApplication +@ComponentScan( + basePackages = "org.apache.nifi.registry", + excludeFilters = { + @ComponentScan.Filter( + type = FilterType.ASSIGNABLE_TYPE, + value = SpringBootServletInitializer.class), // Avoid loading NiFiRegistryApiApplication + @ComponentScan.Filter( + type = FilterType.ASSIGNABLE_TYPE, + value = DataSourceFactory.class), // Avoid loading DataSourceFactory + @ComponentScan.Filter( + type = FilterType.ASSIGNABLE_TYPE, + value = AuthorizerFactory.class), // Avoid loading AuthorizerFactory.getAuthorizer(), as we need to add it again with test-specific @DependsOn annotation + @ComponentScan.Filter( + type = FilterType.REGEX, + pattern = "org\\.apache\\.nifi\\.registry\\.NiFiRegistryPropertiesFactory"), // Avoid loading NiFiRegistryPropertiesFactory + }) +public class SecureLdapTestApiApplication extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(SecureLdapTestApiApplication.class); + } + + public static void main(String[] args) { + SpringApplication.run(SecureLdapTestApiApplication.class, args); + } + +} diff --git a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java index d04e79b96..2ca3caaa4 100644 --- a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java +++ b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java @@ -16,7 +16,7 @@ */ package org.apache.nifi.registry.web.api; -import org.apache.nifi.registry.NiFiRegistryApiTestApplication; +import org.apache.nifi.registry.NiFiRegistryTestApiApplication; import org.junit.Test; import org.junit.runner.RunWith; import org.skyscreamer.jsonassert.JSONAssert; @@ -39,7 +39,7 @@ */ @RunWith(SpringRunner.class) @SpringBootTest( - classes = NiFiRegistryApiTestApplication.class, + classes = NiFiRegistryTestApiApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "spring.profiles.include=ITSecureFile") @Import(SecureITClientConfiguration.class) diff --git a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java new file mode 100644 index 000000000..5f5af59c1 --- /dev/null +++ b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java @@ -0,0 +1,230 @@ +/* + * 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.web.api; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.SecureLdapTestApiApplication; +import org.apache.nifi.registry.extension.ExtensionManager; +import org.apache.nifi.registry.model.authorization.Tenant; +import org.apache.nifi.registry.properties.NiFiRegistryProperties; +import org.apache.nifi.registry.security.authorization.Authorizer; +import org.apache.nifi.registry.security.authorization.AuthorizerFactory; +import org.apache.tomcat.util.codec.binary.Base64; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.DependsOn; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Profile; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Form; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Deploy the Web API Application using an embedded Jetty Server for local integration testing, with the follow characteristics: + * + * - A NiFiRegistryProperties has to be explicitly provided to the ApplicationContext using a profile unique to this test suite. + * - A NiFiRegistryClientConfig has been configured to create a client capable of completing two-way TLS + * - The database is embed H2 using volatile (in-memory) persistence + * - Custom SQL is clearing the DB before each test method by default, unless method overrides this behavior + */ +@RunWith(SpringRunner.class) +@SpringBootTest( + classes = SecureLdapTestApiApplication.class, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = "spring.profiles.include=ITSecureLdap") +@Import(SecureITClientConfiguration.class) +@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:db/clearDB.sql") +public class SecureLdapIT extends IntegrationTestBase { + + @TestConfiguration + @Profile("ITSecureLdap") + public static class LdapTestConfiguration { + + static AuthorizerFactory af; + + @Primary + @Bean + @DependsOn({"directoryServer"}) // Can't load LdapUserGroupProvider until the embedded LDAP server, which creates the "directoryServer" bean, is running + public static Authorizer getAuthorizer(@Autowired NiFiRegistryProperties properties, ExtensionManager extensionManager) { + if (af == null) { + af = new AuthorizerFactory(properties, extensionManager); + } + return af.getAuthorizer(); + } + + } + + private String authToken; + + @Before + public void generateAuthToken() { + final Form form = new Form() + .param("username", "nifiadmin") + .param("password", "password"); + final String token = client + .target(createURL("access/token")) + .request() + .post(Entity.form(form), String.class); + authToken = token; + } + + @Test + public void testTokenGeneration() throws Exception { + + // Note: this test intentionally does not use the token generated + // for nifiadmin by the @Before method + + // Given: the client and server have been configured correctly for LDAP authentication + String expectedJwtPayloadJson = "{" + + "\"sub\":\"nobel\"," + + "\"preferred_username\":\"nobel\"," + + "\"iss\":\"LdapIdentityProvider\"," + + "\"aud\":\"LdapIdentityProvider\"" + + "}"; + String expectedAccessStatusJson = "{" + + "\"identity\":\"nobel\"," + + "\"status\":\"ACTIVE\"" + + "}"; + + // When: the /access/token endpoint is queried + final Form form = new Form() + .param("username", "nobel") + .param("password", "password"); + final Response tokenResponse = client + .target(createURL("access/token")) + .request() + .post(Entity.form(form), Response.class); + + // Then: the server returns 200 OK with an access token + assertEquals(201, tokenResponse.getStatus()); + String token = tokenResponse.readEntity(String.class); + assertTrue(StringUtils.isNotEmpty(token)); + String[] jwtParts = token.split("\\."); + assertEquals(3, jwtParts.length); + String jwtPayload = new String(Base64.decodeBase64(jwtParts[1]), "UTF-8"); + JSONAssert.assertEquals(expectedJwtPayloadJson, jwtPayload, false); + + // When: the token is returned in the Authorization header + final Response accessResponse = client + .target(createURL("access")) + .request() + .header("Authorization", "Bearer " + token) + .get(Response.class); + + // Then: the server acknowledges the client has access + assertEquals(200, accessResponse.getStatus()); + String accessStatus = accessResponse.readEntity(String.class); + JSONAssert.assertEquals(expectedAccessStatusJson, accessStatus, false); + + } + + @Test + public void testUsers() throws Exception { + + // Given: the client and server have been configured correctly for LDAP authentication + String expectedJson = "[" + + "{\"identity\":\"nifiadmin\",\"userGroups\":[]}," + + "{\"identity\":\"euler\",\"userGroups\":[{\"identity\":\"mathematicians\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"euclid\",\"userGroups\":[{\"identity\":\"mathematicians\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"boyle\",\"userGroups\":[{\"identity\":\"chemists\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"newton\",\"userGroups\":[{\"identity\":\"scientists\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"riemann\",\"userGroups\":[{\"identity\":\"mathematicians\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"gauss\",\"userGroups\":[{\"identity\":\"mathematicians\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"galileo\",\"userGroups\":[{\"identity\":\"scientists\"},{\"identity\":\"italians\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"nobel\",\"userGroups\":[{\"identity\":\"chemists\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"pasteur\",\"userGroups\":[{\"identity\":\"chemists\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"tesla\",\"userGroups\":[{\"identity\":\"scientists\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"nogroup\",\"userGroups\":[],\"accessPolicies\":[]}," + + "{\"identity\":\"einstein\",\"userGroups\":[{\"identity\":\"scientists\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"curie\",\"userGroups\":[{\"identity\":\"chemists\"}],\"accessPolicies\":[]}]"; + + // When: the /tenants/users endpoint is queried + final String usersJson = client + .target(createURL("tenants/users")) + .request() + .header("Authorization", "Bearer " + authToken) + .get(String.class); + + // Then: the server returns a list of all users (see test-ldap-data.ldif) + JSONAssert.assertEquals(expectedJson, usersJson, false); + } + + @Test + public void testUserGroups() throws Exception { + + // Given: the client and server have been configured correctly for LDAP authentication + String expectedJson = "[" + + "{\"identity\":\"chemists\",\"users\":[{\"identity\":\"pasteur\"},{\"identity\":\"boyle\"},{\"identity\":\"curie\"},{\"identity\":\"nobel\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"mathematicians\",\"users\":[{\"identity\":\"gauss\"},{\"identity\":\"euclid\"},{\"identity\":\"riemann\"},{\"identity\":\"euler\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"scientists\",\"users\":[{\"identity\":\"einstein\"},{\"identity\":\"tesla\"},{\"identity\":\"newton\"},{\"identity\":\"galileo\"}],\"accessPolicies\":[]}," + + "{\"identity\":\"italians\",\"users\":[{\"identity\":\"galileo\"}],\"accessPolicies\":[]}]"; + + // When: the /tenants/users endpoint is queried + final String groupsJson = client + .target(createURL("tenants/user-groups")) + .request() + .header("Authorization", "Bearer " + authToken) + .get(String.class); + + // Then: the server returns a list of all users (see test-ldap-data.ldif) + JSONAssert.assertEquals(expectedJson, groupsJson, false); + } + + + public void testCreateTenantFails() throws Exception { + + // Given: the server has been configured with the LdapUserGroupProvider, which is non-configurable, + // and: the client wants to create a tenant + Tenant tenant = new Tenant(); + tenant.setIdentity("new_tenant"); + + // When: the POST /tenants/users endpoint is accessed + final Response createUserResponse = client + .target(createURL("tenants/users")) + .request() + .header("Authorization", "Bearer " + authToken) + .post(Entity.entity(tenant, MediaType.APPLICATION_JSON_TYPE), Response.class); + + // Then: an error is returned + assertEquals(405, createUserResponse.getStatus()); + + // When: the POST /tenants/users endpoint is accessed + final Response createUserGroupResponse = client + .target(createURL("tenants/user-groups")) + .request() + .header("Authorization", "Bearer " + authToken) + .post(Entity.entity(tenant, MediaType.APPLICATION_JSON_TYPE), Response.class); + + // Then: an error is returned because the UserGroupProvider is non-configurable + assertEquals(405, createUserGroupResponse.getStatus()); + } + +} diff --git a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredITBase.java b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredITBase.java index e546511fc..a0c981b22 100644 --- a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredITBase.java +++ b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredITBase.java @@ -16,7 +16,7 @@ */ package org.apache.nifi.registry.web.api; -import org.apache.nifi.registry.NiFiRegistryApiTestApplication; +import org.apache.nifi.registry.NiFiRegistryTestApiApplication; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.jdbc.Sql; @@ -31,7 +31,7 @@ */ @RunWith(SpringRunner.class) @SpringBootTest( - classes = NiFiRegistryApiTestApplication.class, + classes = NiFiRegistryTestApiApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "spring.profiles.include=ITUnsecured") @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:db/clearDB.sql") diff --git a/nifi-registry-web-api/src/test/resources/application-ITSecureLdap.properties b/nifi-registry-web-api/src/test/resources/application-ITSecureLdap.properties new file mode 100644 index 000000000..ffcc43edc --- /dev/null +++ b/nifi-registry-web-api/src/test/resources/application-ITSecureLdap.properties @@ -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. +# + + +# Properties for Spring Boot integration tests +# Documentation for common Spring Boot application properties can be found at: +# https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html + + +# Custom (non-standard to Spring Boot) properties +nifi.registry.properties.file: src/test/resources/conf/secure-ldap/nifi-registry.properties +nifi.registry.client.properties.file: src/test/resources/conf/secure-ldap/nifi-registry-client.properties + + +# Embedded Server SSL Context Config +#server.ssl.client-auth: need # LDAP-configured server does not require two-way TLS +server.ssl.key-store: ./target/test-classes/keys/localhost-ks.jks +server.ssl.key-store-password: localhostKeystorePassword +server.ssl.key-password: localhostKeystorePassword +server.ssl.protocol: TLS +server.ssl.trust-store: ./target/test-classes/keys/localhost-ts.jks +server.ssl.trust-store-password: localhostTruststorePassword + +# Embedded LDAP Config +spring.ldap.embedded.base-dn: dc=example,dc=com +spring.ldap.embedded.credential.username: cn=read-only-admin,dc=example,dc=com +spring.ldap.embedded.credential.password: password +spring.ldap.embedded.ldif: classpath:conf/secure-ldap/test-ldap-data.ldif +spring.ldap.embedded.port: 8389 +spring.ldap.embedded.validation.enabled: false + +# Additional Logging Config +logging.level.org.springframework.security.ldap: DEBUG +logging.level.org.springframework.ldap: DEBUG \ No newline at end of file diff --git a/nifi-registry-web-api/src/test/resources/conf/secure-file/authorizers.xml b/nifi-registry-web-api/src/test/resources/conf/secure-file/authorizers.xml index 70d75b106..1f9279339 100644 --- a/nifi-registry-web-api/src/test/resources/conf/secure-file/authorizers.xml +++ b/nifi-registry-web-api/src/test/resources/conf/secure-file/authorizers.xml @@ -44,7 +44,7 @@ --> file-user-group-provider - org.apache.nifi.registry.authorization.file.FileUserGroupProvider + org.apache.nifi.registry.security.authorization.file.FileUserGroupProvider ./target/test-classes/conf/secure-file/users.xml CN=user1, OU=nifi @@ -118,7 +118,7 @@ --> file-access-policy-provider - org.apache.nifi.registry.authorization.file.FileAccessPolicyProvider + org.apache.nifi.registry.security.authorization.file.FileAccessPolicyProvider file-user-group-provider ./target/test-classes/conf/secure-file/authorizations.xml CN=user1, OU=nifi @@ -136,7 +136,7 @@ --> managed-authorizer - org.apache.nifi.registry.authorization.StandardManagedAuthorizer + org.apache.nifi.registry.security.authorization.StandardManagedAuthorizer file-access-policy-provider diff --git a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml new file mode 100644 index 000000000..f7f71337f --- /dev/null +++ b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml @@ -0,0 +1,243 @@ + + + + + + + + + + + ldap-user-group-provider + org.apache.nifi.registry.security.ldap.tenants.LdapUserGroupProvider + SIMPLE + + cn=read-only-admin,dc=example,dc=com + password + + + + FOLLOW + 10 secs + 10 secs + + ldap://localhost:8389 + + 30 mins + + dc=example,dc=com + person + ONE_LEVEL + (uid=*) + uid + + + dc=example,dc=com + groupOfUniqueNames + ONE_LEVEL + (ou=*) + ou + uniqueMember + + + + + + + + + + + file-access-policy-provider + org.apache.nifi.registry.security.authorization.file.FileAccessPolicyProvider + ldap-user-group-provider + ./target/test-classes/conf/secure-ldap/authorizations.xml + nifiadmin + + + + + managed-authorizer + org.apache.nifi.registry.security.authorization.StandardManagedAuthorizer + file-access-policy-provider + + + \ No newline at end of file diff --git a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/identity-providers.xml b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/identity-providers.xml new file mode 100644 index 000000000..91efbdbe6 --- /dev/null +++ b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/identity-providers.xml @@ -0,0 +1,90 @@ + + + + + + + ldap-provider + org.apache.nifi.registry.security.ldap.LdapIdentityProvider + SIMPLE + + cn=read-only-admin,dc=example,dc=com + password + + FOLLOW + 10 secs + 10 secs + + ldap://localhost:8389 + + dc=example,dc=com + (uid={0}) + + + USE_USERNAME + 12 hours + + + \ No newline at end of file diff --git a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/nifi-registry-client.properties b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/nifi-registry-client.properties new file mode 100644 index 000000000..929e1a7c4 --- /dev/null +++ b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/nifi-registry-client.properties @@ -0,0 +1,25 @@ +# +# 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. +# + +# client security properties # +#nifi.registry.security.keystore=./target/test-classes/keys/client-ks.jks +#nifi.registry.security.keystoreType=JKS +#nifi.registry.security.keystorePasswd=clientKeystorePassword +#nifi.registry.security.keyPasswd=u1Pass +nifi.registry.security.truststore=./target/test-classes/keys/localhost-ts.jks +nifi.registry.security.truststoreType=JKS +nifi.registry.security.truststorePasswd=localhostTruststorePassword diff --git a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/nifi-registry.properties b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/nifi-registry.properties new file mode 100644 index 000000000..1ff02634e --- /dev/null +++ b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/nifi-registry.properties @@ -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. +# + +# web properties # +nifi.registry.web.https.host=localhost +nifi.registry.web.https.port=0 + +# security properties # +# +# ** Server KeyStore and TrustStore configuration set in Spring profile properties for embedded Jetty ** +# +nifi.registry.security.authorizers.configuration.file=./target/test-classes/conf/secure-ldap/authorizers.xml +nifi.registry.security.authorizer=managed-authorizer +nifi.registry.security.identity.provider.configuration.file=./target/test-classes/conf/secure-ldap/identity-providers.xml +nifi.registry.security.identity.provider=ldap-provider + +# providers properties # +nifi.registry.providers.configuration.file=./target/test-classes/conf/providers.xml + +# database properties +nifi.registry.db.url.append=;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE \ No newline at end of file diff --git a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/test-ldap-data.ldif b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/test-ldap-data.ldif new file mode 100644 index 000000000..db45689c0 --- /dev/null +++ b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/test-ldap-data.ldif @@ -0,0 +1,261 @@ +# +# 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. +# + +# extended LDIF +# +# LDAPv3 +# base with scope subtree +# filter: objectclass=* +# requesting: ALL +# +# Adapted from Forum Systems' LDAP Test Server +# + +# example.com +dn: dc=example,dc=com +objectClass: top +objectClass: dcObject +objectClass: organization +o: example.com +dc: example + +# read-only-admin, example.com +dn: cn=read-only-admin,dc=example,dc=com +sn: Read Only Admin +cn: read-only-admin +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top + +# nifiadmin, example.com +dn: uid=nifiadmin,dc=example,dc=com +sn: nifiadmin +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +uid: nifiadmin +cn: NiFi Admin +userPassword: password + +# newton, example.com +dn: uid=newton,dc=example,dc=com +sn: Newton +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +uid: newton +cn: Isaac Newton +userPassword: password + +# einstein, example.com +dn: uid=einstein,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: Albert Einstein +sn: Einstein +uid: einstein +userPassword: password + +# tesla, example.com +dn: uid=tesla,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +objectClass: posixAccount +cn: Nikola Tesla +sn: Tesla +uid: tesla +uidNumber: 88888 +gidNumber: 99999 +homeDirectory: home +userPassword: password + +# galileo, example.com +dn: uid=galileo,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: Galileo Galilei +sn: Galilei +uid: galileo +mail: galileo@example.com +userPassword: password + +# euler, example.com +dn: uid=euler,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +uid: euler +sn: Euler +cn: Leonhard Euler +userPassword: password + +# gauss, example.com +dn: uid=gauss,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: Carl Friedrich Gauss +sn: Gauss +uid: gauss +userPassword: password + +# riemann, example.com +dn: uid=riemann,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: Bernhard Riemann +sn: Riemann +uid: riemann +userPassword: password + +# euclid, example.com +dn: uid=euclid,dc=example,dc=com +uid: euclid +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: Euclid +sn: Euclid +userPassword: password + +# curie, example.com +dn: uid=curie,dc=example,dc=com +uid: curie +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: Marie Curie +sn: Curie +userPassword: password + +# nobel, example.com +dn: uid=nobel,dc=example,dc=com +uid: nobel +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +sn: Nobel +cn: Alfred Nobel +userPassword: password + +# boyle, example.com +dn: uid=boyle,dc=example,dc=com +uid: boyle +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: Robert Boyle +sn: Boyle +telephoneNumber: 999-867-5309 +userPassword: password + +# pasteur, example.com +dn: uid=pasteur,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +sn: Pasteur +cn: Louis Pasteur +uid: pasteur +telephoneNumber: 602-214-4978 +userPassword: password + +# nogroup, example.com +dn: uid=nogroup,dc=example,dc=com +uid: nogroup +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +cn: No Group +sn: Group + +# test, example.com +dn: uid=test,dc=example,dc=com +objectClass: posixAccount +objectClass: top +objectClass: inetOrgPerson +gidNumber: 0 +givenName: Test +sn: Test +displayName: Test +uid: test +initials: TS +homeDirectory: home +cn: Test +uidNumber: 24601 +o: Company + +# mathematicians, example.com +dn: ou=mathematicians,dc=example,dc=com +uniqueMember: uid=euclid,dc=example,dc=com +uniqueMember: uid=riemann,dc=example,dc=com +uniqueMember: uid=euler,dc=example,dc=com +uniqueMember: uid=gauss,dc=example,dc=com +uniqueMember: uid=test,dc=example,dc=com +ou: mathematicians +cn: Mathematicians +objectClass: groupOfUniqueNames +objectClass: top + +# scientists, example.com +dn: ou=scientists,dc=example,dc=com +uniqueMember: uid=einstein,dc=example,dc=com +uniqueMember: uid=galileo,dc=example,dc=com +uniqueMember: uid=tesla,dc=example,dc=com +uniqueMember: uid=newton,dc=example,dc=com +ou: scientists +cn: Scientists +objectClass: groupOfUniqueNames +objectClass: top + +# italians, example.com +dn: ou=italians,dc=example,dc=com +uniqueMember: uid=galileo,dc=example,dc=com +ou: italians +cn: Italians +objectClass: groupOfUniqueNames +objectClass: top + +# chemists, example.com +dn: ou=chemists,dc=example,dc=com +ou: chemists +objectClass: groupOfUniqueNames +objectClass: top +uniqueMember: uid=curie,dc=example,dc=com +uniqueMember: uid=boyle,dc=example,dc=com +uniqueMember: uid=nobel,dc=example,dc=com +uniqueMember: uid=pasteur,dc=example,dc=com +cn: Chemists diff --git a/pom.xml b/pom.xml index 8ee241024..f216bcce8 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,6 @@ nifi-registry-resources nifi-registry-runtime nifi-registry-security-api - nifi-registry-security-api-impl nifi-registry-security-utils nifi-registry-framework nifi-registry-provider-api @@ -111,6 +110,7 @@ 2.0.1 2.25.1 1.5.7.RELEASE + 4.2.3.RELEASE 4.2.0 From 9b238bc29ffd09a5f57f272604f750b8ac053183 Mon Sep 17 00:00:00 2001 From: Kevin Doran Date: Tue, 7 Nov 2017 10:56:35 -0500 Subject: [PATCH 2/3] NIFIREG-33 Fix issues uncovered in peer review --- nifi-registry-assembly/pom.xml | 5 +- .../nifi/registry/db/entity/KeyEntity.java | 3 +- .../authorization/AuthorizerFactory.java | 19 +++- .../properties/NiFiRegistryProperties.java | 4 +- .../src/main/resources/conf/authorizers.xml | 2 +- .../resources/conf/identity-providers.xml | 90 +++++++++++++++++++ .../resources/conf/nifi-registry.properties | 2 + .../registry/NiFiRegistryApiApplication.java | 8 +- .../web/NiFiRegistryResourceConfig.java | 13 +++ .../web/api/AccessPolicyResource.java | 18 ++-- .../nifi/registry/web/api/AccessResource.java | 14 ++- .../registry/web/api/HttpStatusMessages.java | 1 - .../nifi/registry/web/api/TenantResource.java | 5 +- .../web/mapper/NotAllowedExceptionMapper.java | 47 ---------- .../LoginIdentityProviderFactory.java | 10 +-- .../src/main/xsd/identity-providers.xsd | 2 +- .../NiFiRegistryTestApiApplication.java | 11 --- .../SecureLdapTestApiApplication.java | 11 --- .../src/test/resources/application.properties | 8 +- .../conf/secure-file/nifi-registry.properties | 3 - .../conf/secure-ldap/authorizers.xml | 2 +- .../conf/secure-ldap/identity-providers.xml | 8 +- .../conf/secure-ldap/nifi-registry.properties | 7 +- 23 files changed, 167 insertions(+), 126 deletions(-) create mode 100644 nifi-registry-resources/src/main/resources/conf/identity-providers.xml delete mode 100644 nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotAllowedExceptionMapper.java diff --git a/nifi-registry-assembly/pom.xml b/nifi-registry-assembly/pom.xml index 69a5ec071..41f66b8b9 100644 --- a/nifi-registry-assembly/pom.xml +++ b/nifi-registry-assembly/pom.xml @@ -151,8 +151,11 @@ - ./conf/authorizers.xml + + ./conf/authorizers.xml managed-authorizer + ./conf/identity-providers.xml + ./conf/providers.xml diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/entity/KeyEntity.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/entity/KeyEntity.java index e3f7e3a71..7ec7d22d0 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/entity/KeyEntity.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/db/entity/KeyEntity.java @@ -28,9 +28,10 @@ public class KeyEntity { @Id private String id; - @Column(unique=true) + @Column(name = "TENANT_IDENTITY", unique = true, nullable = false) private String tenantIdentity; + @Column(name = "KEY_VALUE", nullable = false) private String keyValue; public String getId() { diff --git a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java index 022147840..1ca636833 100644 --- a/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java +++ b/nifi-registry-framework/src/main/java/org/apache/nifi/registry/security/authorization/AuthorizerFactory.java @@ -30,6 +30,7 @@ import org.apache.nifi.registry.security.util.XmlUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -58,7 +59,7 @@ * NiFi's AuthorizerFactory and AuthorizerFactoryBean. */ @Configuration("authorizerFactory") -public class AuthorizerFactory implements UserGroupProviderLookup, AccessPolicyProviderLookup, AuthorizerLookup { +public class AuthorizerFactory implements UserGroupProviderLookup, AccessPolicyProviderLookup, AuthorizerLookup, DisposableBean { private static final Logger logger = LoggerFactory.getLogger(StandardProviderFactory.class); @@ -121,6 +122,7 @@ public Authorizer getAuthorizer(String identifier) { return authorizers.get(identifier); } + /***** AuthorizerFactory / DisposableBean *****/ @Bean public Authorizer getAuthorizer() throws AuthorizerFactoryException { @@ -189,6 +191,21 @@ public Authorizer getAuthorizer() throws AuthorizerFactoryException { return authorizer; } + @Override + public void destroy() throws Exception { + if (authorizers != null) { + authorizers.entrySet().stream().forEach(e -> e.getValue().preDestruction()); + } + + if (accessPolicyProviders != null) { + accessPolicyProviders.entrySet().stream().forEach(e -> e.getValue().preDestruction()); + } + + if (userGroupProviders != null) { + userGroupProviders.entrySet().stream().forEach(e -> e.getValue().preDestruction()); + } + } + private Authorizers loadAuthorizersConfiguration() throws Exception { final File authorizersConfigurationFile = properties.getAuthorizersConfigurationFile(); diff --git a/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java b/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java index cfbc60136..b0090c041 100644 --- a/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java +++ b/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java @@ -49,7 +49,7 @@ public class NiFiRegistryProperties extends Properties { public static final String SECURITY_NEED_CLIENT_AUTH = "nifi.registry.security.needClientAuth"; public static final String SECURITY_AUTHORIZERS_CONFIGURATION_FILE = "nifi.registry.security.authorizers.configuration.file"; public static final String SECURITY_AUTHORIZER = "nifi.registry.security.authorizer"; - public static final String SECURITY_IDENTITY_PROVIDER_CONFIGURATION_FILE = "nifi.registry.security.identity.provider.configuration.file"; + public static final String SECURITY_IDENTITY_PROVIDERS_CONFIGURATION_FILE = "nifi.registry.security.identity.providers.configuration.file"; public static final String SECURITY_IDENTITY_PROVIDER = "nifi.registry.security.identity.provider"; public static final String SECURITY_IDENTITY_MAPPING_PATTERN_PREFIX = "nifi.registry.security.identity.mapping.pattern."; public static final String SECURITY_IDENTITY_MAPPING_VALUE_PREFIX = "nifi.registry.security.identity.mapping.value."; @@ -182,7 +182,7 @@ public File getAuthorizersConfigurationFile() { } public File getIdentityProviderConfigurationFile() { - final String value = getProperty(SECURITY_IDENTITY_PROVIDER_CONFIGURATION_FILE); + final String value = getProperty(SECURITY_IDENTITY_PROVIDERS_CONFIGURATION_FILE); if (StringUtils.isBlank(value)) { return new File(DEFAULT_SECURITY_IDENTITY_PROVIDER_CONFIGURATION_FILE); } else { diff --git a/nifi-registry-resources/src/main/resources/conf/authorizers.xml b/nifi-registry-resources/src/main/resources/conf/authorizers.xml index 663a92691..c3cad80bb 100644 --- a/nifi-registry-resources/src/main/resources/conf/authorizers.xml +++ b/nifi-registry-resources/src/main/resources/conf/authorizers.xml @@ -107,7 +107,7 @@ + + + + + + \ No newline at end of file diff --git a/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties b/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties index cd5fd8bd2..7f0c7ae3f 100644 --- a/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties +++ b/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties @@ -33,6 +33,8 @@ nifi.registry.security.truststorePasswd=${nifi.registry.security.truststorePassw nifi.registry.security.needClientAuth=${nifi.registry.security.needClientAuth} nifi.registry.security.authorizers.configuration.file=${nifi.registry.security.authorizers.configuration.file} nifi.registry.security.authorizer=${nifi.registry.security.authorizer} +nifi.registry.security.identity.providers.configuration.file=${nifi.registry.security.identity.providers.configuration.file} +nifi.registry.security.identity.provider=${nifi.registry.security.identity.provider} # providers properties # nifi.registry.providers.configuration.file=${nifi.registry.providers.configuration.file} diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java index e8e36a73f..19adaed91 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java @@ -38,13 +38,13 @@ public class NiFiRegistryApiApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { - final Properties fixedProps = new Properties(); - fixedProps.setProperty("spring.jpa.hibernate.ddl-auto", "none"); - fixedProps.setProperty("spring.jpa.hibernate.naming.physical-strategy", "org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl"); + final Properties defaultProperties = new Properties(); + defaultProperties.setProperty("spring.jpa.hibernate.ddl-auto", "none"); + defaultProperties.setProperty("spring.jpa.hibernate.naming.physical-strategy", "org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl"); return application .sources(NiFiRegistryApiApplication.class) - .properties(fixedProps); + .properties(defaultProperties); } public static void main(String[] args) { diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java index d577e1e27..503c9483d 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java @@ -29,8 +29,13 @@ import org.glassfish.jersey.server.filter.HttpMethodOverrideFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.HiddenHttpMethodFilter; +import javax.servlet.Filter; import javax.servlet.ServletContext; import javax.ws.rs.core.Context; @@ -63,4 +68,12 @@ public NiFiRegistryResourceConfig(@Context ServletContext servletContext) { property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true); } + + // Disable default SpringMVC filter beans that are not compatible with Jersey + @Bean + public FilterRegistrationBean registration(@Autowired HiddenHttpMethodFilter filter) { + FilterRegistrationBean registration = new FilterRegistrationBean((Filter) filter); + registration.setEnabled(false); + return registration; + } } diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java index 7d28fca2e..b9cfbb9a8 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java @@ -21,7 +21,6 @@ import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; -import javax.ws.rs.NotAllowedException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -92,8 +91,7 @@ public AccessPolicyResource( @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), - @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405), - @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409 + " The NiFi Registry might not be configured to use a ConfigurableAccessPolicyProvider.") }) public Response createAccessPolicy( @Context final HttpServletRequest httpServletRequest, @ApiParam(value = "The access policy configuration details.", required = true) @@ -137,7 +135,6 @@ public Response createAccessPolicy( @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_409), - @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_409) }) public Response getAccessPolicies() { @@ -167,7 +164,7 @@ public Response getAccessPolicies() { @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), - @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405) }) + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) public Response getAccessPolicy( @ApiParam(value = "The access policy id.", required = true) @PathParam("id") final String identifier) { @@ -206,7 +203,6 @@ public Response getAccessPolicy( @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), - @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405), @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) public Response getAccessPolicyForResource( @ApiParam(value = "The request action.", allowableValues = "read, write, delete", required = true) @@ -250,8 +246,7 @@ public Response getAccessPolicyForResource( @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), - @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405), - @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409 + " The NiFi Registry might not be configured to use a ConfigurableAccessPolicyProvider.") }) public Response updateAccessPolicy( @Context final HttpServletRequest httpServletRequest, @@ -299,7 +294,7 @@ public Response updateAccessPolicy( @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), - @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405) }) + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409 + " The NiFi Registry might not be configured to use a ConfigurableAccessPolicyProvider.") }) public Response removeAccessPolicy( @Context final HttpServletRequest httpServletRequest, @ApiParam(value = "The access policy id.", required = true) @@ -315,13 +310,14 @@ public Response removeAccessPolicy( private void verifyAuthorizerIsManaged() { if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) { - throw new NotAllowedException(AuthorizationService.MSG_NON_MANAGED_AUTHORIZER); + throw new IllegalStateException(AuthorizationService.MSG_NON_MANAGED_AUTHORIZER); } } private void verifyAuthorizerSupportsConfigurablePolicies() { if (!AuthorizerCapabilityDetection.isConfigurableAccessPolicyProvider(authorizer)) { - throw new NotAllowedException(AuthorizationService.MSG_NON_CONFIGURABLE_POLICIES); + verifyAuthorizerIsManaged(); + throw new IllegalStateException(AuthorizationService.MSG_NON_CONFIGURABLE_POLICIES); } } diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java index 43d187f01..ad923c473 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java @@ -55,7 +55,6 @@ import javax.ws.rs.Consumes; import javax.ws.rs.FormParam; import javax.ws.rs.GET; -import javax.ws.rs.NotAllowedException; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; @@ -116,12 +115,11 @@ public AccessResource(X509CertificateExtractor certificateExtractor, @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), - @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405), - @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409 + " The NiFi Registry might be running unsecured.") }) public Response getAccessStatus(@Context HttpServletRequest httpServletRequest) { // only consider user specific access over https if (!httpServletRequest.isSecure()) { - throw new NotAllowedException("User authentication/authorization is only supported when running over HTTPS."); + throw new IllegalStateException("User authentication/authorization is only supported when running over HTTPS."); } final AccessStatus accessStatus = new AccessStatus(); @@ -208,8 +206,8 @@ public Response getAccessStatus(@Context HttpServletRequest httpServletRequest) @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), - @ApiResponse(code = 405, message = HttpStatusMessages.MESSAGE_405 + ". The NiFi Registry may not be configured to support username/password login."), - @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_500) }) + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409 + " The NiFi Registry may not be configured to support username/password login."), + @ApiResponse(code = 500, message = HttpStatusMessages.MESSAGE_500) }) public Response createAccessToken( @Context HttpServletRequest httpServletRequest, @FormParam("username") String username, @@ -217,12 +215,12 @@ public Response createAccessToken( // only support access tokens when communicating over HTTPS if (!httpServletRequest.isSecure()) { - throw new NotAllowedException("Access tokens are only issued over HTTPS"); + throw new IllegalStateException("Access tokens are only issued over HTTPS"); } // if not configuration for login, don't consider credentials if (loginIdentityProvider == null) { - throw new NotAllowedException("Username/Password login not supported by this NiFi"); + throw new IllegalStateException("Username/Password login not supported by this NiFi"); } final LoginAuthenticationToken loginAuthenticationToken; diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/HttpStatusMessages.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/HttpStatusMessages.java index 36e310ede..a3ba939d2 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/HttpStatusMessages.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/HttpStatusMessages.java @@ -23,7 +23,6 @@ class HttpStatusMessages { static final String MESSAGE_401 = "Client could not be authenticated."; static final String MESSAGE_403 = "Client is not authorized to make this request."; static final String MESSAGE_404 = "The specified resource could not be found."; - static final String MESSAGE_405 = "The HTTP method is not allowed for the specified resource for this NiFi Registry based to its configuration."; static final String MESSAGE_409 = "NiFi Registry was unable to complete the request because it assumes a server state that is not valid."; /* 5xx messages */ diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java index 9d5e89695..3d088d709 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java @@ -39,7 +39,6 @@ import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; -import javax.ws.rs.NotAllowedException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -459,13 +458,13 @@ public Response removeUserGroup( private void verifyAuthorizerIsManaged() { if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) { - throw new NotAllowedException(AuthorizationService.MSG_NON_MANAGED_AUTHORIZER); + throw new IllegalStateException(AuthorizationService.MSG_NON_MANAGED_AUTHORIZER); } } private void verifyAuthorizerSupportsConfigurableUserGroups() { if (!AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer)) { - throw new NotAllowedException(AuthorizationService.MSG_NON_CONFIGURABLE_USERS); + throw new IllegalStateException(AuthorizationService.MSG_NON_CONFIGURABLE_USERS); } } diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotAllowedExceptionMapper.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotAllowedExceptionMapper.java deleted file mode 100644 index 1867196db..000000000 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotAllowedExceptionMapper.java +++ /dev/null @@ -1,47 +0,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. - */ -package org.apache.nifi.registry.web.mapper; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import javax.ws.rs.NotAllowedException; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; - -@Component -@Provider -public class NotAllowedExceptionMapper implements ExceptionMapper { - - private static final Logger logger = LoggerFactory.getLogger(NotAllowedExceptionMapper.class); - - @Override - public Response toResponse(NotAllowedException exception) { - // log the error - logger.info(String.format("%s. Returning %s response.", exception, Response.Status.METHOD_NOT_ALLOWED)); - - if (logger.isDebugEnabled()) { - logger.debug(StringUtils.EMPTY, exception); - } - - return Response.status(Response.Status.METHOD_NOT_ALLOWED).entity(exception.getMessage()).type("text/plain").build(); - } - -} diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/LoginIdentityProviderFactory.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/LoginIdentityProviderFactory.java index a3e77b050..607da6a2a 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/LoginIdentityProviderFactory.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/LoginIdentityProviderFactory.java @@ -23,7 +23,7 @@ import org.apache.nifi.registry.security.authentication.annotation.LoginIdentityProviderContext; import org.apache.nifi.registry.extension.ExtensionManager; import org.apache.nifi.registry.properties.NiFiRegistryProperties; -import org.apache.nifi.registry.security.authentication.generated.LoginIdentityProviders; +import org.apache.nifi.registry.security.authentication.generated.IdentityProviders; import org.apache.nifi.registry.security.authentication.generated.Property; import org.apache.nifi.registry.security.authentication.generated.Provider; import org.apache.nifi.registry.security.util.XmlUtils; @@ -99,7 +99,7 @@ public LoginIdentityProvider getLoginIdentityProvider() throws Exception { // ensure the login identity provider class name was specified if (StringUtils.isNotBlank(loginIdentityProviderIdentifier)) { - final LoginIdentityProviders loginIdentityProviderConfiguration = loadLoginIdentityProvidersConfiguration(); + final IdentityProviders loginIdentityProviderConfiguration = loadLoginIdentityProvidersConfiguration(); // create each login identity provider for (final Provider provider : loginIdentityProviderConfiguration.getProvider()) { @@ -125,7 +125,7 @@ public LoginIdentityProvider getLoginIdentityProvider() throws Exception { return loginIdentityProvider; } - private LoginIdentityProviders loadLoginIdentityProvidersConfiguration() throws Exception { + private IdentityProviders loadLoginIdentityProvidersConfiguration() throws Exception { final File loginIdentityProvidersConfigurationFile = properties.getIdentityProviderConfigurationFile(); // load the users from the specified file @@ -133,13 +133,13 @@ private LoginIdentityProviders loadLoginIdentityProvidersConfiguration() throws try { // find the schema final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - final Schema schema = schemaFactory.newSchema(LoginIdentityProviders.class.getResource(LOGIN_IDENTITY_PROVIDERS_XSD)); + final Schema schema = schemaFactory.newSchema(IdentityProviders.class.getResource(LOGIN_IDENTITY_PROVIDERS_XSD)); // attempt to unmarshal XMLStreamReader xsr = XmlUtils.createSafeReader(new StreamSource(loginIdentityProvidersConfigurationFile)); final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller(); unmarshaller.setSchema(schema); - final JAXBElement element = unmarshaller.unmarshal(xsr, LoginIdentityProviders.class); + final JAXBElement element = unmarshaller.unmarshal(xsr, IdentityProviders.class); return element.getValue(); } catch (SAXException | JAXBException e) { throw new Exception("Unable to load the login identity provider configuration file at: " + loginIdentityProvidersConfigurationFile.getAbsolutePath()); diff --git a/nifi-registry-web-api/src/main/xsd/identity-providers.xsd b/nifi-registry-web-api/src/main/xsd/identity-providers.xsd index 12a85ca3c..bcca014fc 100644 --- a/nifi-registry-web-api/src/main/xsd/identity-providers.xsd +++ b/nifi-registry-web-api/src/main/xsd/identity-providers.xsd @@ -40,7 +40,7 @@ - + diff --git a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/NiFiRegistryTestApiApplication.java b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/NiFiRegistryTestApiApplication.java index cfb52c8df..e55fb210d 100644 --- a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/NiFiRegistryTestApiApplication.java +++ b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/NiFiRegistryTestApiApplication.java @@ -17,9 +17,7 @@ package org.apache.nifi.registry; import org.apache.nifi.registry.db.DataSourceFactory; -import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.support.SpringBootServletInitializer; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; @@ -39,13 +37,4 @@ }) public class NiFiRegistryTestApiApplication extends SpringBootServletInitializer { - @Override - protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { - return application.sources(NiFiRegistryTestApiApplication.class); - } - - public static void main(String[] args) { - SpringApplication.run(NiFiRegistryTestApiApplication.class, args); - } - } diff --git a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/SecureLdapTestApiApplication.java b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/SecureLdapTestApiApplication.java index 46ab2934e..fea83493e 100644 --- a/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/SecureLdapTestApiApplication.java +++ b/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/SecureLdapTestApiApplication.java @@ -18,9 +18,7 @@ import org.apache.nifi.registry.db.DataSourceFactory; import org.apache.nifi.registry.security.authorization.AuthorizerFactory; -import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.support.SpringBootServletInitializer; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; @@ -44,13 +42,4 @@ }) public class SecureLdapTestApiApplication extends SpringBootServletInitializer { - @Override - protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { - return application.sources(SecureLdapTestApiApplication.class); - } - - public static void main(String[] args) { - SpringApplication.run(SecureLdapTestApiApplication.class, args); - } - } diff --git a/nifi-registry-web-api/src/test/resources/application.properties b/nifi-registry-web-api/src/test/resources/application.properties index 9cc9f51f4..f63f89d88 100644 --- a/nifi-registry-web-api/src/test/resources/application.properties +++ b/nifi-registry-web-api/src/test/resources/application.properties @@ -24,8 +24,8 @@ #logging.level.org.springframework.context.annotation: DEBUG #logging.level.org.springframework.web: DEBUG -#spring.jpa.show-sql=true -spring.jpa.hibernate.ddl-auto=validate -spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect +# These properties should match the defaultProperties hardcoded in NiFiRegistryApiApplication.configure() +spring.jpa.hibernate.ddl-auto = none +spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl -spring.datasource.url = jdbc:h2:mem:test \ No newline at end of file +spring.jpa.show-sql = true \ No newline at end of file diff --git a/nifi-registry-web-api/src/test/resources/conf/secure-file/nifi-registry.properties b/nifi-registry-web-api/src/test/resources/conf/secure-file/nifi-registry.properties index 65271c653..408f44d0b 100644 --- a/nifi-registry-web-api/src/test/resources/conf/secure-file/nifi-registry.properties +++ b/nifi-registry-web-api/src/test/resources/conf/secure-file/nifi-registry.properties @@ -28,6 +28,3 @@ nifi.registry.security.authorizer=managed-authorizer # providers properties # nifi.registry.providers.configuration.file=./target/test-classes/conf/providers.xml - -# database properties -nifi.registry.db.url.append=;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE \ No newline at end of file diff --git a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml index f7f71337f..ca472cc4c 100644 --- a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml +++ b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/authorizers.xml @@ -141,7 +141,7 @@ ONE_LEVEL (uid=*) uid - + dc=example,dc=com groupOfUniqueNames diff --git a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/identity-providers.xml b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/identity-providers.xml index 91efbdbe6..280a97593 100644 --- a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/identity-providers.xml +++ b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/identity-providers.xml @@ -20,7 +20,7 @@ to use a specific provider it must be configured here and it's identifier must be specified in the nifi.properties file. --> - + - ldap-provider + ldap-identity-provider org.apache.nifi.registry.security.ldap.LdapIdentityProvider SIMPLE @@ -78,13 +78,11 @@ 10 secs ldap://localhost:8389 - dc=example,dc=com (uid={0}) - USE_USERNAME 12 hours - \ No newline at end of file + \ No newline at end of file diff --git a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/nifi-registry.properties b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/nifi-registry.properties index 1ff02634e..40b291684 100644 --- a/nifi-registry-web-api/src/test/resources/conf/secure-ldap/nifi-registry.properties +++ b/nifi-registry-web-api/src/test/resources/conf/secure-ldap/nifi-registry.properties @@ -25,11 +25,8 @@ nifi.registry.web.https.port=0 # nifi.registry.security.authorizers.configuration.file=./target/test-classes/conf/secure-ldap/authorizers.xml nifi.registry.security.authorizer=managed-authorizer -nifi.registry.security.identity.provider.configuration.file=./target/test-classes/conf/secure-ldap/identity-providers.xml -nifi.registry.security.identity.provider=ldap-provider +nifi.registry.security.identity.providers.configuration.file=./target/test-classes/conf/secure-ldap/identity-providers.xml +nifi.registry.security.identity.provider=ldap-identity-provider # providers properties # nifi.registry.providers.configuration.file=./target/test-classes/conf/providers.xml - -# database properties -nifi.registry.db.url.append=;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE \ No newline at end of file From c3f1ac9a90bdd60c28ef9aab4060e70d86a36ac8 Mon Sep 17 00:00:00 2001 From: Kevin Doran Date: Tue, 7 Nov 2017 12:51:28 -0500 Subject: [PATCH 3/3] NIFIREG-33 Make LoginIdentityProviderFactory a DisposableBean --- .../authentication/LoginIdentityProviderFactory.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/LoginIdentityProviderFactory.java b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/LoginIdentityProviderFactory.java index 607da6a2a..434d88131 100644 --- a/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/LoginIdentityProviderFactory.java +++ b/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/LoginIdentityProviderFactory.java @@ -29,6 +29,7 @@ import org.apache.nifi.registry.security.util.XmlUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -52,7 +53,7 @@ import java.util.Map; @Configuration -public class LoginIdentityProviderFactory implements LoginIdentityProviderLookup { +public class LoginIdentityProviderFactory implements LoginIdentityProviderLookup, DisposableBean { private static final Logger logger = LoggerFactory.getLogger(LoginIdentityProviderFactory.class); private static final String LOGIN_IDENTITY_PROVIDERS_XSD = "/identity-providers.xsd"; @@ -125,6 +126,13 @@ public LoginIdentityProvider getLoginIdentityProvider() throws Exception { return loginIdentityProvider; } + @Override + public void destroy() throws Exception { + if (loginIdentityProviders != null) { + loginIdentityProviders.entrySet().stream().forEach(e -> e.getValue().preDestruction()); + } + } + private IdentityProviders loadLoginIdentityProvidersConfiguration() throws Exception { final File loginIdentityProvidersConfigurationFile = properties.getIdentityProviderConfigurationFile();