OriginBoundAuthenticationSelectorTest.java

/*
 * 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.maven.internal.aether;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.eclipse.aether.repository.AuthenticationSelector;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;

/**
 * UT for {@link OriginBoundAuthenticationSelector}.
 */
class OriginBoundAuthenticationSelectorTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(OriginBoundAuthenticationSelectorTest.class);

    private static AuthenticationSelector serverCredentials(String... ids) {
        DefaultAuthenticationSelector selector = new DefaultAuthenticationSelector();
        for (String id : ids) {
            selector.add(
                    id,
                    new AuthenticationBuilder()
                            .addUsername("user")
                            .addPassword("pass")
                            .build());
        }
        return selector;
    }

    private static Map<String, Set<String>> declared(String id, String url) {
        Map<String, Set<String>> origins = new HashMap<>();
        OriginBoundAuthenticationSelector.addOrigin(origins, id, url);
        return origins;
    }

    private static RemoteRepository repo(String id, String url) {
        return new RemoteRepository.Builder(id, "default", url).build();
    }

    @Test
    void credentialsServedForDeclaredOrigin() {
        AuthenticationSelector selector = OriginBoundAuthenticationSelector.wrap(
                serverCredentials("releases"),
                OriginBoundAuthenticationSelector.SCOPE_ORIGIN,
                declared("releases", "https://repo.example.org/releases/"),
                LOGGER);

        assertNotNull(selector.getAuthentication(repo("releases", "https://repo.example.org/releases/")));
    }

    @Test
    void authenticationScopedToDeclaredOrigin() {
        // credentials are scoped to the declared origin, so a different-origin
        // repository with the same id is not served
        AuthenticationSelector selector = OriginBoundAuthenticationSelector.wrap(
                serverCredentials("releases"),
                OriginBoundAuthenticationSelector.SCOPE_ORIGIN,
                declared("releases", "https://repo.example.org/releases/"),
                LOGGER);

        assertNull(selector.getAuthentication(repo("releases", "https://other.example.org/m2/")));
        // an unparseable URL on a bound id fails closed as well
        assertNull(selector.getAuthentication(repo("releases", "notaurl")));
    }

    @Test
    void undeclaredIdKeepsLegacyBehaviorInOriginScope() {
        // e.g. a pure deployment server whose URL only exists in the project's distributionManagement
        AuthenticationSelector selector = OriginBoundAuthenticationSelector.wrap(
                serverCredentials("deploy-server"),
                OriginBoundAuthenticationSelector.SCOPE_ORIGIN,
                new HashMap<>(),
                LOGGER);

        assertNotNull(selector.getAuthentication(repo("deploy-server", "https://deploy.example.org/releases/")));
    }

    @Test
    void undeclaredIdRefusedInStrictScope() {
        AuthenticationSelector selector = OriginBoundAuthenticationSelector.wrap(
                serverCredentials("deploy-server"),
                OriginBoundAuthenticationSelector.SCOPE_STRICT,
                new HashMap<>(),
                LOGGER);

        assertNull(selector.getAuthentication(repo("deploy-server", "https://deploy.example.org/releases/")));
    }

    @Test
    void idScopeReturnsUnwrappedDelegate() {
        AuthenticationSelector delegate = serverCredentials("releases");
        AuthenticationSelector selector = OriginBoundAuthenticationSelector.wrap(
                delegate,
                OriginBoundAuthenticationSelector.SCOPE_ID,
                declared("releases", "https://repo.example.org/releases/"),
                LOGGER);

        assertSame(delegate, selector);
        assertNotNull(selector.getAuthentication(repo("releases", "https://other.example.org/m2/")));
    }

    @Test
    void unknownScopeIsRejected() {
        assertThrows(
                IllegalArgumentException.class,
                () -> OriginBoundAuthenticationSelector.wrap(serverCredentials(), "bogus", new HashMap<>(), LOGGER));
    }

    @Test
    void originsAreNormalized() {
        assertEquals(
                OriginBoundAuthenticationSelector.originOf("https://repo.example.org/releases/"),
                OriginBoundAuthenticationSelector.originOf("HTTPS://Repo.Example.Org:443/other/path"));
        assertEquals(
                OriginBoundAuthenticationSelector.originOf("http://repo.example.org:80/"),
                OriginBoundAuthenticationSelector.originOf("http://repo.example.org/releases/"));
        assertNull(OriginBoundAuthenticationSelector.originOf("file:/tmp/repo"));
        assertNull(OriginBoundAuthenticationSelector.originOf(null));
    }
}