/*
 * 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.gravitino.server.web.filter;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.Collections;
import java.util.List;
import javax.ws.rs.core.Response;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.gravitino.Entity;
import org.apache.gravitino.EntityStore;
import org.apache.gravitino.GravitinoEnv;
import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.UserPrincipal;
import org.apache.gravitino.authorization.AuthorizationRequestContext;
import org.apache.gravitino.authorization.AuthorizationUtils;
import org.apache.gravitino.authorization.GravitinoAuthorizer;
import org.apache.gravitino.authorization.Privilege;
import org.apache.gravitino.dto.responses.ErrorResponse;
import org.apache.gravitino.exceptions.ForbiddenException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.listener.EventBus;
import org.apache.gravitino.listener.api.event.server.AuthorizationDenialFailureEvent;
import org.apache.gravitino.metalake.MetalakeManager;
import org.apache.gravitino.server.authorization.GravitinoAuthorizerProvider;
import org.apache.gravitino.server.authorization.annotations.AuthorizationExpression;
import org.apache.gravitino.server.authorization.annotations.AuthorizationMetadata;
import org.apache.gravitino.server.web.Utils;
import org.apache.gravitino.utils.PrincipalUtils;
import org.apache.gravitino.utils.RequestContext;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.MockedStatic;

/** Test for {@link GravitinoInterceptionService}. */
public class TestGravitinoInterceptionService {

  @AfterEach
  public void clearRequestContext() {
    RequestContext.resetOperationFailureFired();
    RequestContext.clear();
  }

  @Test
  public void testMetadataAuthorizationMethodInterceptor() throws Throwable {
    try (MockedStatic<PrincipalUtils> principalUtilsMocked = mockStatic(PrincipalUtils.class);
        MockedStatic<GravitinoAuthorizerProvider> mockStatic =
            mockStatic(GravitinoAuthorizerProvider.class);
        MockedStatic<GravitinoEnv> envMocked = mockStatic(GravitinoEnv.class);
        MockedStatic<MetalakeManager> metalakeManagerMocked = mockStatic(MetalakeManager.class)) {
      principalUtilsMocked
          .when(PrincipalUtils::getCurrentPrincipal)
          .thenReturn(new UserPrincipal("tester"));
      principalUtilsMocked.when(PrincipalUtils::getCurrentUserName).thenReturn("tester");
      MethodInvocation methodInvocation = mock(MethodInvocation.class);
      GravitinoAuthorizerProvider mockedProvider = mock(GravitinoAuthorizerProvider.class);
      mockStatic.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
      when(mockedProvider.getGravitinoAuthorizer()).thenReturn(new MockGravitinoAuthorizer());

      // Mock GravitinoEnv and EntityStore
      GravitinoEnv mockEnv = mock(GravitinoEnv.class);
      EntityStore mockStore = mock(EntityStore.class);
      envMocked.when(GravitinoEnv::getInstance).thenReturn(mockEnv);
      when(mockEnv.entityStore()).thenReturn(mockStore);

      // Mock MetalakeManager.checkMetalake to do nothing (metalake exists)
      metalakeManagerMocked
          .when(() -> MetalakeManager.checkMetalake(ArgumentMatchers.any(), ArgumentMatchers.any()))
          .thenAnswer(invocation -> null);

      GravitinoInterceptionService gravitinoInterceptionService =
          new GravitinoInterceptionService();
      Class<TestOperations> testOperationsClass = TestOperations.class;
      Method[] methods = testOperationsClass.getMethods();
      Method testMethod = methods[0];
      List<MethodInterceptor> methodInterceptors =
          gravitinoInterceptionService.getMethodInterceptors(testMethod);
      MethodInterceptor methodInterceptor = methodInterceptors.get(0);
      assertEquals(1, methodInterceptors.size());
      when(methodInvocation.proceed()).thenReturn(new TestOperations().testMethod("testMetalake"));
      when(methodInvocation.getMethod()).thenReturn(testMethod);
      when(methodInvocation.getArguments()).thenReturn(new Object[] {"testMetalake"});
      Response response = (Response) methodInterceptor.invoke(methodInvocation);
      assertEquals("ok", response.getEntity());
      when(methodInvocation.getMethod()).thenReturn(testMethod);
      when(methodInvocation.getArguments()).thenReturn(new Object[] {"testMetalake2"});
      Response response2 = (Response) methodInterceptor.invoke(methodInvocation);
      assertEquals(
          "User 'tester' is not authorized to perform operation 'testMethod' on metadata 'testMetalake2'",
          ((ErrorResponse) response2.getEntity()).getMessage());
    }
  }

