Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New security response mapper mechanism (master) #4093

Merged
merged 2 commits into from
Apr 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.logging.Logger;

import io.helidon.common.serviceloader.HelidonServiceLoader;
import io.helidon.config.Config;
import io.helidon.security.AuthenticationResponse;
import io.helidon.security.AuthorizationResponse;
Expand All @@ -47,6 +49,9 @@
abstract class SecurityFilterCommon {
static final String PROP_FILTER_CONTEXT = "io.helidon.security.jersey.FilterContext";

private static final List<SecurityResponseMapper> RESPONSE_MAPPERS = HelidonServiceLoader
.builder(ServiceLoader.load(SecurityResponseMapper.class)).build().asList();

@Context
private Security security;

Expand Down Expand Up @@ -343,7 +348,10 @@ protected void abortRequest(SecurityFilter.FilterContext context,
updateHeaders(responseHeaders, responseBuilder);
}

if (featureConfig.isDebug()) {
// Run security response mappers if available, or revert to old logic for compatibility
if (!RESPONSE_MAPPERS.isEmpty()) {
RESPONSE_MAPPERS.forEach(m -> m.aborted(response, responseBuilder));
} else if (featureConfig.isDebug()) {
response.description().ifPresent(responseBuilder::entity);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
*
* Licensed 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 io.helidon.security.integration.jersey;

import io.helidon.common.context.Contexts;
import io.helidon.security.SecurityResponse;

import jakarta.ws.rs.core.Response;

/**
* A {@link SecurityResponse} mapper that is called when a security error is
* encountered. Gives a chance for applications to craft a more informative
* response to the user as to the cause of the error.
*/
@FunctionalInterface
public interface SecurityResponseMapper {

/**
* Called when a security response is aborted due to a security problem (e.g. authentication
* failure). Handles control to the application to construct the response returned to
* the client. Security providers can provide context to mappers using the Helidon
* context mechanism.
*
* @param securityResponse the security response
* @param responseBuilder the response builder
* @see Contexts#context()
*/
void aborted(SecurityResponse securityResponse, Response.ResponseBuilder responseBuilder);
}
3 changes: 2 additions & 1 deletion security/integration/jersey/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2021 Oracle and/or its affiliates.
* Copyright (c) 2017, 2022 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -44,4 +44,5 @@
opens io.helidon.security.integration.jersey to hk2.locator,hk2.utils,weld.core.impl, io.helidon.microprofile.cdi;

uses io.helidon.security.providers.common.spi.AnnotationAnalyzer;
uses io.helidon.security.integration.jersey.SecurityResponseMapper;
}
3 changes: 2 additions & 1 deletion tests/integration/security/pom.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2020, 2021 Oracle and/or its affiliates.
Copyright (c) 2020, 2022 Oracle and/or its affiliates.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -39,5 +39,6 @@
<module>gh2297</module>
<module>gh2455</module>
<module>path-params</module>
<module>security-response-mapper</module>
</modules>
</project>
62 changes: 62 additions & 0 deletions tests/integration/security/security-response-mapper/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2020, 2022 Oracle and/or its affiliates.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>helidon-tests-integration-security</artifactId>
<groupId>io.helidon.tests.integration</groupId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>helidon-tests-integration-security-response-mapper</artifactId>
<name>Helidon Tests Integration Security Response Mappers</name>

<description>
Integration test for security response mappers
</description>

<dependencies>
<dependency>
<groupId>io.helidon.microprofile.bundles</groupId>
<artifactId>helidon-microprofile</artifactId>
</dependency>
<dependency>
<groupId>org.jboss</groupId>
<artifactId>jandex</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.helidon.microprofile.tests</groupId>
<artifactId>helidon-microprofile-tests-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
*
* Licensed 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 io.helidon.tests.integration.security.mapper;

import jakarta.ws.rs.core.Response;

import io.helidon.common.context.Contexts;
import io.helidon.security.SecurityResponse;
import io.helidon.security.integration.jersey.SecurityResponseMapper;

/**
* Mapper that intercepts creation of {@link Response} when a security
* error is encountered.
*/
public class MySecurityResponseMapper implements SecurityResponseMapper {

@Override
public void aborted(SecurityResponse securityResponse, Response.ResponseBuilder responseBuilder) {
responseBuilder.header("MAPPED-BY", "MySecurityResponseMapper");

// Access data set in RestrictedProvider
Contexts.context()
.flatMap(c -> c.get(RestrictedProvider.class, String.class))
.ifPresent(p -> responseBuilder.header("PROVIDER", p));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
*
* Licensed 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 io.helidon.tests.integration.security.mapper;

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;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
@Inherited
public @interface Restricted {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
*
* Licensed 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 io.helidon.tests.integration.security.mapper;

import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.List;

import io.helidon.common.context.Contexts;
import io.helidon.security.AuthenticationResponse;
import io.helidon.security.ProviderRequest;
import io.helidon.security.spi.AuthenticationProvider;
import io.helidon.security.spi.SynchronousProvider;

public class RestrictedProvider extends SynchronousProvider implements AuthenticationProvider {

/**
* Register an entry in {@link io.helidon.common.context.Context} and fail authentication.
*
* @param providerRequest provider request
* @return authentication response
*/
@Override
protected AuthenticationResponse syncAuthenticate(ProviderRequest providerRequest) {
// Use context to communicate with MySecurityResponseMapper
Contexts.context()
.ifPresent(c -> c.register(RestrictedProvider.class, getClass().getSimpleName()));
return AuthenticationResponse.failed("Oops");

}

@Override
public Collection<Class<? extends Annotation>> supportedAnnotations() {
return List.of(Restricted.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
*
* Licensed 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 io.helidon.tests.integration.security.mapper;

import io.helidon.config.Config;
import io.helidon.security.spi.SecurityProvider;
import io.helidon.security.spi.SecurityProviderService;

public class RestrictedProviderService implements SecurityProviderService {

@Override
public String providerConfigKey() {
return "restricted";
}

@Override
public Class<? extends SecurityProvider> providerClass() {
return RestrictedProvider.class;
}

@Override
public SecurityProvider providerInstance(Config config) {
return new RestrictedProvider();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
*
* Licensed 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 io.helidon.tests.integration.security.mapper;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import io.helidon.security.annotations.Authenticated;

@Path("restricted")
@Authenticated
public class RestrictedResource {

@GET
@Restricted
public String restricted() {
return "Never called";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.helidon.tests.integration.security.mapper.MySecurityResponseMapper
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.helidon.tests.integration.security.mapper.RestrictedProviderService
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#
# Copyright (c) 2022 Oracle and/or its affiliates.
#
# Licensed 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.
#

server:
port: -1

security:
providers:
- restricted:
Loading