Commit 271a2230 authored by Sonia Zorba's avatar Sonia Zorba
Browse files

GMS client lib: fixed URL encoding issue in invited registration call

parent ae69e025
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -22,7 +22,8 @@ public class AddInvitedRegistrationCall extends BaseGmsCall {

        String endpoint = "invited-registration";

        String bodyParams = "token_hash=" + tokenHash
        // plus symbol in token hash is encoded to %2B, otherwise it will be interpreted as space
        String bodyParams = "token_hash=" + tokenHash.replace("+", "%2B")
                + "&email=" + email + "&groups="
                + String.join("\n", groupsPermissions.entrySet()
                        .stream().map(e -> e.getKey() + " " + e.getValue())
+75 −0
Original line number Diff line number Diff line
@@ -7,14 +7,23 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublisher;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodySubscriber;
import java.net.http.HttpResponse.BodySubscribers;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Flow;
import java.util.concurrent.Flow.Subscriber;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentMatcher;
import org.mockito.ArgumentMatchers;
import static org.mockito.ArgumentMatchers.any;
@@ -145,6 +154,72 @@ public class GmsClientTest {
        verify(httpClient, times(1)).sendAsync(endpointEq("DELETE", "permission/LBT.INAF?user_id=user"), any());
    }

    @Test
    public void testInvitedRegistration() {

        CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(201));

        when(httpClient.sendAsync(any(), any())).thenReturn(response);
        Map<String, Permission> permissionsMap = new HashMap<>();
        permissionsMap.put("group1", Permission.MANAGE_MEMBERS);
        permissionsMap.put("group2", Permission.MANAGE_MEMBERS);
        client.addInvitedRegistration("bvjsgqu423", "email", permissionsMap);
        // hash = AOyojiwaRR7BHPde6Tomg3+BMoQQggNM3wUHEarXuNQ=

        verify(httpClient, times(1)).sendAsync(
                AdditionalMatchers.and(
                        endpointEq("POST", "invited-registration"),
                        ArgumentMatchers.argThat(req -> {
                            String reqbody = req.bodyPublisher().map(p -> {
                                var bodySubscriber = BodySubscribers.ofString(StandardCharsets.UTF_8);
                                var flowSubscriber = new StringSubscriber(bodySubscriber);
                                p.subscribe(flowSubscriber);
                                return bodySubscriber.getBody().toCompletableFuture().join();
                            }).get();

                            // If the resulting hash contains a + symbol it has to be encoded to %2B,
                            // otherwise it will be interpreted as a space and wrong value will be
                            // stored into the database
                            String expectedBody = "token_hash=AOyojiwaRR7BHPde6Tomg3%2BBMoQQggNM3wUHEarXuNQ="
                                    + "&email=email&groups=group2 MANAGE_MEMBERS\n"
                                    + "group1 MANAGE_MEMBERS";

                            return reqbody.equals(expectedBody);
                        })), any());
    }

    /**
     * Credit: https://stackoverflow.com/a/55816685/771431
     */
    static final class StringSubscriber implements Flow.Subscriber<ByteBuffer> {

        final BodySubscriber<String> wrapped;

        StringSubscriber(BodySubscriber<String> wrapped) {
            this.wrapped = wrapped;
        }

        @Override
        public void onSubscribe(Flow.Subscription subscription) {
            wrapped.onSubscribe(subscription);
        }

        @Override
        public void onNext(ByteBuffer item) {
            wrapped.onNext(List.of(item));
        }

        @Override
        public void onError(Throwable throwable) {
            wrapped.onError(throwable);
        }

        @Override
        public void onComplete() {
            wrapped.onComplete();
        }
    }

    private HttpResponse getMockedResponse(int statusCode, String body) {
        HttpResponse response = getMockedResponse(statusCode);
        InputStream in = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));