  @Test
  public void testSystemInternalErrorHandling() throws Throwable {
    try (MockedStatic<PrincipalUtils> principalUtilsMocked = mockStatic(PrincipalUtils.class);
        MockedStatic<GravitinoAuthorizerProvider> mockStatic =
            mockStatic(GravitinoAuthorizerProvider.class)) {
      principalUtilsMocked
          .when(PrincipalUtils::getCurrentPrincipal)
          .thenReturn(new UserPrincipal("tester"));
      principalUtilsMocked.when(PrincipalUtils::getCurrentUserName).thenReturn("tester");

      MethodInvocation methodInvocation = mock(MethodInvocation.class);
      GravitinoAuthorizerProvider mockedProvider = mock(GravitinoAuthorizerProvider.class);
      mockStatic.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);

      // Mock an exception during authorization
      when(mockedProvider.getGravitinoAuthorizer())
          .thenThrow(new RuntimeException("Database connection failed"));

      GravitinoInterceptionService gravitinoInterceptionService =
          new GravitinoInterceptionService();
      Class<TestOperations> testOperationsClass = TestOperations.class;
      Method[] methods = testOperationsClass.getMethods();
      Method testMethod = methods[0];
      List<MethodInterceptor> methodInterceptors =
          gravitinoInterceptionService.getMethodInterceptors(testMethod);
      MethodInterceptor methodInterceptor = methodInterceptors.get(0);

      // Test system internal error
      when(methodInvocation.getMethod()).thenReturn(testMethod);
      when(methodInvocation.getArguments()).thenReturn(new Object[] {"testMetalake"});
      Response response = (Response) methodInterceptor.invoke(methodInvocation);

      // Verify the system internal error message
      ErrorResponse errorResponse = (ErrorResponse) response.getEntity();
      assertEquals(
          "Authorization failed due to system internal error. Please contact administrator.",
          errorResponse.getMessage());

      // Verify correct HTTP status
      assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
    }
  }

  @Test
  public void testMetalakeNotExist() throws Throwable {
    try (MockedStatic<PrincipalUtils> principalUtilsMocked = mockStatic(PrincipalUtils.class);
        MockedStatic<GravitinoAuthorizerProvider> authorizerMocked =
            mockStatic(GravitinoAuthorizerProvider.class);
        MockedStatic<AuthorizationUtils> authorizationUtilsMocked =
            mockStatic(AuthorizationUtils.class)) {

      principalUtilsMocked
          .when(PrincipalUtils::getCurrentPrincipal)
          .thenReturn(new UserPrincipal("tester"));
      principalUtilsMocked.when(PrincipalUtils::getCurrentUserName).thenReturn("tester");

      MethodInvocation methodInvocation = mock(MethodInvocation.class);
      GravitinoAuthorizerProvider mockedProvider = mock(GravitinoAuthorizerProvider.class);
      authorizerMocked.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
      when(mockedProvider.getGravitinoAuthorizer()).thenReturn(new MockGravitinoAuthorizer());

      // Mock AuthorizationUtils.checkCurrentUser to throw NoSuchMetalakeException
      authorizationUtilsMocked
          .when(
              () ->
                  AuthorizationUtils.checkCurrentUser(
                      ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any()))
          .thenThrow(new NoSuchMetalakeException("Metalake nonExistentMetalake does not exist"));

      GravitinoInterceptionService gravitinoInterceptionService =
          new GravitinoInterceptionService();
      Class<TestOperations> testOperationsClass = TestOperations.class;
      Method[] methods = testOperationsClass.getMethods();
      Method testMethod = methods[0];
      List<MethodInterceptor> methodInterceptors =
          gravitinoInterceptionService.getMethodInterceptors(testMethod);
      MethodInterceptor methodInterceptor = methodInterceptors.get(0);

      // Test with non-existent metalake
      when(methodInvocation.getMethod()).thenReturn(testMethod);
      when(methodInvocation.getArguments()).thenReturn(new Object[] {"nonExistentMetalake"});
      Response response = (Response) methodInterceptor.invoke(methodInvocation);

      // Verify that a 403 Forbidden response is returned
      assertEquals(Response.Status.FORBIDDEN.getStatusCode(), response.getStatus());
      ErrorResponse errorResponse = (ErrorResponse) response.getEntity();
      assertEquals(
          "User 'tester' is not authorized to perform operation 'testMethod' on "
              + "metadata 'nonExistentMetalake'",
          errorResponse.getMessage());
    }
  }

  @Test
  public void testEmptyExpressionSkipsAuthorization() throws Throwable {
    try (MockedStatic<PrincipalUtils> principalUtilsMocked = mockStatic(PrincipalUtils.class);
        MockedStatic<GravitinoAuthorizerProvider> authorizerMocked =
            mockStatic(GravitinoAuthorizerProvider.class);
        MockedStatic<org.apache.gravitino.GravitinoEnv> envMocked =
            mockStatic(org.apache.gravitino.GravitinoEnv.class);
        MockedStatic<org.apache.gravitino.metalake.MetalakeManager> metalakeManagerMocked =
            mockStatic(org.apache.gravitino.metalake.MetalakeManager.class)) {

      principalUtilsMocked
          .when(PrincipalUtils::getCurrentPrincipal)
          .thenReturn(new UserPrincipal("tester"));
      principalUtilsMocked.when(PrincipalUtils::getCurrentUserName).thenReturn("tester");

      GravitinoAuthorizerProvider mockedProvider = mock(GravitinoAuthorizerProvider.class);
      authorizerMocked.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
      when(mockedProvider.getGravitinoAuthorizer()).thenReturn(new MockGravitinoAuthorizer());

      GravitinoEnv mockEnv = mock(GravitinoEnv.class);
      EntityStore mockStore = mock(EntityStore.class);
      envMocked.when(GravitinoEnv::getInstance).thenReturn(mockEnv);
      when(mockEnv.entityStore()).thenReturn(mockStore);

      // Mock MetalakeManager.checkMetalake to do nothing (metalake exists)
      metalakeManagerMocked
          .when(() -> MetalakeManager.checkMetalake(ArgumentMatchers.any(), ArgumentMatchers.any()))
          .thenAnswer(invocation -> null);

      GravitinoInterceptionService gravitinoInterceptionService =
          new GravitinoInterceptionService();
      Class<TestOperationsWithEmptyExpression> testOperationsClass =
          TestOperationsWithEmptyExpression.class;
      Method[] methods = testOperationsClass.getMethods();
      Method testMethod = methods[0];
      List<MethodInterceptor> methodInterceptors =
          gravitinoInterceptionService.getMethodInterceptors(testMethod);
      MethodInterceptor methodInterceptor = methodInterceptors.get(0);

      MethodInvocation methodInvocation = mock(MethodInvocation.class);
      when(methodInvocation.getMethod()).thenReturn(testMethod);
      when(methodInvocation.getArguments()).thenReturn(new Object[] {"testMetalake"});
      when(methodInvocation.proceed()).thenReturn(Utils.ok("success"));

      // Test with empty expression - should skip authorization and proceed
      Response response = (Response) methodInterceptor.invoke(methodInvocation);

      // Verify that the method was allowed to proceed without authorization check
      assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
      assertEquals("success", response.getEntity());
    }
  }

  /**
   * When the authorization executor returns {@code false}, {@code buildNoAuthResponse} is called
   * with {@code emitEvent=true}. Verify that:
   *
   * <ul>
   *   <li>An {@link AuthorizationDenialFailureEvent} is dispatched via the EventBus.
   *   <li>{@code RequestContext.operationFailureFired} is set so {@code HttpAuditFilter} skips
   *       emitting a duplicate failure event for the same request.
   * </ul>
   */
  @Test
  public void testExecutorDenialDispatchesEventAndSetsFlag() throws Throwable {
    try (MockedStatic<PrincipalUtils> principalUtilsMocked = mockStatic(PrincipalUtils.class);
        MockedStatic<GravitinoAuthorizerProvider> authorizerMocked =
            mockStatic(GravitinoAuthorizerProvider.class);
        MockedStatic<GravitinoEnv> envMocked = mockStatic(GravitinoEnv.class);
        MockedStatic<MetalakeManager> metalakeManagerMocked = mockStatic(MetalakeManager.class)) {

      principalUtilsMocked
          .when(PrincipalUtils::getCurrentPrincipal)
          .thenReturn(new UserPrincipal("tester"));
      principalUtilsMocked.when(PrincipalUtils::getCurrentUserName).thenReturn("tester");

      GravitinoAuthorizerProvider mockedProvider = mock(GravitinoAuthorizerProvider.class);
      authorizerMocked.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
      when(mockedProvider.getGravitinoAuthorizer()).thenReturn(new MockGravitinoAuthorizer());

      GravitinoEnv mockEnv = mock(GravitinoEnv.class);
      EntityStore mockStore = mock(EntityStore.class);
      EventBus mockEventBus = spy(new EventBus(Collections.emptyList()));
      envMocked.when(GravitinoEnv::getInstance).thenReturn(mockEnv);
      when(mockEnv.entityStore()).thenReturn(mockStore);
      when(mockEnv.eventBus()).thenReturn(mockEventBus);

      metalakeManagerMocked
          .when(() -> MetalakeManager.checkMetalake(ArgumentMatchers.any(), ArgumentMatchers.any()))
          .thenAnswer(invocation -> null);

      GravitinoInterceptionService service = new GravitinoInterceptionService();
      Method testMethod = TestOperations.class.getMethods()[0];
      MethodInterceptor interceptor = service.getMethodInterceptors(testMethod).get(0);

      MethodInvocation invocation = mock(MethodInvocation.class);
      when(invocation.getMethod()).thenReturn(testMethod);
      // testMetalake2 triggers denial in MockGravitinoAuthorizer
      when(invocation.getArguments()).thenReturn(new Object[] {"testMetalake2"});

      Assertions.assertFalse(RequestContext.isOperationFailureFired());
      Response response = (Response) interceptor.invoke(invocation);

      assertEquals(Response.Status.FORBIDDEN.getStatusCode(), response.getStatus());
      // Event dispatched
      ArgumentCaptor<AuthorizationDenialFailureEvent> captor =
          ArgumentCaptor.forClass(AuthorizationDenialFailureEvent.class);
      verify(mockEventBus).dispatchEvent(captor.capture());
      AuthorizationDenialFailureEvent event = captor.getValue();
      assertEquals("tester", event.user());
      assertEquals("testMethod", event.methodName());
      // Flag set so HttpAuditFilter skips duplicate
      Assertions.assertTrue(RequestContext.isOperationFailureFired());
    }
  }

  /**
   * When {@code checkCurrentUser} throws {@link ForbiddenException} (user is not a metalake
   * member), the interceptor must dispatch an {@link AuthorizationDenialFailureEvent} and set
   * {@code operationFailureFired}.
   */
  @Test
  public void testForbiddenExceptionDispatchesEventAndSetsFlag() throws Throwable {
    try (MockedStatic<PrincipalUtils> principalUtilsMocked = mockStatic(PrincipalUtils.class);
        MockedStatic<GravitinoAuthorizerProvider> authorizerMocked =
            mockStatic(GravitinoAuthorizerProvider.class);
        MockedStatic<AuthorizationUtils> authUtilsMocked = mockStatic(AuthorizationUtils.class);
        MockedStatic<GravitinoEnv> envMocked = mockStatic(GravitinoEnv.class)) {

      principalUtilsMocked
          .when(PrincipalUtils::getCurrentPrincipal)
          .thenReturn(new UserPrincipal("outsider"));
      principalUtilsMocked.when(PrincipalUtils::getCurrentUserName).thenReturn("outsider");

      GravitinoAuthorizerProvider mockedProvider = mock(GravitinoAuthorizerProvider.class);
      authorizerMocked.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
      when(mockedProvider.getGravitinoAuthorizer()).thenReturn(new MockGravitinoAuthorizer());

      authUtilsMocked
          .when(
              () ->
                  AuthorizationUtils.checkCurrentUser(
                      ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any()))
          .thenThrow(new ForbiddenException("User outsider is not a member"));

      GravitinoEnv mockEnv = mock(GravitinoEnv.class);
      EventBus mockEventBus = spy(new EventBus(Collections.emptyList()));
      envMocked.when(GravitinoEnv::getInstance).thenReturn(mockEnv);
      when(mockEnv.eventBus()).thenReturn(mockEventBus);

      GravitinoInterceptionService service = new GravitinoInterceptionService();
      Method testMethod = TestOperations.class.getMethods()[0];
      MethodInterceptor interceptor = service.getMethodInterceptors(testMethod).get(0);

      MethodInvocation invocation = mock(MethodInvocation.class);
      when(invocation.getMethod()).thenReturn(testMethod);
      when(invocation.getArguments()).thenReturn(new Object[] {"testMetalake"});

      Assertions.assertFalse(RequestContext.isOperationFailureFired());
      Response response = (Response) interceptor.invoke(invocation);

      assertEquals(Response.Status.FORBIDDEN.getStatusCode(), response.getStatus());
      ArgumentCaptor<AuthorizationDenialFailureEvent> captor =
          ArgumentCaptor.forClass(AuthorizationDenialFailureEvent.class);
      verify(mockEventBus).dispatchEvent(captor.capture());
      AuthorizationDenialFailureEvent event = captor.getValue();
      assertEquals("outsider", event.user());
      Assertions.assertTrue(RequestContext.isOperationFailureFired());
    }
  }

  /**
   * When {@code checkCurrentUser} throws {@link NoSuchMetalakeException}, the 403 is a
   * resource-not-found masquerading as forbidden — no {@link AuthorizationDenialFailureEvent} is
   * dispatched, leaving {@code operationFailureFired} false so that {@code HttpAuditFilter} can
   * emit a generic {@code HttpRequestFailureEvent}.
   */
  @Test
  public void testNoSuchMetalakeExceptionDoesNotDispatchEventAndFlagStaysFalse() throws Throwable {
    try (MockedStatic<PrincipalUtils> principalUtilsMocked = mockStatic(PrincipalUtils.class);
        MockedStatic<GravitinoAuthorizerProvider> authorizerMocked =
            mockStatic(GravitinoAuthorizerProvider.class);
        MockedStatic<AuthorizationUtils> authUtilsMocked = mockStatic(AuthorizationUtils.class);
        MockedStatic<GravitinoEnv> envMocked = mockStatic(GravitinoEnv.class)) {

      principalUtilsMocked
          .when(PrincipalUtils::getCurrentPrincipal)
          .thenReturn(new UserPrincipal("tester"));
      principalUtilsMocked.when(PrincipalUtils::getCurrentUserName).thenReturn("tester");

      GravitinoAuthorizerProvider mockedProvider = mock(GravitinoAuthorizerProvider.class);
      authorizerMocked.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
      when(mockedProvider.getGravitinoAuthorizer()).thenReturn(new MockGravitinoAuthorizer());

      authUtilsMocked
          .when(
              () ->
                  AuthorizationUtils.checkCurrentUser(
                      ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any()))
          .thenThrow(new NoSuchMetalakeException("Metalake gone does not exist"));

      GravitinoEnv mockEnv = mock(GravitinoEnv.class);
      EventBus mockEventBus = mock(EventBus.class);
      envMocked.when(GravitinoEnv::getInstance).thenReturn(mockEnv);
      when(mockEnv.eventBus()).thenReturn(mockEventBus);

      GravitinoInterceptionService service = new GravitinoInterceptionService();
      Method testMethod = TestOperations.class.getMethods()[0];
      MethodInterceptor interceptor = service.getMethodInterceptors(testMethod).get(0);

      MethodInvocation invocation = mock(MethodInvocation.class);
      when(invocation.getMethod()).thenReturn(testMethod);
      when(invocation.getArguments()).thenReturn(new Object[] {"gone"});

      Response response = (Response) interceptor.invoke(invocation);

      assertEquals(Response.Status.FORBIDDEN.getStatusCode(), response.getStatus());
      // No AuthorizationDenialFailureEvent dispatched — HttpAuditFilter handles this 403
      verify(mockEventBus, never()).dispatchEvent(ArgumentMatchers.any());
      Assertions.assertFalse(
          RequestContext.isOperationFailureFired(),
          "operationFailureFired must stay false so HttpAuditFilter emits the HTTP-level event");
    }
  }

  public static class TestOperations {

    @AuthorizationExpression(
        expression = "METALAKE::USE_CATALOG || METALAKE::OWNER",
        accessMetadataType = MetadataObject.Type.METALAKE)
    public Response testMethod(
        @AuthorizationMetadata(type = Entity.EntityType.METALAKE) String metalake) {
      return Utils.ok("ok");
    }
  }

  public static class TestOperationsWithEmptyExpression {

    @AuthorizationExpression(expression = "", accessMetadataType = MetadataObject.Type.METALAKE)
    public Response testMethodWithEmptyExpression(
        @AuthorizationMetadata(type = Entity.EntityType.METALAKE) String metalake) {
      return Utils.ok("success");
    }
  }

  private static class MockGravitinoAuthorizer implements GravitinoAuthorizer {

    @Override
    public void initialize() {}

    @Override
    public boolean authorize(
        Principal principal,
        String metalake,
        MetadataObject metadataObject,
        Privilege.Name privilege,
        AuthorizationRequestContext requestContext) {
      return "tester".equals(principal.getName())
          && "testMetalake".equals(metalake)
          && metadataObject.type() == MetadataObject.Type.METALAKE
          && privilege == Privilege.Name.USE_CATALOG;
    }

    @Override
    public boolean deny(
        Principal principal,
        String metalake,
        MetadataObject metadataObject,
        Privilege.Name privilege,
        AuthorizationRequestContext requestContext) {
      return false;
    }

    @Override
    public boolean isOwner(
        Principal principal,
        String metalake,
        MetadataObject metadataObject,
        AuthorizationRequestContext requestContext) {
      return false;
    }

    @Override
    public boolean isServiceAdmin() {
      return false;
    }

    @Override
    public boolean isSelf(
        Entity.EntityType type,
        NameIdentifier nameIdentifier,
        AuthorizationRequestContext requestContext) {
      return true;
    }

    @Override
    public boolean isMetalakeUser(String metalake, AuthorizationRequestContext requestContext) {
      return true;
    }

    @Override
    public boolean hasSetOwnerPermission(
        String metalake, String type, String fullName, AuthorizationRequestContext requestContext) {
      return true;
    }

    @Override
    public boolean hasMetadataPrivilegePermission(
        String metalake, String type, String fullName, AuthorizationRequestContext requestContext) {
      return true;
    }

    @Override
    public void handleRolePrivilegeChange(Long roleId) {}

    @Override
    public void handleMetadataOwnerChange(
        String metalake, Long oldOwnerId, NameIdentifier nameIdentifier, Entity.EntityType type) {}

    @Override
    public void close() throws IOException {}
  }
}
