Skip to content

Commit

Permalink
fix: provide valid scope value to DeviceNotification service
Browse files Browse the repository at this point in the history
fixes AM-4722
  • Loading branch information
leleueri committed Feb 3, 2025
1 parent 53e7d75 commit 120b6ef
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,22 @@
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-standalone</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -113,4 +129,4 @@
</plugin>
</plugins>
</build>
</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import io.vertx.rxjava3.core.buffer.Buffer;
import io.vertx.rxjava3.ext.web.client.HttpRequest;
import io.vertx.rxjava3.ext.web.client.WebClient;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -39,6 +41,7 @@
import org.springframework.util.CollectionUtils;

import java.util.Optional;
import java.util.stream.Collectors;

import static org.springframework.util.StringUtils.hasText;

Expand All @@ -47,6 +50,8 @@
* @author GraviteeSource Team
*/
@Import(HttpAuthenticationDeviceProviderSpringConfiguration.class)
@AllArgsConstructor
@NoArgsConstructor
public class HttpAuthenticationDeviceNotifierProvider implements AuthenticationDeviceNotifierProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpAuthenticationDeviceNotifierProvider.class);

Expand Down Expand Up @@ -128,7 +133,7 @@ private MultiMap prepareFormData(ADNotificationRequest request) {
formData.set(TRANSACTION_ID, request.getTransactionId());
formData.set(STATE, request.getState());
formData.set(PARAM_SUBJECT, request.getSubject());
formData.set(PARAM_SCOPE, request.getScopes());
formData.set(PARAM_SCOPE, request.getScopes().stream().collect(Collectors.joining(" ")));
formData.set(PARAM_EXPIRE, Integer.toString(request.getExpiresIn()));

if (!CollectionUtils.isEmpty(request.getAcrValues())) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* 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.gravitee.am.authdevice.notifier.http.provider;


import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
import com.github.tomakehurst.wiremock.matching.EqualToPattern;
import io.gravitee.am.authdevice.notifier.api.exception.DeviceNotificationException;
import io.gravitee.am.authdevice.notifier.api.model.ADNotificationRequest;
import io.gravitee.am.authdevice.notifier.http.HttpAuthenticationDeviceNotifierConfiguration;
import io.vertx.rxjava3.core.Vertx;
import io.vertx.rxjava3.ext.web.client.WebClient;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static com.github.tomakehurst.wiremock.client.WireMock.notFound;
import static com.github.tomakehurst.wiremock.client.WireMock.okJson;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;

/**
* @author Eric LELEU (eric.leleu at graviteesource.com)
* @author GraviteeSource Team
*/
public class HttpAuthenticationDeviceNotifierProviderTest {

public static final String NOTIFY_ENDPOINT = "/notify";
@RegisterExtension
private static WireMockExtension wiremock = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort().dynamicHttpsPort())
.build();

private static HttpAuthenticationDeviceNotifierConfiguration config = new HttpAuthenticationDeviceNotifierConfiguration();
private static WebClient webClient;

@BeforeAll
public static void initWebClient() {
webClient = WebClient.create(Vertx.vertx());
config.setEndpoint("http://localhost:" + wiremock.getRuntimeInfo().getHttpPort() + NOTIFY_ENDPOINT);
}

@Test
void notifier_client_should_provide_all_scopes() throws Exception {
final var notificationRequest = new ADNotificationRequest();
notificationRequest.setDeviceNotifierId(UUID.randomUUID().toString());
notificationRequest.setScopes(new TreeSet<>(Set.of("email", "openid", "profile")));
notificationRequest.setMessage(UUID.randomUUID().toString());
notificationRequest.setSubject(UUID.randomUUID().toString());
notificationRequest.setTransactionId("transaction_id");
notificationRequest.setState("mystate");

// The static DSL will be automatically configured for you
wiremock.stubFor(post(NOTIFY_ENDPOINT)
.withFormParam("scope", new EqualToPattern("email openid profile"))
.willReturn(okJson("""
{
"tid": "transaction_id",
"state": "mystate",
"data": {
"a": "avalue",
"b": "bvalue"
}
}
""")));

var observer = new HttpAuthenticationDeviceNotifierProvider(webClient, config).notify(notificationRequest).test();
observer.await(10, TimeUnit.SECONDS);
observer.assertNoErrors();
observer.assertValue(notifierResponse -> notifierResponse.getTransactionId().equals(notificationRequest.getTransactionId()));
observer.assertValue(notifierResponse -> notifierResponse.getExtraData().containsKey("a") && notifierResponse.getExtraData().containsKey("b"));
}

@Test
void notifier_should_reject_response_with_invalid_tid() throws Exception {
final var notificationRequest = new ADNotificationRequest();
notificationRequest.setDeviceNotifierId(UUID.randomUUID().toString());
notificationRequest.setScopes(new TreeSet<>(Set.of("email", "openid", "profile")));
notificationRequest.setMessage(UUID.randomUUID().toString());
notificationRequest.setSubject(UUID.randomUUID().toString());
notificationRequest.setTransactionId("transaction_id");
notificationRequest.setState("mystate");

// The static DSL will be automatically configured for you
wiremock.stubFor(post(NOTIFY_ENDPOINT)
.withFormParam("scope", new EqualToPattern("email openid profile"))
.willReturn(okJson("""
{
"tid": "unknown",
"state": "mystate",
"data": {
"a": "avalue",
"b": "bvalue"
}
}
""")));

var observer = new HttpAuthenticationDeviceNotifierProvider(webClient, config).notify(notificationRequest).test();
observer.await(10, TimeUnit.SECONDS);
observer.assertError(DeviceNotificationException.class);
observer.assertError(err -> err.getMessage().equals("Invalid device notification response"));
}

@Test
void notifier_should_reject_response_with_invalid_state() throws Exception {
final var notificationRequest = new ADNotificationRequest();
notificationRequest.setDeviceNotifierId(UUID.randomUUID().toString());
notificationRequest.setScopes(new TreeSet<>(Set.of("email", "openid", "profile")));
notificationRequest.setMessage(UUID.randomUUID().toString());
notificationRequest.setSubject(UUID.randomUUID().toString());
notificationRequest.setTransactionId("transaction_id");
notificationRequest.setState("mystate");

// The static DSL will be automatically configured for you
wiremock.stubFor(post(NOTIFY_ENDPOINT)
.withFormParam("scope", new EqualToPattern("email openid profile"))
.willReturn(okJson("""
{
"tid": "transaction_id",
"state": "anotherstate",
"data": {
"a": "avalue",
"b": "bvalue"
}
}
""")));

var observer = new HttpAuthenticationDeviceNotifierProvider(webClient, config).notify(notificationRequest).test();
observer.await(10, TimeUnit.SECONDS);
observer.assertError(DeviceNotificationException.class);
observer.assertError(err -> err.getMessage().equals("Invalid device notification response"));
}

@Test
void notifier_should_return_error_on_notOK_status() throws Exception {
final var notificationRequest = new ADNotificationRequest();
notificationRequest.setDeviceNotifierId(UUID.randomUUID().toString());
notificationRequest.setScopes(new TreeSet<>(Set.of("email", "openid", "profile")));
notificationRequest.setMessage(UUID.randomUUID().toString());
notificationRequest.setSubject(UUID.randomUUID().toString());
notificationRequest.setTransactionId("transaction_id");
notificationRequest.setState("mystate");

// The static DSL will be automatically configured for you
wiremock.stubFor(post(NOTIFY_ENDPOINT)
.withFormParam("scope", new EqualToPattern("email openid profile"))
.willReturn(notFound()));

var observer = new HttpAuthenticationDeviceNotifierProvider(webClient, config).notify(notificationRequest).test();
observer.await(10, TimeUnit.SECONDS);
observer.assertError(DeviceNotificationException.class);
observer.assertError(err -> err.getMessage().equals("Device notification fails"));
}
}

0 comments on commit 120b6ef

Please sign in to comment.