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

io.camunda.zeebe.gateway.interceptors.impl.IdentityInterceptor Maven / Gradle / Ivy

There is a newer version: 8.6.0-alpha5
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 Camunda License 1.0. You may not use this file
 * except in compliance with the Camunda License 1.0.
 */
package io.camunda.zeebe.gateway.interceptors.impl;

import io.camunda.identity.sdk.Identity;
import io.camunda.identity.sdk.IdentityConfiguration;
import io.camunda.identity.sdk.authentication.exception.TokenVerificationException;
import io.camunda.identity.sdk.tenants.dto.Tenant;
import io.camunda.zeebe.gateway.impl.configuration.IdentityCfg;
import io.camunda.zeebe.gateway.impl.configuration.MultiTenancyCfg;
import io.camunda.zeebe.gateway.interceptors.InterceptorUtil;
import io.grpc.Contexts;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.Status;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class IdentityInterceptor implements ServerInterceptor {

  private static final Logger LOGGER = LoggerFactory.getLogger(IdentityInterceptor.class);
  private static final Metadata.Key AUTH_KEY =
      Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER);
  private final Identity identity;
  private final MultiTenancyCfg multiTenancy;

  public IdentityInterceptor(final IdentityCfg config, final MultiTenancyCfg multiTenancy) {
    this(createIdentity(config), multiTenancy);
  }

  public IdentityInterceptor(
      final IdentityConfiguration configuration, final MultiTenancyCfg multiTenancy) {
    this(new Identity(configuration), multiTenancy);
  }

  public IdentityInterceptor(final Identity identity, final MultiTenancyCfg multiTenancy) {
    this.identity = identity;
    this.multiTenancy = multiTenancy;
  }

  private static Identity createIdentity(final IdentityCfg config) {
    return new Identity(
        new IdentityConfiguration.Builder()
            .withIssuerBackendUrl(config.getIssuerBackendUrl())
            .withAudience(config.getAudience())
            .withType(config.getType().name())
            .withBaseUrl(config.getBaseUrl())
            .build());
  }

  @Override
  public  ServerCall.Listener interceptCall(
      final ServerCall call,
      final Metadata headers,
      final ServerCallHandler next) {
    final var methodDescriptor = call.getMethodDescriptor();

    final var authorization = headers.get(AUTH_KEY);
    if (authorization == null) {
      LOGGER.debug(
          "Denying call {} as no token was provided", methodDescriptor.getFullMethodName());
      return deny(
          call,
          Status.UNAUTHENTICATED.augmentDescription(
              "Expected bearer token at header with key [%s], but found nothing"
                  .formatted(AUTH_KEY.name())));
    }

    final String token = authorization.replaceFirst("^Bearer ", "");
    try {
      identity.authentication().verifyToken(token);
    } catch (final TokenVerificationException e) {
      LOGGER.debug(
          "Denying call {} as the token could not be fully verified. Error message: {}",
          methodDescriptor.getFullMethodName(),
          e.getMessage());

      return deny(
          call,
          Status.UNAUTHENTICATED
              .augmentDescription("Failed to parse bearer token, see cause for details")
              .withCause(e));
    }

    if (!multiTenancy.isEnabled()) {
      return next.startCall(call, headers);
    }

    try {
      final List authorizedTenants =
          identity.tenants().forToken(token).stream().map(Tenant::getTenantId).toList();
      final var context = InterceptorUtil.setAuthorizedTenants(authorizedTenants);
      return Contexts.interceptCall(context, call, headers, next);

    } catch (final RuntimeException e) {
      LOGGER.debug(
          "Denying call {} as the authorized tenants could not be retrieved from Identity. Error message: {}",
          methodDescriptor.getFullMethodName(),
          e.getMessage());
      return deny(
          call,
          Status.UNAUTHENTICATED
              .augmentDescription(
                  "Expected Identity to provide authorized tenants, see cause for details")
              .withCause(e));
    }
  }

  private  ServerCall.Listener deny(
      final ServerCall call, final Status status) {
    call.close(status, new Metadata());
    return new ServerCall.Listener<>() {};
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy