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

io.camunda.zeebe.shared.security.ProblemAuthFailureHandler Maven / Gradle / Ivy

There is a newer version: 8.7.0-alpha1
Show newest version
/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
 * one or more contributor license agreements. See the NOTICE file distributed
 * with this work for additional information regarding copyright ownership.
 * Licensed under the Zeebe Community License 1.1. You may not use this file
 * except in compliance with the Zeebe Community License 1.1.
 */
package io.camunda.zeebe.shared.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ProblemDetail;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public final class ProblemAuthFailureHandler
    implements ServerAuthenticationFailureHandler,
        ServerAccessDeniedHandler,
        ServerAuthenticationEntryPoint {

  private final ObjectMapper objectMapper;

  @Autowired
  public ProblemAuthFailureHandler(final ObjectMapper objectMapper) {
    this.objectMapper = objectMapper;
  }

  @Override
  public Mono onAuthenticationFailure(
      final WebFilterExchange exchange, final AuthenticationException error) {
    return handleFailure(exchange.getExchange(), HttpStatus.UNAUTHORIZED, error);
  }

  @Override
  public Mono handle(final ServerWebExchange exchange, final AccessDeniedException error) {
    // if a token was passed but could not be validated, onAuthenticationFailure is called
    // however, if no token was passed, then access is denied here, and we want to distinguish
    // between unauthorized and forbidden; we can do that by checking the session principal to see
    // if it's authenticated or not
    return exchange
        .getPrincipal()
        .flatMap(
            principal -> {
              if (principal instanceof final Authentication auth && auth.isAuthenticated()) {
                return handleFailure(exchange, HttpStatus.FORBIDDEN, error);
              }

              return handleFailure(exchange, HttpStatus.UNAUTHORIZED, error);
            });
  }

  @Override
  public Mono commence(
      final ServerWebExchange exchange, final AuthenticationException error) {
    return handleFailure(exchange, HttpStatus.UNAUTHORIZED, error);
  }

  private Mono handleFailure(
      final ServerWebExchange exchange, final HttpStatus status, final Exception error) {
    final var request = exchange.getRequest();
    final var response = exchange.getResponse();
    final var problem = ProblemDetail.forStatus(status);
    problem.setInstance(request.getURI());
    problem.setDetail(error.getMessage());

    response.setStatusCode(status);
    response.getHeaders().setContentType(MediaType.APPLICATION_PROBLEM_JSON);

    return response.writeWith(
        Mono.fromCallable(() -> objectMapper.writeValueAsBytes(problem))
            .map(bytes -> response.bufferFactory().wrap(bytes)));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy