/*
 * 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.trino.server.security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.trino.spi.security.BasicPrincipal;
import io.trino.spi.security.Identity;

import javax.ws.rs.container.ContainerRequestContext;

import static com.google.common.base.Strings.nullToEmpty;
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
import static java.util.Objects.requireNonNull;

public abstract class AbstractBearerAuthenticator
        implements Authenticator
{
    private final String principalField;
    private final UserMapping userMapping;

    protected AbstractBearerAuthenticator(String principalField, UserMapping userMapping)
    {
        this.principalField = requireNonNull(principalField, "principalField is null");
        this.userMapping = requireNonNull(userMapping, "userMapping is null");
    }

    @Override
    public Identity authenticate(ContainerRequestContext request)
            throws AuthenticationException
    {
        String header = nullToEmpty(request.getHeaders().getFirst(AUTHORIZATION));

        int space = header.indexOf(' ');
        if ((space < 0) || !header.substring(0, space).equalsIgnoreCase("bearer")) {
            throw needAuthentication(request, null);
        }
        String token = header.substring(space + 1).trim();
        if (token.isEmpty()) {
            throw needAuthentication(request, null);
        }

        try {
            Jws<Claims> claimsJws = parseClaimsJws(token);
            String principal = claimsJws.getBody().get(principalField, String.class);
            if (principal == null) {
                throw needAuthentication(request, "Invalid credentials");
            }
            String authenticatedUser = userMapping.mapUser(principal);
            return Identity.forUser(authenticatedUser)
                    .withPrincipal(new BasicPrincipal(principal))
                    .build();
        }
        catch (JwtException | UserMappingException e) {
            throw needAuthentication(request, e.getMessage());
        }
        catch (RuntimeException e) {
            throw new RuntimeException("Authentication error", e);
        }
    }

    protected abstract Jws<Claims> parseClaimsJws(String jws);

    protected abstract AuthenticationException needAuthentication(ContainerRequestContext request, String message);
}

@@ -837,58 +837,24 @@
 ort 
-static com.google.common.base.Strings.nullToEmpty;
+java.util.List;%0A
 %0Aimp
@@ -913,16 +913,55 @@
 ZATION;%0A
+import static java.lang.String.format;%0A
 import s
@@ -1582,83 +1582,446 @@
     
+List%3C
 String
+%3E
  header
+s
  = 
-nullToEmpty(request.getHeaders().getFirst(AUTHORIZATION));%0A
+request.getHeaders().get(AUTHORIZATION);%0A        if (headers == null %7C%7C headers.size() == 0) %7B%0A            throw needAuthentication(request, null);%0A        %7D%0A        if (headers.size() %3E 1) %7B%0A            throw new IllegalArgumentException(format(%22Multiple %25s headers detected: %25s, where only single %25s header is supported%22, AUTHORIZATION, headers, AUTHORIZATION));%0A        %7D%0A%0A        String header = headers.get(0);
 %0A   

Check that only single Authorization header is send