Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
jerqi committed Oct 11, 2024
1 parent 630cf67 commit 91c5869
Show file tree
Hide file tree
Showing 14 changed files with 389 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.exceptions;

import com.google.errorprone.annotations.FormatMethod;
import com.google.errorprone.annotations.FormatString;

/** Exception thrown when a user is forbidden to perform an action. */
public class ForbiddenException extends GravitinoRuntimeException {
/**
* Constructs a new exception with the specified detail message.
*
* @param message the detail message.
* @param args the arguments to the message.
*/
@FormatMethod
public ForbiddenException(@FormatString String message, Object... args) {
super(message, args);
}

/**
* Constructs a new exception with the specified detail message and cause.
*
* @param cause the cause.
* @param message the detail message.
* @param args the arguments to the message.
*/
@FormatMethod
public ForbiddenException(Throwable cause, @FormatString String message, Object... args) {
super(cause, message, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.gravitino.exceptions.CatalogAlreadyExistsException;
import org.apache.gravitino.exceptions.ConnectionFailedException;
import org.apache.gravitino.exceptions.FilesetAlreadyExistsException;
import org.apache.gravitino.exceptions.ForbiddenException;
import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
import org.apache.gravitino.exceptions.IllegalPrivilegeException;
import org.apache.gravitino.exceptions.MetalakeAlreadyExistsException;
Expand Down Expand Up @@ -303,9 +304,13 @@ public void accept(ErrorResponse errorResponse) {

case ErrorConstants.INTERNAL_ERROR_CODE:
throw new RuntimeException(errorMessage);

case ErrorConstants.UNSUPPORTED_OPERATION_CODE:
throw new UnsupportedOperationException(errorMessage);

case ErrorConstants.FORBIDDEN_CODE:
throw new ForbiddenException(errorMessage);

default:
super.accept(errorResponse);
}
Expand Down Expand Up @@ -343,6 +348,9 @@ public void accept(ErrorResponse errorResponse) {
case ErrorConstants.UNSUPPORTED_OPERATION_CODE:
throw new UnsupportedOperationException(errorMessage);

case ErrorConstants.FORBIDDEN_CODE:
throw new ForbiddenException(errorMessage);

case ErrorConstants.INTERNAL_ERROR_CODE:
throw new RuntimeException(errorMessage);

Expand Down Expand Up @@ -380,6 +388,9 @@ public void accept(ErrorResponse errorResponse) {
case ErrorConstants.ALREADY_EXISTS_CODE:
throw new CatalogAlreadyExistsException(errorMessage);

case ErrorConstants.FORBIDDEN_CODE:
throw new ForbiddenException(errorMessage);

case ErrorConstants.INTERNAL_ERROR_CODE:
throw new RuntimeException(errorMessage);

Expand Down Expand Up @@ -495,6 +506,9 @@ public void accept(ErrorResponse errorResponse) {
case ErrorConstants.ALREADY_EXISTS_CODE:
throw new FilesetAlreadyExistsException(errorMessage);

case ErrorConstants.FORBIDDEN_CODE:
throw new ForbiddenException(errorMessage);

case ErrorConstants.INTERNAL_ERROR_CODE:
throw new RuntimeException(errorMessage);

Expand Down Expand Up @@ -530,6 +544,9 @@ public void accept(ErrorResponse errorResponse) {
case ErrorConstants.ALREADY_EXISTS_CODE:
throw new TopicAlreadyExistsException(errorMessage);

case ErrorConstants.FORBIDDEN_CODE:
throw new ForbiddenException(errorMessage);

case ErrorConstants.INTERNAL_ERROR_CODE:
throw new RuntimeException(errorMessage);

Expand Down Expand Up @@ -652,6 +669,9 @@ public void accept(ErrorResponse errorResponse) {
case ErrorConstants.UNSUPPORTED_OPERATION_CODE:
throw new UnsupportedOperationException(errorMessage);

case ErrorConstants.FORBIDDEN_CODE:
throw new ForbiddenException(errorMessage);

case ErrorConstants.INTERNAL_ERROR_CODE:
throw new RuntimeException(errorMessage);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
/*
* 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.client.integration.test.authorization;

import static org.apache.gravitino.server.GravitinoServer.WEBSERVER_CONF_PREFIX;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.Collections;
import java.util.Map;
import org.apache.gravitino.Catalog;
import org.apache.gravitino.Configs;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.auth.AuthConstants;
import org.apache.gravitino.authorization.Privileges;
import org.apache.gravitino.authorization.SecurableObject;
import org.apache.gravitino.authorization.SecurableObjects;
import org.apache.gravitino.client.GravitinoAdminClient;
import org.apache.gravitino.client.GravitinoMetalake;
import org.apache.gravitino.exceptions.ForbiddenException;
import org.apache.gravitino.file.Fileset;
import org.apache.gravitino.integration.test.container.ContainerSuite;
import org.apache.gravitino.integration.test.container.HiveContainer;
import org.apache.gravitino.integration.test.container.KafkaContainer;
import org.apache.gravitino.integration.test.util.AbstractIT;
import org.apache.gravitino.rel.Column;
import org.apache.gravitino.rel.types.Types;
import org.apache.gravitino.server.web.JettyServerConfig;
import org.apache.gravitino.utils.RandomNameUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Tag("gravitino-docker-test")
public class CheckCurrentUserIT extends AbstractIT {

private static final Logger LOG = LoggerFactory.getLogger(CheckCurrentUserIT.class);
private static final ContainerSuite containerSuite = ContainerSuite.getInstance();
private static String hmsUri;
private static String kafkaBootstrapServers;
private static GravitinoMetalake metalake;
private static GravitinoMetalake anotherMetalake;
private static String metalakeName = RandomNameUtils.genRandomName("metalake");

@BeforeAll
public static void startIntegrationTest() throws Exception {
Map<String, String> configs = Maps.newHashMap();
configs.put(Configs.ENABLE_AUTHORIZATION.getKey(), String.valueOf(true));
configs.put(Configs.SERVICE_ADMINS.getKey(), AuthConstants.ANONYMOUS_USER);
registerCustomConfigs(configs);
AbstractIT.startIntegrationTest();

containerSuite.startHiveContainer();
hmsUri =
String.format(
"thrift://%s:%d",
containerSuite.getHiveContainer().getContainerIpAddress(),
HiveContainer.HIVE_METASTORE_PORT);

containerSuite.startKafkaContainer();
kafkaBootstrapServers =
String.format(
"%s:%d",
containerSuite.getKafkaContainer().getContainerIpAddress(),
KafkaContainer.DEFAULT_BROKER_PORT);

JettyServerConfig jettyServerConfig =
JettyServerConfig.fromConfig(serverConfig, WEBSERVER_CONF_PREFIX);

String uri = "http://" + jettyServerConfig.getHost() + ":" + jettyServerConfig.getHttpPort();
System.setProperty("user.name", "test");
GravitinoAdminClient anotherClient = GravitinoAdminClient.builder(uri).withSimpleAuth().build();

metalake = client.createMetalake(metalakeName, "metalake comment", Collections.emptyMap());
anotherMetalake = anotherClient.loadMetalake(metalakeName);
}

@AfterAll
public static void tearDown() {
if (client != null) {
client.dropMetalake(metalakeName);
client.close();
client = null;
}

try {
closer.close();
} catch (Exception e) {
LOG.error("Exception in closing CloseableGroup", e);
}
}

@Test
public void testCreateTopic() {
String catalogName = RandomNameUtils.genRandomName("catalogA");

Map<String, String> properties = Maps.newHashMap();
properties.put("bootstrap.servers", kafkaBootstrapServers);

// Test to create catalog with not-existed user
Assertions.assertThrows(
ForbiddenException.class,
() ->
anotherMetalake.createCatalog(
catalogName, Catalog.Type.MESSAGING, "kafka", "comment", properties));

metalake.createCatalog(catalogName, Catalog.Type.MESSAGING, "kafka", "comment", properties);

// Test to create topic with not-existed user
metalake.addUser("test");
Catalog catalog = anotherMetalake.loadCatalog(catalogName);
metalake.removeUser("test");
NameIdentifier topicIdent = NameIdentifier.of("default", "topic");
Assertions.assertThrows(
ForbiddenException.class,
() ->
catalog
.asTopicCatalog()
.createTopic(topicIdent, "comment", null, Collections.emptyMap()));

metalake.dropCatalog(catalogName);
}

@Test
public void testCreateFileset() {
String catalogName = RandomNameUtils.genRandomName("catalog");
// Test to create a fileset with a not-existed user
Assertions.assertThrows(
ForbiddenException.class,
() ->
anotherMetalake.createCatalog(
catalogName, Catalog.Type.FILESET, "hadoop", "comment", Collections.emptyMap()));

Catalog catalog =
metalake.createCatalog(
catalogName, Catalog.Type.FILESET, "hadoop", "comment", Collections.emptyMap());

// Test to create a schema with a not-existed user
Catalog anotherCatalog = anotherMetalake.loadCatalog(catalogName);
Assertions.assertThrows(
ForbiddenException.class,
() -> anotherCatalog.asSchemas().createSchema("schema", "comment", Collections.emptyMap()));

catalog.asSchemas().createSchema("schema", "comment", Collections.emptyMap());

// Test to create a fileset with a not-existed user
NameIdentifier fileIdent = NameIdentifier.of("schema", "fileset");
Assertions.assertThrows(
ForbiddenException.class,
() ->
anotherCatalog
.asFilesetCatalog()
.createFileset(
fileIdent, "comment", Fileset.Type.EXTERNAL, "tmp", Collections.emptyMap()));

// Clean up
catalog.asSchemas().dropSchema("schema", true);
metalake.dropCatalog(catalogName);
}

@Test
public void testCreateRole() {
SecurableObject metalakeSecObject =
SecurableObjects.ofMetalake(
metalakeName, Lists.newArrayList(Privileges.CreateCatalog.allow()));
Assertions.assertThrows(
ForbiddenException.class,
() ->
anotherMetalake.createRole(
"role", Collections.emptyMap(), Lists.newArrayList(metalakeSecObject)));
}

@Test
public void testCreateTable() {
String catalogName = RandomNameUtils.genRandomName("catalog");
Map<String, String> properties = Maps.newHashMap();
properties.put("metastore.uris", hmsUri);

// Test to create catalog with not-existed user
Assertions.assertThrows(
ForbiddenException.class,
() ->
anotherMetalake.createCatalog(
catalogName, Catalog.Type.RELATIONAL, "hive", "catalog comment", properties));
Catalog catalog =
metalake.createCatalog(
catalogName, Catalog.Type.RELATIONAL, "hive", "catalog comment", properties);

// Test to create schema with not-existed user
Catalog anotherCatalog = anotherMetalake.loadCatalog(catalogName);
Assertions.assertThrows(
ForbiddenException.class,
() -> anotherCatalog.asSchemas().createSchema("schema", "comment", Collections.emptyMap()));

catalog.asSchemas().createSchema("schema", "comment", Collections.emptyMap());

// Test to create table with not-existed user
NameIdentifier tableIdent = NameIdentifier.of("schema", "table");
Assertions.assertThrows(
ForbiddenException.class,
() ->
anotherCatalog
.asTableCatalog()
.createTable(
tableIdent,
new Column[] {
Column.of("col1", Types.IntegerType.get()),
Column.of("col2", Types.StringType.get())
},
"comment",
Collections.emptyMap()));

// Clean up
catalog.asSchemas().dropSchema("schema", true);
metalake.dropCatalog(catalogName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public class ErrorConstants {
/** Error codes for connect to catalog failed. */
public static final int CONNECTION_FAILED_CODE = 1007;

/** Error codes for forbidden operation. */
public static final int FORBIDDEN_CODE = 1008;

/** Error codes for invalid state. */
public static final int UNKNOWN_ERROR_CODE = 1100;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.apache.gravitino.exceptions.ConnectionFailedException;
import org.apache.gravitino.exceptions.ForbiddenException;
import org.apache.gravitino.exceptions.RESTException;

/** Represents an error response. */
Expand Down Expand Up @@ -305,6 +306,21 @@ public static ErrorResponse unsupportedOperation(String message, Throwable throw
getStackTrace(throwable));
}

/**
* Create a new forbidden operation error instance of {@link ErrorResponse}.
*
* @param message The message of the error.
* @param throwable The throwable that caused the error.
* @return The new instance.
*/
public static ErrorResponse forbidden(String message, Throwable throwable) {
return new ErrorResponse(
ErrorConstants.FORBIDDEN_CODE,
ForbiddenException.class.getSimpleName(),
message,
getStackTrace(throwable));
}

private static List<String> getStackTrace(Throwable throwable) {
if (throwable == null) {
return null;
Expand Down
Loading

0 comments on commit 91c5869

Please sign in to comment.