All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.palantir.conjure.java.undertow.runtime.ConjureAuthorizationExtractor Maven / Gradle / Ivy

There is a newer version: 8.34.0
Show newest version
/*
 * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
 *
 * 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 com.palantir.conjure.java.undertow.runtime;

import com.palantir.conjure.java.api.errors.ErrorType;
import com.palantir.conjure.java.api.errors.ServiceException;
import com.palantir.conjure.java.undertow.lib.AuthorizationExtractor;
import com.palantir.conjure.java.undertow.lib.PlainSerDe;
import com.palantir.tokens.auth.AuthHeader;
import com.palantir.tokens.auth.BearerToken;
import com.palantir.tokens.auth.UnverifiedJsonWebToken;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.Cookie;
import io.undertow.util.HeaderValues;
import io.undertow.util.Headers;
import java.util.Optional;
import java.util.function.Consumer;
import org.slf4j.MDC;

/**
 * Implementation of {@link AuthorizationExtractor} which sets thread state based on a parsed
 * {@link UnverifiedJsonWebToken}. This behavior requires invocations to be wrapped with the
 * {@link LoggingContextHandler} to avoid leaking {@link MDC} state to other operations.
 *
 * 

Package private internal API. */ final class ConjureAuthorizationExtractor implements AuthorizationExtractor { private static final String USER_ID_KEY = "userId"; private static final String SESSION_ID_KEY = "sessionId"; private static final String TOKEN_ID_KEY = "tokenId"; private static final String ORGANIZATION_ID_KEY = "organizationId"; private static final Consumer sessionIdSetter = sessionId -> MDC.put(SESSION_ID_KEY, sessionId); private static final Consumer tokenIdSetter = tokenId -> MDC.put(TOKEN_ID_KEY, tokenId); private static final Consumer organizationIdSetter = organizationId -> MDC.put(ORGANIZATION_ID_KEY, organizationId); private static final ErrorType MISSING_CREDENTIAL_ERROR_TYPE = ErrorType.create(ErrorType.Code.UNAUTHORIZED, "Conjure:MissingCredentials"); private static final ErrorType MALFORMED_CREDENTIAL_ERROR_TYPE = ErrorType.create(ErrorType.Code.UNAUTHORIZED, "Conjure:MalformedCredentials"); private final PlainSerDe plainSerDe; private final JsonWebTokenHandler jsonWebTokenHandler; ConjureAuthorizationExtractor(PlainSerDe plainSerDe, JsonWebTokenHandler jsonWebTokenHandler) { this.plainSerDe = plainSerDe; this.jsonWebTokenHandler = jsonWebTokenHandler; } /** * Parses an {@link AuthHeader} from the provided {@link HttpServerExchange} and applies * {@link UnverifiedJsonWebToken} information to the {@link MDC thread state} if present. */ @Override public AuthHeader header(HttpServerExchange exchange) { AuthHeader authHeader = parseAuthHeader(exchange); return setState(exchange, authHeader); } /** * Parses a {@link BearerToken} from the provided {@link HttpServerExchange} and applies * {@link UnverifiedJsonWebToken} information to the {@link MDC thread state} if present. */ @Override public BearerToken cookie(HttpServerExchange exchange, String cookieName) { Cookie cookie = exchange.getRequestCookies().get(cookieName); if (cookie == null) { throw new ServiceException(MISSING_CREDENTIAL_ERROR_TYPE); } try { return setState(exchange, plainSerDe.deserializeBearerToken(cookie.getValue())); } catch (RuntimeException e) { throw new ServiceException(MALFORMED_CREDENTIAL_ERROR_TYPE, e); } } @Override public void setRequestToken(HttpServerExchange exchange, Optional parsedJwt) { exchange.putAttachment(Attachments.UNVERIFIED_JWT, parsedJwt); if (parsedJwt.isPresent()) { UnverifiedJsonWebToken jwt = parsedJwt.get(); MDC.put(USER_ID_KEY, jwt.getUnverifiedUserId()); jwt.getUnverifiedSessionId().ifPresent(sessionIdSetter); jwt.getUnverifiedTokenId().ifPresent(tokenIdSetter); jwt.getUnverifiedOrganizationId().ifPresent(organizationIdSetter); } // may throw jsonWebTokenHandler.handle(exchange, parsedJwt); } /** * Attempts to extract a {@link UnverifiedJsonWebToken JSON Web Token} from the {@link BearerToken} value, and * populates the SLF4J {@link MDC} with user id, session id, and token id extracted from the JWT. This is * best-effort and does not throw an exception in case any of these steps fail. */ private BearerToken setState(HttpServerExchange exchange, BearerToken token) { Optional parsedJwt = UnverifiedJsonWebToken.tryParse(token.getToken()); setRequestToken(exchange, parsedJwt); return token; } private AuthHeader setState(HttpServerExchange exchange, AuthHeader authHeader) { setState(exchange, authHeader.getBearerToken()); return authHeader; } private static AuthHeader parseAuthHeader(HttpServerExchange exchange) { HeaderValues authorization = exchange.getRequestHeaders().get(Headers.AUTHORIZATION); // Do not use Iterables.getOnlyElement because it includes values in the exception message. // We do not want credential material logged to disk, even if it's marked unsafe. if (authorization == null) { throw new ServiceException(MISSING_CREDENTIAL_ERROR_TYPE); } if (authorization.size() != 1) { throw new ServiceException(MALFORMED_CREDENTIAL_ERROR_TYPE); } try { return AuthHeader.valueOf(authorization.get(0)); } catch (RuntimeException e) { throw new ServiceException(MALFORMED_CREDENTIAL_ERROR_TYPE, e); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy