You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by kd...@apache.org on 2018/09/22 02:11:14 UTC

[15/51] [partial] nifi-registry git commit: NIFIREG-201 Refactoring project structure to better isolate extensions

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/IllegalStateExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/IllegalStateExceptionMapper.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/IllegalStateExceptionMapper.java
new file mode 100644
index 0000000..11fc09d
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/IllegalStateExceptionMapper.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.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.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+@Component
+@Provider
+public class IllegalStateExceptionMapper implements ExceptionMapper<IllegalStateException> {
+
+    private static final Logger logger = LoggerFactory.getLogger(IllegalStateExceptionMapper.class);
+
+    @Override
+    public Response toResponse(IllegalStateException exception) {
+        // log the error
+        logger.info(String.format("%s. Returning %s response.", exception, Response.Status.CONFLICT));
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(StringUtils.EMPTY, exception);
+        }
+
+        return Response.status(Response.Status.CONFLICT).entity(exception.getMessage()).type("text/plain").build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/InvalidAuthenticationExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/InvalidAuthenticationExceptionMapper.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/InvalidAuthenticationExceptionMapper.java
new file mode 100644
index 0000000..15bb227
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/InvalidAuthenticationExceptionMapper.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.apache.nifi.registry.web.security.authentication.exception.InvalidAuthenticationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Maps access denied exceptions into a client response.
+ */
+@Component
+@Provider
+public class InvalidAuthenticationExceptionMapper implements ExceptionMapper<InvalidAuthenticationException> {
+
+    private static final Logger logger = LoggerFactory.getLogger(InvalidAuthenticationExceptionMapper.class);
+
+    @Override
+    public Response toResponse(InvalidAuthenticationException exception) {
+        if (logger.isDebugEnabled()) {
+            logger.debug(StringUtils.EMPTY, exception);
+        }
+
+        return Response.status(Response.Status.UNAUTHORIZED).entity(exception.getMessage()).type("text/plain").build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NiFiRegistryJsonProvider.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NiFiRegistryJsonProvider.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NiFiRegistryJsonProvider.java
new file mode 100644
index 0000000..10a044a
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NiFiRegistryJsonProvider.java
@@ -0,0 +1,36 @@
+/*
+ * 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.nifi.registry.serialization.jackson.ObjectMapperProvider;
+import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.ext.Provider;
+
+@Component
+@Provider
+@Produces(MediaType.APPLICATION_JSON)
+public class NiFiRegistryJsonProvider extends JacksonJaxbJsonProvider {
+
+    public NiFiRegistryJsonProvider() {
+        super();
+        setMapper(ObjectMapperProvider.getMapper());
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotAllowedExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotAllowedExceptionMapper.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotAllowedExceptionMapper.java
new file mode 100644
index 0000000..9237735
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotAllowedExceptionMapper.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.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.core.Response.Status;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Maps exceptions into client responses.
+ */
+@Component
+@Provider
+public class NotAllowedExceptionMapper implements ExceptionMapper<NotAllowedException> {
+
+    private static final Logger logger = LoggerFactory.getLogger(NotAllowedExceptionMapper.class);
+
+    @Override
+    public Response toResponse(NotAllowedException exception) {
+        logger.info(String.format("%s. Returning %s response.", exception, Status.METHOD_NOT_ALLOWED));
+        logger.debug(StringUtils.EMPTY, exception);
+        return Response.status(Status.METHOD_NOT_ALLOWED).entity(exception.getMessage()).type("text/plain").build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotFoundExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotFoundExceptionMapper.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotFoundExceptionMapper.java
new file mode 100644
index 0000000..8abd4c0
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/NotFoundExceptionMapper.java
@@ -0,0 +1,50 @@
+/*
+ * 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 javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * Maps not found exceptions into client responses.
+ */
+@Component
+@Provider
+public class NotFoundExceptionMapper implements ExceptionMapper<NotFoundException> {
+
+    private static final Logger logger = LoggerFactory.getLogger(NotFoundExceptionMapper.class);
+
+    @Override
+    public Response toResponse(NotFoundException exception) {
+        // log the error
+        logger.info(String.format("%s. Returning %s response.", exception, Response.Status.NOT_FOUND));
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(StringUtils.EMPTY, exception);
+        }
+
+        return Response.status(Response.Status.NOT_FOUND).entity(exception.getMessage()).type("text/plain").build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/ResourceNotFoundExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/ResourceNotFoundExceptionMapper.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/ResourceNotFoundExceptionMapper.java
new file mode 100644
index 0000000..a71452d
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/ResourceNotFoundExceptionMapper.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.apache.nifi.registry.exception.ResourceNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+@Component
+@Provider
+public class ResourceNotFoundExceptionMapper implements ExceptionMapper<ResourceNotFoundException> {
+
+    private static final Logger logger = LoggerFactory.getLogger(ResourceNotFoundExceptionMapper.class);
+
+    @Override
+    public Response toResponse(ResourceNotFoundException exception) {
+        // log the error
+        logger.info(String.format("%s. Returning %s response.", exception, Response.Status.NOT_FOUND));
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(StringUtils.EMPTY, exception);
+        }
+
+        return Response.status(Response.Status.NOT_FOUND).entity(exception.getMessage()).type("text/plain").build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/SerializationExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/SerializationExceptionMapper.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/SerializationExceptionMapper.java
new file mode 100644
index 0000000..8f53141
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/SerializationExceptionMapper.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.apache.nifi.registry.serialization.SerializationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+@Component
+@Provider
+public class SerializationExceptionMapper implements ExceptionMapper<SerializationException> {
+
+    private static final Logger logger = LoggerFactory.getLogger(SerializationExceptionMapper.class);
+
+    @Override
+    public Response toResponse(SerializationException exception) {
+        // log the error
+        logger.info(String.format("%s. Returning %s response.", exception, Response.Status.INTERNAL_SERVER_ERROR));
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(StringUtils.EMPTY, exception);
+        }
+
+        return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(exception.getMessage()).type("text/plain").build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/ThrowableMapper.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/ThrowableMapper.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/ThrowableMapper.java
new file mode 100644
index 0000000..4b434ed
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/ThrowableMapper.java
@@ -0,0 +1,42 @@
+/*
+ * 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 javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * Maps unknown node exceptions into client responses.
+ */
+@Component
+@Provider
+public class ThrowableMapper implements ExceptionMapper<Throwable> {
+
+    private static final Logger logger = LoggerFactory.getLogger(ThrowableMapper.class);
+
+    @Override
+    public Response toResponse(Throwable exception) {
+        // log the error
+        logger.error(String.format("An unexpected error has occurred: %s. Returning %s response.", exception, Response.Status.INTERNAL_SERVER_ERROR), exception);
+        return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("An unexpected error has occurred. Please check the logs for additional details.").type("text/plain").build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/UnauthorizedExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/UnauthorizedExceptionMapper.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/UnauthorizedExceptionMapper.java
new file mode 100644
index 0000000..1c67e94
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/UnauthorizedExceptionMapper.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.web.mapper;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.web.exception.UnauthorizedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Maps Unauthorized exceptions into client responses that set the WWW-Authenticate header
+ * with a list of challenges (i.e., acceptable auth scheme types).
+ */
+@Component
+@Provider
+public class UnauthorizedExceptionMapper implements ExceptionMapper<UnauthorizedException> {
+
+    private static final Logger logger = LoggerFactory.getLogger(UnauthorizedExceptionMapper.class);
+
+    private static final String AUTHENTICATION_CHALLENGE_HEADER_NAME = "WWW-Authenticate";
+
+    @Override
+    public Response toResponse(UnauthorizedException exception) {
+
+        logger.info("{}. Returning {} response.", exception, Response.Status.UNAUTHORIZED);
+        logger.debug(StringUtils.EMPTY, exception);
+
+        final Response.ResponseBuilder response = Response.status(Response.Status.UNAUTHORIZED);
+        if (exception.getWwwAuthenticateChallenge() != null) {
+            response.header(AUTHENTICATION_CHALLENGE_HEADER_NAME, exception.getWwwAuthenticateChallenge());
+        }
+        response.entity(exception.getMessage()).type("text/plain");
+        return response.build();
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistryMasterKeyProviderFactory.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistryMasterKeyProviderFactory.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistryMasterKeyProviderFactory.java
new file mode 100644
index 0000000..8026cca
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistryMasterKeyProviderFactory.java
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import org.apache.nifi.registry.NiFiRegistryApiApplication;
+import org.apache.nifi.registry.security.crypto.CryptoKeyProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.context.ServletContextAware;
+
+import javax.servlet.ServletContext;
+
+@Configuration
+public class NiFiRegistryMasterKeyProviderFactory implements ServletContextAware {
+
+    private static final Logger logger = LoggerFactory.getLogger(NiFiRegistryMasterKeyProviderFactory.class);
+
+    private CryptoKeyProvider masterKeyProvider = null;
+
+    @Bean
+    public CryptoKeyProvider getNiFiRegistryMasterKeyProvider() {
+        return masterKeyProvider;
+    }
+
+    @Override
+    public void setServletContext(ServletContext servletContext) {
+        Object rawKeyProviderObject = servletContext.getAttribute(NiFiRegistryApiApplication.NIFI_REGISTRY_MASTER_KEY_ATTRIBUTE);
+
+        if (rawKeyProviderObject == null) {
+            logger.warn("Value of {} was null. " +
+                    "{} bean will not be available in Application Context, so any attempt to load protected property values may fail.",
+                    NiFiRegistryApiApplication.NIFI_REGISTRY_MASTER_KEY_ATTRIBUTE,
+                    CryptoKeyProvider.class.getSimpleName());
+            return;
+        }
+
+        if (!(rawKeyProviderObject instanceof CryptoKeyProvider)) {
+            logger.warn("Expected value of {} to be of type {}, but instead got {}. " +
+                    "{} bean will NOT be available in Application Context, so any attempt to load protected property values may fail.",
+                    NiFiRegistryApiApplication.NIFI_REGISTRY_MASTER_KEY_ATTRIBUTE,
+                    CryptoKeyProvider.class.getName(),
+                    rawKeyProviderObject.getClass().getName(),
+                    CryptoKeyProvider.class.getSimpleName());
+            return;
+        }
+
+        logger.info("Updating Application Context with {} bean for obtaining NiFi Registry master key.", CryptoKeyProvider.class.getSimpleName());
+        masterKeyProvider = (CryptoKeyProvider) rawKeyProviderObject;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
new file mode 100644
index 0000000..a2a6ea9
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
@@ -0,0 +1,210 @@
+/*
+ * 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;
+
+import org.apache.nifi.registry.properties.NiFiRegistryProperties;
+import org.apache.nifi.registry.security.authorization.resource.ResourceType;
+import org.apache.nifi.registry.service.AuthorizationService;
+import org.apache.nifi.registry.web.security.authentication.AnonymousIdentityFilter;
+import org.apache.nifi.registry.web.security.authentication.IdentityAuthenticationProvider;
+import org.apache.nifi.registry.web.security.authentication.IdentityFilter;
+import org.apache.nifi.registry.web.security.authentication.exception.UntrustedProxyException;
+import org.apache.nifi.registry.web.security.authentication.jwt.JwtIdentityProvider;
+import org.apache.nifi.registry.web.security.authentication.x509.X509IdentityAuthenticationProvider;
+import org.apache.nifi.registry.web.security.authentication.x509.X509IdentityProvider;
+import org.apache.nifi.registry.web.security.authorization.ResourceAuthorizationFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
+import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * NiFi Registry Web Api Spring security
+ */
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class NiFiRegistrySecurityConfig extends WebSecurityConfigurerAdapter {
+
+    private static final Logger logger = LoggerFactory.getLogger(NiFiRegistrySecurityConfig.class);
+
+    @Autowired
+    private NiFiRegistryProperties properties;
+
+    @Autowired
+    private AuthorizationService authorizationService;
+
+    private AnonymousIdentityFilter anonymousAuthenticationFilter = new AnonymousIdentityFilter();
+
+    @Autowired
+    private X509IdentityProvider x509IdentityProvider;
+    private IdentityFilter x509AuthenticationFilter;
+    private IdentityAuthenticationProvider x509AuthenticationProvider;
+
+    @Autowired
+    private JwtIdentityProvider jwtIdentityProvider;
+    private IdentityFilter jwtAuthenticationFilter;
+    private IdentityAuthenticationProvider jwtAuthenticationProvider;
+
+    private ResourceAuthorizationFilter resourceAuthorizationFilter;
+
+    public NiFiRegistrySecurityConfig() {
+        super(true); // disable defaults
+    }
+
+    @Override
+    public void configure(WebSecurity webSecurity) throws Exception {
+        // allow any client to access the endpoint for logging in to generate an access token
+        webSecurity.ignoring().antMatchers( "/access/token/**");
+    }
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http
+                .rememberMe().disable()
+                .authorizeRequests()
+                    .anyRequest().fullyAuthenticated()
+                    .and()
+                .exceptionHandling()
+                    .authenticationEntryPoint(http401AuthenticationEntryPoint())
+                    .and()
+                .sessionManagement()
+                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+
+        // x509
+        http.addFilterBefore(x509AuthenticationFilter(), AnonymousAuthenticationFilter.class);
+
+        // jwt
+        http.addFilterBefore(jwtAuthenticationFilter(), AnonymousAuthenticationFilter.class);
+
+        // otp
+        // todo, if needed one-time password auth filter goes here
+
+        if (properties.getSslPort() == null) {
+            // If we are running an unsecured NiFi Registry server, add an
+            // anonymous authentication filter that will populate the
+            // authenticated, anonymous user if no other user identity
+            // is detected earlier in the Spring filter chain.
+            http.anonymous().authenticationFilter(anonymousAuthenticationFilter);
+        }
+
+        // After Spring Security filter chain is complete (so authentication is done),
+        // but before the Jersey application endpoints get the request,
+        // insert the ResourceAuthorizationFilter to do its authorization checks
+        http.addFilterAfter(resourceAuthorizationFilter(), FilterSecurityInterceptor.class);
+
+    }
+
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+        auth
+                .authenticationProvider(x509AuthenticationProvider())
+                .authenticationProvider(jwtAuthenticationProvider());
+    }
+
+    private IdentityFilter x509AuthenticationFilter() throws Exception {
+        if (x509AuthenticationFilter == null) {
+            x509AuthenticationFilter = new IdentityFilter(x509IdentityProvider);
+        }
+        return x509AuthenticationFilter;
+    }
+
+    private IdentityAuthenticationProvider x509AuthenticationProvider() {
+        if (x509AuthenticationProvider == null) {
+            x509AuthenticationProvider = new X509IdentityAuthenticationProvider(properties, authorizationService.getAuthorizer(), x509IdentityProvider);
+        }
+        return x509AuthenticationProvider;
+    }
+
+    private IdentityFilter jwtAuthenticationFilter() throws Exception {
+        if (jwtAuthenticationFilter == null) {
+            jwtAuthenticationFilter = new IdentityFilter(jwtIdentityProvider);
+        }
+        return jwtAuthenticationFilter;
+    }
+
+    private IdentityAuthenticationProvider jwtAuthenticationProvider() {
+        if (jwtAuthenticationProvider == null) {
+            jwtAuthenticationProvider = new IdentityAuthenticationProvider(properties, authorizationService.getAuthorizer(), jwtIdentityProvider);
+        }
+        return jwtAuthenticationProvider;
+    }
+
+    private ResourceAuthorizationFilter resourceAuthorizationFilter() {
+        if (resourceAuthorizationFilter == null) {
+            resourceAuthorizationFilter = ResourceAuthorizationFilter.builder()
+                    .setAuthorizationService(authorizationService)
+                    .addResourceType(ResourceType.Actuator)
+                    .addResourceType(ResourceType.Swagger)
+                    .build();
+        }
+        return resourceAuthorizationFilter;
+    }
+
+    private AuthenticationEntryPoint http401AuthenticationEntryPoint() {
+        // This gets used for both secured and unsecured configurations. It will be called by Spring Security if a request makes it through the filter chain without being authenticated.
+        // For unsecured, this should never be reached because the custom AnonymousAuthenticationFilter should always populate a fully-authenticated anonymous user
+        // For secured, this will cause attempt to access any API endpoint (except those explicitly ignored) without providing credentials to return a 401 Unauthorized challenge
+        return new AuthenticationEntryPoint() {
+            @Override
+            public void commence(HttpServletRequest request,
+                                 HttpServletResponse response,
+                                 AuthenticationException authenticationException)
+                    throws IOException, ServletException {
+
+                final int status;
+
+                // See X509IdentityAuthenticationProvider.buildAuthenticatedToken(...)
+                if (authenticationException instanceof UntrustedProxyException) {
+                    // return a 403 response
+                    status = HttpServletResponse.SC_FORBIDDEN;
+                    logger.info("Identity in proxy chain not trusted to act as a proxy: {} Returning 403 response.", authenticationException.toString());
+
+                } else {
+                    // return a 401 response
+                    status = HttpServletResponse.SC_UNAUTHORIZED;
+                    logger.info("Client could not be authenticated due to: {} Returning 401 response.", authenticationException.toString());
+                }
+
+                logger.debug("", authenticationException);
+
+                if (!response.isCommitted()) {
+                    response.setStatus(status);
+                    response.setContentType("text/plain");
+                    response.getWriter().println(String.format("%s Contact the system administrator.", authenticationException.getLocalizedMessage()));
+                }
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/PermissionsService.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/PermissionsService.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/PermissionsService.java
new file mode 100644
index 0000000..1e00ee1
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/PermissionsService.java
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+import org.apache.nifi.registry.bucket.Bucket;
+import org.apache.nifi.registry.bucket.BucketItem;
+import org.apache.nifi.registry.authorization.Permissions;
+import org.apache.nifi.registry.security.authorization.AuthorizableLookup;
+import org.apache.nifi.registry.security.authorization.resource.Authorizable;
+import org.apache.nifi.registry.service.AuthorizationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * This is a class that Resource classes can utilized to populate fields
+ * on model objects returned by the {@link org.apache.nifi.registry.service.RegistryService}
+ * before returning them to a client.
+ *
+ * The fields cannot be populated by the RegistryService because they require
+ * the {@link AuthorizationService}, which RegistryService does not depend on.
+ */
+@Service
+public class PermissionsService {
+
+    private AuthorizationService authorizationService;
+    private AuthorizableLookup authorizableLookup;
+
+    @Autowired
+    public PermissionsService(AuthorizationService authorizationService, AuthorizableLookup authorizableLookup) {
+        this.authorizationService = authorizationService;
+        this.authorizableLookup = authorizableLookup;
+    }
+
+    public void populateBucketPermissions(final Iterable<Bucket> buckets) {
+        Permissions topLevelBucketPermissions = authorizationService.getPermissionsForResource(authorizableLookup.getBucketsAuthorizable());
+        buckets.forEach(b -> populateBucketPermissions(b, topLevelBucketPermissions));
+    }
+
+    public void populateBucketPermissions(final Bucket bucket) {
+        populateBucketPermissions(bucket, null);
+    }
+
+    public void populateItemPermissions(final Iterable<? extends BucketItem> bucketItems) {
+        Permissions topLevelBucketPermissions = authorizationService.getPermissionsForResource(authorizableLookup.getBucketsAuthorizable());
+        bucketItems.forEach(i -> populateItemPermissions(i, topLevelBucketPermissions));
+    }
+
+    public void populateItemPermissions(final BucketItem bucketItem) {
+        populateItemPermissions(bucketItem, null);
+    }
+
+    private void populateBucketPermissions(final Bucket bucket, final Permissions knownPermissions) {
+
+        if (bucket == null) {
+            return;
+        }
+
+        Permissions bucketPermissions = createPermissionsForBucketId(bucket.getIdentifier(), knownPermissions);
+        bucket.setPermissions(bucketPermissions);
+
+    }
+
+    private void populateItemPermissions(final BucketItem bucketItem, final Permissions knownPermissions) {
+
+        if (bucketItem == null) {
+            return;
+        }
+
+        Permissions bucketItemPermissions = createPermissionsForBucketId(bucketItem.getBucketIdentifier(), knownPermissions);
+        bucketItem.setPermissions(bucketItemPermissions);
+
+    }
+
+    private Permissions createPermissionsForBucketId(String bucketId, final Permissions knownPermissions) {
+
+        Authorizable bucketResource = authorizableLookup.getBucketAuthorizable(bucketId);
+
+        Permissions permissions = knownPermissions == null
+                ? authorizationService.getPermissionsForResource(bucketResource)
+                : authorizationService.getPermissionsForResource(bucketResource, knownPermissions);
+
+        return permissions;
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/AnonymousIdentityFilter.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/AnonymousIdentityFilter.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/AnonymousIdentityFilter.java
new file mode 100644
index 0000000..f879f0d
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/AnonymousIdentityFilter.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.web.security.authentication;
+
+import org.apache.nifi.registry.security.authorization.user.NiFiUserDetails;
+import org.apache.nifi.registry.security.authorization.user.StandardNiFiUser;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class AnonymousIdentityFilter extends AnonymousAuthenticationFilter {
+
+    private static final String ANONYMOUS_KEY = "anonymousNifiKey";
+
+    public AnonymousIdentityFilter() {
+        super(ANONYMOUS_KEY);
+    }
+
+    @Override
+    protected Authentication createAuthentication(HttpServletRequest request) {
+        return new AuthenticationSuccessToken(new NiFiUserDetails(StandardNiFiUser.ANONYMOUS));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/AuthenticationRequestToken.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/AuthenticationRequestToken.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/AuthenticationRequestToken.java
new file mode 100644
index 0000000..a5a5ec3
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/AuthenticationRequestToken.java
@@ -0,0 +1,107 @@
+/*
+ * 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.AuthenticationRequest;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+
+import java.security.Principal;
+import java.util.Collection;
+
+/**
+ * Wraps an AuthenticationRequest in a Token that implements the Spring Security Authentication interface.
+ */
+public class AuthenticationRequestToken implements Authentication {
+
+    private final AuthenticationRequest authenticationRequest;
+    private final Class<?> authenticationRequestOrigin;
+    private final String clientAddress;
+
+    public AuthenticationRequestToken(AuthenticationRequest authenticationRequest, Class<?> authenticationRequestOrigin, String clientAddress) {
+        this.authenticationRequest = authenticationRequest;
+        this.authenticationRequestOrigin = authenticationRequestOrigin;
+        this.clientAddress = clientAddress;
+    }
+
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        return null;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return authenticationRequest.getCredentials();
+    }
+
+    @Override
+    public Object getDetails() {
+        return authenticationRequest.getDetails();
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return new Principal() {
+            @Override
+            public String getName() {
+                return authenticationRequest.getUsername();
+            }
+        };
+    }
+
+    @Override
+    public boolean isAuthenticated() {
+        return false;
+    }
+
+    @Override
+    public void setAuthenticated(boolean b) throws IllegalArgumentException {
+        throw new IllegalArgumentException("AuthenticationRequestWrapper cannot be trusted. It is only to be used for storing an identity claim.");
+    }
+
+    @Override
+    public String getName() {
+        return authenticationRequest.getUsername();
+    }
+
+    @Override
+    public int hashCode() {
+        return authenticationRequest.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return authenticationRequest.equals(obj);
+    }
+
+    @Override
+    public String toString() {
+        return authenticationRequest.toString();
+    }
+
+    public AuthenticationRequest getAuthenticationRequest() {
+        return authenticationRequest;
+    }
+
+    public Class<?> getAuthenticationRequestOrigin() {
+        return authenticationRequestOrigin;
+    }
+
+    public String getClientAddress() {
+        return clientAddress;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/AuthenticationSuccessToken.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/AuthenticationSuccessToken.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/AuthenticationSuccessToken.java
new file mode 100644
index 0000000..ea6f1e9
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/AuthenticationSuccessToken.java
@@ -0,0 +1,55 @@
+/*
+ * 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.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.userdetails.UserDetails;
+
+/**
+ * An authentication token that represents an Authenticated and Authorized user of the NiFi Apis. The authorities are based off the specified UserDetails.
+ */
+public class AuthenticationSuccessToken extends AbstractAuthenticationToken {
+
+    private final UserDetails nifiUserDetails;
+
+    public AuthenticationSuccessToken(final UserDetails nifiUserDetails) {
+        super(nifiUserDetails.getAuthorities());
+        super.setAuthenticated(true);
+        setDetails(nifiUserDetails);
+        this.nifiUserDetails = nifiUserDetails;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return nifiUserDetails.getPassword();
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return nifiUserDetails;
+    }
+
+    @Override
+    public final void setAuthenticated(boolean authenticated) {
+        throw new IllegalArgumentException("Cannot change the authenticated state.");
+    }
+
+    @Override
+    public String toString() {
+        return nifiUserDetails.getUsername();
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationProvider.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationProvider.java
new file mode 100644
index 0000000..ff6a218
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityAuthenticationProvider.java
@@ -0,0 +1,140 @@
+/*
+ * 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.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.AuthenticationRequest;
+import org.apache.nifi.registry.security.authentication.AuthenticationResponse;
+import org.apache.nifi.registry.security.authentication.IdentityProvider;
+import org.apache.nifi.registry.security.authentication.exception.InvalidCredentialsException;
+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.security.authorization.user.NiFiUserDetails;
+import org.apache.nifi.registry.security.authorization.user.StandardNiFiUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class IdentityAuthenticationProvider implements AuthenticationProvider {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(IdentityAuthenticationProvider.class);
+
+    protected NiFiRegistryProperties properties;
+    protected Authorizer authorizer;
+    protected final IdentityProvider identityProvider;
+    private List<IdentityMapping> mappings;
+
+    public IdentityAuthenticationProvider(
+            NiFiRegistryProperties properties,
+            Authorizer authorizer,
+            IdentityProvider identityProvider) {
+        this.properties = properties;
+        this.authorizer = authorizer;
+        this.identityProvider = identityProvider;
+        this.mappings = Collections.unmodifiableList(IdentityMappingUtil.getIdentityMappings(properties));
+    }
+
+    @Override
+    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+
+        // Determine if this AuthenticationProvider's identityProvider should be able to support this AuthenticationRequest
+        boolean tokenOriginatedFromThisIdentityProvider = checkTokenOriginatedFromThisIdentityProvider(authentication);
+
+        if (!tokenOriginatedFromThisIdentityProvider) {
+            // Returning null indicates to The Spring Security AuthenticationManager that this AuthenticationProvider
+            // cannot authenticate this token and another provider should be tried.
+            return null;
+        }
+
+        AuthenticationRequestToken authenticationRequestToken = ((AuthenticationRequestToken)authentication);
+        AuthenticationRequest authenticationRequest = authenticationRequestToken.getAuthenticationRequest();
+
+        try {
+            AuthenticationResponse authenticationResponse = identityProvider.authenticate(authenticationRequest);
+            if (authenticationResponse == null) {
+                return null;
+            }
+            return buildAuthenticatedToken(authenticationRequestToken, authenticationResponse);
+        } catch (InvalidCredentialsException e) {
+            throw new BadCredentialsException("Identity Provider authentication failed.", e);
+        }
+
+    }
+
+    @Override
+    public boolean supports(Class<?> authenticationClazz) {
+        // is authenticationClazz a subclass of AuthenticationRequestWrapper?
+        return AuthenticationRequestToken.class.isAssignableFrom(authenticationClazz);
+    }
+
+    protected AuthenticationSuccessToken buildAuthenticatedToken(
+            AuthenticationRequestToken requestToken,
+            AuthenticationResponse response) {
+
+        final String mappedIdentity = mapIdentity(response.getIdentity());
+
+        return new AuthenticationSuccessToken(new NiFiUserDetails(
+                new StandardNiFiUser.Builder()
+                        .identity(mappedIdentity)
+                        .groups(getUserGroups(mappedIdentity))
+                        .clientAddress(requestToken.getClientAddress())
+                        .build()));
+    }
+
+    protected boolean checkTokenOriginatedFromThisIdentityProvider(Authentication authentication) {
+        return (authentication instanceof AuthenticationRequestToken
+                && identityProvider.getClass().equals(((AuthenticationRequestToken) authentication).getAuthenticationRequestOrigin()));
+    }
+
+    protected String mapIdentity(final String identity) {
+        return IdentityMappingUtil.mapIdentity(identity, mappings);
+    }
+
+    protected Set<String> getUserGroups(final String identity) {
+        return getUserGroups(authorizer, identity);
+    }
+
+    private static Set<String> getUserGroups(final Authorizer authorizer, final String userIdentity) {
+        if (authorizer instanceof ManagedAuthorizer) {
+            final ManagedAuthorizer managedAuthorizer = (ManagedAuthorizer) authorizer;
+            final UserGroupProvider userGroupProvider = managedAuthorizer.getAccessPolicyProvider().getUserGroupProvider();
+            final UserAndGroups userAndGroups = userGroupProvider.getUserAndGroups(userIdentity);
+            final Set<Group> userGroups = userAndGroups.getGroups();
+
+            if (userGroups == null || userGroups.isEmpty()) {
+                return Collections.emptySet();
+            } else {
+                return userAndGroups.getGroups().stream().map(Group::getName).collect(Collectors.toSet());
+            }
+        } else {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityFilter.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityFilter.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityFilter.java
new file mode 100644
index 0000000..cd5e2bf
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/IdentityFilter.java
@@ -0,0 +1,97 @@
+/*
+ * 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.AuthenticationRequest;
+import org.apache.nifi.registry.security.authentication.IdentityProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.filter.GenericFilterBean;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * A class that will extract an identity / credentials claim from an HttpServlet Request using an injected IdentityProvider.
+ *
+ * This class is designed to be used in collaboration with an {@link IdentityAuthenticationProvider}. The identity/credentials will be
+ * extracted by this filter and later validated by the {@link IdentityAuthenticationProvider} in the default SecurityInterceptorFilter.
+ */
+public class IdentityFilter extends GenericFilterBean {
+
+    private static final Logger logger = LoggerFactory.getLogger(IdentityFilter.class);
+
+    private final IdentityProvider identityProvider;
+
+    public IdentityFilter(IdentityProvider identityProvider) {
+        this.identityProvider = identityProvider;
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+
+        // Only require authentication from an identity provider if the NiFi registry is running securely.
+        if (!servletRequest.isSecure()) {
+            // Otherwise, requests will be "authenticated" by the AnonymousIdentityFilter
+            filterChain.doFilter(servletRequest, servletResponse);
+            return;
+        }
+
+        if (identityProvider == null) {
+            logger.warn("Identity Filter configured with NULL identity provider. Credentials will not be extracted.");
+            filterChain.doFilter(servletRequest, servletResponse);
+            return;
+        }
+
+        if (credentialsAlreadyPresent()) {
+            logger.debug("Credentials already extracted for [{}], skipping credentials extraction filter using {}",
+                    SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString(),
+                    identityProvider.getClass().getSimpleName());
+            filterChain.doFilter(servletRequest, servletResponse);
+            return;
+        }
+
+        logger.debug("Attempting to extract user credentials using {}", identityProvider.getClass().getSimpleName());
+
+        try {
+            AuthenticationRequest authenticationRequest = identityProvider.extractCredentials((HttpServletRequest)servletRequest);
+            if (authenticationRequest != null) {
+                Authentication authentication = new AuthenticationRequestToken(authenticationRequest, identityProvider.getClass(), servletRequest.getRemoteAddr());
+                logger.debug("Adding credentials claim to SecurityContext to be authenticated. Credentials extracted by {}: {}",
+                        identityProvider.getClass().getSimpleName(),
+                        authenticationRequest);
+                SecurityContextHolder.getContext().setAuthentication(authentication);
+                // This filter's job, which is merely to search for and extract an identity claim, is done.
+                // The actual authentication of the identity claim will be handled by a corresponding IdentityAuthenticationProvider
+            }
+        } catch (Exception e) {
+            logger.debug("Exception occurred while extracting credentials:", e);
+        }
+
+        filterChain.doFilter(servletRequest, servletResponse);
+    }
+
+    private boolean credentialsAlreadyPresent() {
+        return SecurityContextHolder.getContext().getAuthentication() != null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/InvalidAuthenticationException.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/InvalidAuthenticationException.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/InvalidAuthenticationException.java
new file mode 100644
index 0000000..016e9cb
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/InvalidAuthenticationException.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.web.security.authentication.exception;
+
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * Thrown if the authentication of a given request is invalid. For instance,
+ * an expired certificate or token.
+ */
+public class InvalidAuthenticationException extends AuthenticationException {
+
+    public InvalidAuthenticationException(String msg) {
+        super(msg);
+    }
+
+    public InvalidAuthenticationException(String msg, Throwable t) {
+        super(msg, t);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/UntrustedProxyException.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/UntrustedProxyException.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/UntrustedProxyException.java
new file mode 100644
index 0000000..82570a3
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/exception/UntrustedProxyException.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.registry.web.security.authentication.exception;
+
+import org.springframework.security.core.AuthenticationException;
+
+public class UntrustedProxyException extends AuthenticationException {
+
+    public UntrustedProxyException(String msg) {
+        super(msg);
+    }
+
+    public UntrustedProxyException(String msg, Throwable t) {
+        super(msg, t);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtIdentityProvider.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtIdentityProvider.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtIdentityProvider.java
new file mode 100644
index 0000000..d3f12c9
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtIdentityProvider.java
@@ -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.web.security.authentication.jwt;
+
+import io.jsonwebtoken.JwtException;
+import org.apache.nifi.registry.properties.NiFiRegistryProperties;
+import org.apache.nifi.registry.security.authentication.AuthenticationRequest;
+import org.apache.nifi.registry.security.authentication.AuthenticationResponse;
+import org.apache.nifi.registry.security.authentication.BearerAuthIdentityProvider;
+import org.apache.nifi.registry.security.authentication.IdentityProvider;
+import org.apache.nifi.registry.security.authentication.IdentityProviderConfigurationContext;
+import org.apache.nifi.registry.security.authentication.exception.IdentityAccessException;
+import org.apache.nifi.registry.security.authentication.exception.InvalidCredentialsException;
+import org.apache.nifi.registry.security.authorization.Authorizer;
+import org.apache.nifi.registry.security.exception.SecurityProviderCreationException;
+import org.apache.nifi.registry.security.exception.SecurityProviderDestructionException;
+import org.apache.nifi.registry.web.security.authentication.exception.InvalidAuthenticationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class JwtIdentityProvider extends BearerAuthIdentityProvider implements IdentityProvider {
+
+    private static final Logger logger = LoggerFactory.getLogger(JwtIdentityProvider.class);
+
+    private static final String issuer = JwtIdentityProvider.class.getSimpleName();
+
+    private static final long expiration = TimeUnit.MILLISECONDS.convert(12, TimeUnit.HOURS);
+
+    private final JwtService jwtService;
+
+    @Autowired
+    public JwtIdentityProvider(JwtService jwtService, NiFiRegistryProperties nifiProperties, Authorizer authorizer) {
+        this.jwtService = jwtService;
+    }
+
+    @Override
+    public AuthenticationResponse authenticate(AuthenticationRequest authenticationRequest) throws InvalidCredentialsException, IdentityAccessException {
+
+        if (authenticationRequest == null) {
+            logger.info("Cannot authenticate null authenticationRequest, returning null.");
+            return null;
+        }
+
+        final Object credentials = authenticationRequest.getCredentials();
+        String jwtAuthToken = credentials != null && credentials instanceof String ? (String) credentials : null;
+
+        if (credentials == null) {
+            logger.info("JWT not found in authenticationRequest credentials, returning null.");
+            return null;
+        }
+
+        try {
+            final String jwtPrincipal = jwtService.getAuthenticationFromToken(jwtAuthToken);
+            return new AuthenticationResponse(jwtPrincipal, jwtPrincipal, expiration, issuer);
+        } catch (JwtException e) {
+            throw new InvalidAuthenticationException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public void onConfigured(IdentityProviderConfigurationContext configurationContext) throws SecurityProviderCreationException {}
+
+    @Override
+    public void preDestruction() throws SecurityProviderDestructionException {}
+
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/6f26290d/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java
----------------------------------------------------------------------
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java
new file mode 100644
index 0000000..d47b301
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/authentication/jwt/JwtService.java
@@ -0,0 +1,212 @@
+/*
+ * 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.security.authentication.AuthenticationResponse;
+import org.apache.nifi.registry.security.key.Key;
+import org.apache.nifi.registry.security.key.KeyService;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.concurrent.TimeUnit;
+
+// TODO, look into replacing this JwtService service with Apache Licensed JJWT library
+@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;
+
+    @Autowired
+    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<Claims> 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 IdentityProvider?
+            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<Claims> 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 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 IdentityProvider AuthenticationResponse
+     *
+     * @param authenticationResponse an instance issued by an IdentityProvider after identity claim has been verified as authentic
+     * @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 AuthenticationResponse authenticationResponse) throws JwtException {
+        if (authenticationResponse == null) {
+            throw new IllegalArgumentException("Cannot generate a JWT for a null authenticationResponse");
+        }
+
+        return generateSignedToken(
+                authenticationResponse.getIdentity(),
+                authenticationResponse.getUsername(),
+                authenticationResponse.getIssuer(),
+                authenticationResponse.getIssuer(),
+                authenticationResponse.getExpiration());
+    }
+
+    public String generateSignedToken(String identity, String preferredUsername, String issuer, String audience, long expirationMillis) throws JwtException {
+
+        if (identity == null || StringUtils.isEmpty(identity)) {
+            String errorMessage = "Cannot generate a JWT for a token with an empty identity";
+            errorMessage = issuer != null ? errorMessage + " issued by " + issuer + "." : ".";
+            logger.error(errorMessage);
+            throw new IllegalArgumentException(errorMessage);
+        }
+
+        // Compute expiration
+        final Calendar now = Calendar.getInstance();
+        long expirationMillisRelativeToNow = validateTokenExpiration(expirationMillis, identity);
+        long expirationMillisSinceEpoch = now.getTimeInMillis() + expirationMillisRelativeToNow;
+        final Calendar expiration = new Calendar.Builder().setInstant(expirationMillisSinceEpoch).build();
+
+        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 " + describe(authenticationResponse));
+
+            // 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(issuer)
+                    .setAudience(audience)
+                    .claim(USERNAME_CLAIM, preferredUsername)
+                    .claim(KEY_ID_CLAIM, key.getId())
+                    .setIssuedAt(now.getTime())
+                    .setExpiration(expiration.getTime())
+                    .signWith(SIGNATURE_ALGORITHM, keyBytes).compact();
+        } catch (NullPointerException e) {
+            final String errorMessage = "Could not retrieve the signing key for JWT for " + identity;
+            logger.error(errorMessage, e);
+            throw new JwtException(errorMessage, e);
+        }
+
+    }
+
+    private static 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;
+    }
+
+    private static String describe(AuthenticationResponse authenticationResponse) {
+        Calendar expirationTime = Calendar.getInstance();
+        expirationTime.setTimeInMillis(authenticationResponse.getExpiration());
+        long remainingTime = expirationTime.getTimeInMillis() - Calendar.getInstance().getTimeInMillis();
+
+        SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss.SSS");
+        dateFormat.setTimeZone(expirationTime.getTimeZone());
+        String expirationTimeString = dateFormat.format(expirationTime.getTime());
+
+        return new StringBuilder("LoginAuthenticationToken for ")
+                .append(authenticationResponse.getUsername())
+                .append(" issued by ")
+                .append(authenticationResponse.getIssuer())
+                .append(" expiring at ")
+                .append(expirationTimeString)
+                .append(" [")
+                .append(authenticationResponse.getExpiration())
+                .append(" ms, ")
+                .append(remainingTime)
+                .append(" ms remaining]")
+                .toString();
+    }
+}