Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.stargate.it.cql.JwtAuthTest Maven / Gradle / Ivy
package io.stargate.it.cql;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import com.datastax.oss.driver.api.core.AllNodesFailedException;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.CqlSessionBuilder;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata;
import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException;
import com.datastax.oss.driver.api.core.servererrors.UnauthorizedException;
import io.stargate.it.BaseIntegrationTest;
import io.stargate.it.KeycloakContainer;
import io.stargate.it.TestOrder;
import io.stargate.it.driver.CqlSessionExtension;
import io.stargate.it.driver.CqlSessionSpec;
import io.stargate.it.driver.TestKeyspace;
import io.stargate.it.driver.WithProtocolVersion;
import io.stargate.it.storage.StargateParameters;
import io.stargate.it.storage.StargateSpec;
import java.io.IOException;
import java.time.Instant;
import java.util.Optional;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.testcontainers.junit.jupiter.Testcontainers;
@StargateSpec(parametersCustomizer = "buildParameters")
@ExtendWith(CqlSessionExtension.class)
@CqlSessionSpec(
initQueries = {
"CREATE ROLE IF NOT EXISTS 'web_user' WITH PASSWORD = 'web_user' AND LOGIN = TRUE",
"CREATE KEYSPACE IF NOT EXISTS store2 WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':'1'}",
"CREATE TABLE IF NOT EXISTS store2.shopping_cart (userid text, item_count int, last_update_timestamp timestamp, PRIMARY KEY (userid, last_update_timestamp));",
"INSERT INTO store2.shopping_cart (userid, item_count, last_update_timestamp) VALUES ('9876', 2, toTimeStamp(now()))",
"INSERT INTO store2.shopping_cart (userid, item_count, last_update_timestamp) VALUES ('1234', 5, toTimeStamp(now()))",
"GRANT MODIFY ON TABLE store2.shopping_cart TO web_user",
"GRANT SELECT ON TABLE store2.shopping_cart TO web_user",
})
@Testcontainers(disabledWithoutDocker = true)
@Order(TestOrder.LAST)
public abstract class JwtAuthTest extends BaseIntegrationTest {
private final String keyspaceName = "store2";
private final String tableName = "shopping_cart";
private static String authToken;
private static KeycloakContainer keycloakContainer;
@SuppressWarnings("unused") // referenced in @StargateSpec
public static void buildParameters(StargateParameters.Builder builder) throws IOException {
keycloakContainer = new KeycloakContainer();
keycloakContainer.initKeycloakContainer();
builder.enableAuth(true);
builder.putSystemProperties("stargate.auth_id", "AuthJwtService");
builder.putSystemProperties("stargate.cql_use_auth_service", "true");
builder.putSystemProperties("stargate.cql_token_max_length", "4096");
builder.putSystemProperties(
"stargate.auth.jwt_provider_url",
String.format(
"%s/auth/realms/stargate/protocol/openid-connect/certs", keycloakContainer.host()));
}
@AfterAll
public static void teardown() {
keycloakContainer.stop();
}
@BeforeEach
public void setup(CqlSession session) throws IOException {
// Must recreate every time because some methods alter the schema
session.execute("DROP TABLE IF EXISTS jwt_auth_test");
session.execute("CREATE TABLE jwt_auth_test (k text PRIMARY KEY, v text)");
authToken = keycloakContainer.generateJWT();
}
@Test
public void invalidCredentials(CqlSessionBuilder builder) {
assertThatThrownBy(() -> builder.withAuthCredentials("invalid", "invalid").build())
.isInstanceOf(AllNodesFailedException.class)
.hasMessageContaining("Provided username invalid and/or password are incorrect");
}
@Test
public void tokenAuthentication(CqlSessionBuilder builder) {
try (CqlSession tokenSession = builder.withAuthCredentials("token", authToken).build()) {
Row row = tokenSession.execute("SELECT * FROM system.local").one();
assertThat(row).isNotNull();
}
}
@Test
public void useKeyspace(CqlSessionBuilder builder) {
try (CqlSession tokenSession = builder.withAuthCredentials("token", authToken).build()) {
assertThatThrownBy(() -> tokenSession.execute("SELECT * FROM " + tableName))
.isInstanceOf(InvalidQueryException.class)
.hasMessage(
"No keyspace has been specified. USE a keyspace, or explicitly specify keyspace.tablename");
// Switch to keyspace and retry query
tokenSession.execute(String.format("USE %s", keyspaceName));
Row row = tokenSession.execute("SELECT * FROM " + tableName).one();
assertThat(row).isNotNull();
}
}
@Test
public void createKeyspaceUnauthorized(CqlSessionBuilder builder) {
try (CqlSession tokenSession = builder.withAuthCredentials("token", authToken).build()) {
assertThatThrownBy(
() ->
tokenSession.execute(
"CREATE KEYSPACE IF NOT EXISTS foo WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':'1'}"))
.isInstanceOf(UnauthorizedException.class)
.hasMessage(
"User web_user has no CREATE permission on or any of its parents");
}
}
@Test
public void createTableUnauthorized(
CqlSessionBuilder builder, @TestKeyspace CqlIdentifier keyspaceId) {
try (CqlSession tokenSession = builder.withAuthCredentials("token", authToken).build()) {
String errorMessage =
"User web_user has no CREATE permission on or any of its parents";
if (backend.isDse() || isCassandra41()) {
errorMessage =
"User web_user has no CREATE permission on or any of its parents";
}
assertThatThrownBy(
() ->
tokenSession.execute(
String.format(
"CREATE TABLE IF NOT EXISTS %s.test (k INT PRIMARY KEY)",
keyspaceId.asCql(false))))
.isInstanceOf(UnauthorizedException.class)
.hasMessageMatching(errorMessage);
}
}
@Test
public void insertPreparedStatement(CqlSessionBuilder builder) {
try (CqlSession tokenSession = builder.withAuthCredentials("token", authToken).build()) {
PreparedStatement prepared =
tokenSession.prepare(
String.format(
"INSERT INTO %s.%s (userid, item_count, last_update_timestamp) VALUES (?, ?, ?)",
keyspaceName, tableName));
Instant now = Instant.now();
tokenSession.execute(prepared.bind("9876", 0, now));
Row row =
tokenSession
.execute(
String.format(
"SELECT * FROM %s.%s WHERE userid=? AND last_update_timestamp=?",
keyspaceName, tableName),
"9876",
now)
.one();
assertThat(row).isNotNull();
assertThat(row.getInt("item_count")).isEqualTo(0);
}
}
@Test
public void insertPreparedStatementNotAuthorized(
CqlSessionBuilder builder, @TestKeyspace CqlIdentifier keyspaceId) {
try (CqlSession tokenSession = builder.withAuthCredentials("token", authToken).build()) {
PreparedStatement prepared =
tokenSession.prepare(
String.format(
"INSERT INTO %s.jwt_auth_test (k, v) VALUES (?, ?)", keyspaceId.asCql(false)));
String errorMessage =
"User web_user has no MODIFY permission on or any of its parents";
if (backend.isDse()) {
errorMessage =
"User web_user has no UPDATE permission on or any of its parents";
}
assertThatThrownBy(() -> tokenSession.execute(prepared.bind("foo", "bar")))
.isInstanceOf(UnauthorizedException.class)
.hasMessageMatching(errorMessage);
}
}
@Test
public void insert(CqlSessionBuilder builder) {
try (CqlSession tokenSession = builder.withAuthCredentials("token", authToken).build()) {
Instant now = Instant.now();
tokenSession.execute(
String.format(
"INSERT INTO %s.%s (userid, item_count, last_update_timestamp) VALUES (?, ?, ?)",
keyspaceName, tableName),
"9876",
1,
now);
Row row =
tokenSession
.execute(
String.format(
"SELECT * FROM %s.%s WHERE userid=? AND last_update_timestamp=?",
keyspaceName, tableName),
"9876",
now)
.one();
assertThat(row).isNotNull();
assertThat(row.getInt("item_count")).isEqualTo(1);
}
}
@Test
public void insertNotAuthorized(
CqlSessionBuilder builder, @TestKeyspace CqlIdentifier keyspaceId) {
try (CqlSession tokenSession = builder.withAuthCredentials("token", authToken).build()) {
String errorMessage =
"User web_user has no MODIFY permission on or any of its parents";
if (backend.isDse()) {
errorMessage =
"User web_user has no UPDATE permission on or any of its parents";
}
assertThatThrownBy(
() ->
tokenSession.execute(
String.format(
"INSERT INTO %s.jwt_auth_test (k, v) VALUES (?, ?)",
keyspaceId.asCql(false)),
"foo",
"bar"))
.isInstanceOf(UnauthorizedException.class)
.hasMessageMatching(errorMessage);
}
}
@Test
public void selectNotAuthorized(
CqlSessionBuilder builder, @TestKeyspace CqlIdentifier keyspaceId) {
try (CqlSession tokenSession = builder.withAuthCredentials("token", authToken).build()) {
assertThatThrownBy(
() ->
tokenSession
.execute(
String.format("SELECT * FROM %s.jwt_auth_test", keyspaceId.asCql(false)))
.one())
.isInstanceOf(UnauthorizedException.class)
.hasMessageMatching(
"User web_user has no SELECT permission on or any of its parents");
}
}
@Test
public void update(CqlSessionBuilder builder) {
try (CqlSession tokenSession = builder.withAuthCredentials("token", authToken).build()) {
Instant now = Instant.now();
tokenSession.execute(
String.format(
"INSERT INTO %s.%s (userid, item_count, last_update_timestamp) VALUES (?, ?, ?)",
keyspaceName, tableName),
"9876",
2,
now);
tokenSession.execute(
String.format(
"UPDATE %s.%s set item_count = ? WHERE userid = ? AND last_update_timestamp = ?",
keyspaceName, tableName),
3,
"9876",
now);
Row row =
tokenSession
.execute(
String.format(
"SELECT * FROM %s.%s WHERE userid=? AND last_update_timestamp=?",
keyspaceName, tableName),
"9876",
now)
.one();
assertThat(row).isNotNull();
assertThat(row.getInt("item_count")).isEqualTo(3);
}
}
@Test
public void updateNotAuthorized(
CqlSessionBuilder builder, @TestKeyspace CqlIdentifier keyspaceId) {
try (CqlSession tokenSession = builder.withAuthCredentials("token", authToken).build()) {
String errorMessage =
"User web_user has no MODIFY permission on or any of its parents";
if (backend.isDse()) {
errorMessage =
"User web_user has no UPDATE permission on or any of its parents";
}
assertThatThrownBy(
() ->
tokenSession.execute(
String.format(
"UPDATE %s.jwt_auth_test set v = ? where k = ?", keyspaceId.asCql(false)),
"bar",
"foo"))
.isInstanceOf(UnauthorizedException.class)
.hasMessageMatching(errorMessage);
}
}
@Test
public void createAlterDropTable(CqlSession session, @TestKeyspace CqlIdentifier keyspaceId) {
session.execute("CREATE TABLE foo(k int PRIMARY KEY)");
assertThat(session.getMetadata().getKeyspace(keyspaceId))
.hasValueSatisfying(ks -> assertThat(ks.getTable("foo")).isNotEmpty());
session.execute("ALTER TABLE foo ADD v int");
Optional updatedTable =
session.getMetadata().getKeyspace(keyspaceId).flatMap(keyspace -> keyspace.getTable("foo"));
assertThat(updatedTable).isPresent();
assertThat(updatedTable.get().getName().asInternal()).isEqualTo("foo");
assertThat(updatedTable.get().getColumns().keySet())
.containsExactly(CqlIdentifier.fromInternal("k"), CqlIdentifier.fromInternal("v"));
assertThat(session.getMetadata().getKeyspace(keyspaceId).flatMap(ks -> ks.getTable("foo")))
.hasValue(updatedTable.get());
session.execute("DROP TABLE foo");
assertThat(session.getMetadata().getKeyspace(keyspaceId))
.hasValueSatisfying(ks -> assertThat(ks.getTable("foo")).isEmpty());
}
@WithProtocolVersion("V4")
public static class WithV4ProtocolVersionTest extends JwtAuthTest {}
@WithProtocolVersion("V5")
public static class WithV5ProtocolVersionTest extends JwtAuthTest {}
}