You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2023/01/09 02:36:59 UTC
[james-project] branch master updated: JAMES-3756 Delegation store should allow listing accounts delegated to me (#1372)
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push:
new 7cd91b5158 JAMES-3756 Delegation store should allow listing accounts delegated to me (#1372)
7cd91b5158 is described below
commit 7cd91b5158536cc2dfc254376c7acd97263f3343
Author: vttran <vt...@linagora.com>
AuthorDate: Mon Jan 9 09:36:53 2023 +0700
JAMES-3756 Delegation store should allow listing accounts delegated to me (#1372)
---
.../org/apache/james/user/api/DelegationStore.java | 5 +
.../james/user/api/DelegationStoreContract.java | 119 ++++++++++++++++++++-
.../user/cassandra/CassandraDelegationStore.java | 10 ++
.../james/user/cassandra/CassandraUsersDAO.java | 92 ++++++++++++++--
.../cassandra/CassandraUsersRepositoryModule.java | 3 +-
.../user/cassandra/tables/CassandraUserTable.java | 1 +
.../cassandra/CassandraDelegationStoreTest.java | 14 ++-
.../james/user/memory/MemoryDelegationStore.java | 15 +++
.../james/user/memory/NaiveDelegationStore.java | 10 ++
upgrade-instructions.md | 16 +++
10 files changed, 274 insertions(+), 11 deletions(-)
diff --git a/server/data/data-api/src/main/java/org/apache/james/user/api/DelegationStore.java b/server/data/data-api/src/main/java/org/apache/james/user/api/DelegationStore.java
index b8cd22b5f8..25d86eae4c 100644
--- a/server/data/data-api/src/main/java/org/apache/james/user/api/DelegationStore.java
+++ b/server/data/data-api/src/main/java/org/apache/james/user/api/DelegationStore.java
@@ -45,4 +45,9 @@ public interface DelegationStore {
default Fluent removeAuthorizedUser(Username userWithAccess) {
return baseUser -> removeAuthorizedUser(baseUser, userWithAccess);
}
+
+ Publisher<Username> delegatedUsers(Username baseUser);
+
+ Publisher<Void> removeDelegatedUser(Username baseUser, Username delegatedToUser);
+
}
diff --git a/server/data/data-api/src/test/java/org/apache/james/user/api/DelegationStoreContract.java b/server/data/data-api/src/test/java/org/apache/james/user/api/DelegationStoreContract.java
index 2375a5e0fd..7a398ab49f 100644
--- a/server/data/data-api/src/test/java/org/apache/james/user/api/DelegationStoreContract.java
+++ b/server/data/data-api/src/test/java/org/apache/james/user/api/DelegationStoreContract.java
@@ -20,6 +20,7 @@
package org.apache.james.user.api;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
import org.apache.james.core.Username;
import org.junit.jupiter.api.Test;
@@ -37,8 +38,11 @@ public interface DelegationStoreContract {
DelegationStore testee();
+ default void addUser(Username username) {
+ }
+
@Test
- default void authorizedUsersShouldReturnEmptyByDefult() {
+ default void authorizedUsersShouldReturnEmptyByDefault() {
assertThat(Flux.from(testee().authorizedUsers(ALICE)).collectList().block())
.isEmpty();
}
@@ -67,6 +71,8 @@ public interface DelegationStoreContract {
@Test
default void clearShouldBeIdempotent() {
+ addUser(BOB);
+ Mono.from(testee().addAuthorizedUser(EDGARD, BOB)).block();
Mono.from(testee().addAuthorizedUser(ALICE, BOB)).block();
Mono.from(testee().addAuthorizedUser(ALICE, CEDRIC)).block();
Mono.from(testee().addAuthorizedUser(ALICE, DAMIEN)).block();
@@ -76,6 +82,8 @@ public interface DelegationStoreContract {
assertThat(Flux.from(testee().authorizedUsers(ALICE)).collectList().block())
.isEmpty();
+ assertThat(Flux.from(testee().delegatedUsers(BOB)).collectList().block())
+ .containsExactly(EDGARD);
}
@Test
@@ -92,15 +100,20 @@ public interface DelegationStoreContract {
@Test
default void removeAuthorizedUserShouldBeIdempotent() {
+ addUser(CEDRIC);
Mono.from(testee().addAuthorizedUser(ALICE, BOB)).block();
Mono.from(testee().addAuthorizedUser(ALICE, CEDRIC)).block();
Mono.from(testee().addAuthorizedUser(ALICE, DAMIEN)).block();
+ Mono.from(testee().addAuthorizedUser(EDGARD, CEDRIC)).block();
Mono.from(testee().removeAuthorizedUser(ALICE, CEDRIC)).block();
Mono.from(testee().removeAuthorizedUser(ALICE, CEDRIC)).block();
assertThat(Flux.from(testee().authorizedUsers(ALICE)).collectList().block())
.containsOnly(BOB, DAMIEN);
+
+ assertThat(Flux.from(testee().delegatedUsers(CEDRIC)).collectList().block())
+ .containsOnly(EDGARD);
}
@Test
@@ -121,4 +134,108 @@ public interface DelegationStoreContract {
.isEmpty();
}
+ @Test
+ default void delegatedUsersShouldReturnEmptyByDefault() {
+ assertThat(Flux.from(testee().delegatedUsers(BOB)).collectList().block())
+ .isEmpty();
+ }
+
+ @Test
+ default void delegatedUsersShouldReturnCorrectUsers() {
+ addUser(BOB);
+ Mono.from(testee().addAuthorizedUser(ALICE, BOB)).block();
+ Mono.from(testee().addAuthorizedUser(CEDRIC, BOB)).block();
+
+ assertThat(Flux.from(testee().delegatedUsers(BOB)).collectList().block())
+ .containsOnly(CEDRIC, ALICE);
+ }
+
+ @Test
+ default void delegatedUsersShouldReturnUpdateEntryAfterClearDelegatedBaseUser() {
+ addUser(BOB);
+ Mono.from(testee().addAuthorizedUser(ALICE, BOB)).block();
+ Mono.from(testee().addAuthorizedUser(CEDRIC, BOB)).block();
+
+ Mono.from(testee().clear(ALICE)).block();
+ assertThat(Flux.from(testee().delegatedUsers(BOB)).collectList().block())
+ .containsOnly(CEDRIC);
+ }
+
+ @Test
+ default void delegatedUsersShouldNotReturnDeletedUsers() {
+ addUser(BOB);
+ Mono.from(testee().addAuthorizedUser(ALICE, BOB)).block();
+ Mono.from(testee().addAuthorizedUser(CEDRIC, BOB)).block();
+ Mono.from(testee().addAuthorizedUser(DAMIEN, BOB)).block();
+
+ Mono.from(testee().removeAuthorizedUser(ALICE, BOB)).block();
+
+ assertThat(Flux.from(testee().delegatedUsers(BOB)).collectList().block())
+ .containsOnly(CEDRIC, DAMIEN);
+ }
+
+ @Test
+ default void delegatedUsersShouldNotReturnDuplicates() {
+ addUser(BOB);
+ Mono.from(testee().addAuthorizedUser(ALICE, BOB)).block();
+ Mono.from(testee().addAuthorizedUser(ALICE, BOB)).block();
+
+ assertThat(Flux.from(testee().delegatedUsers(BOB)).collectList().block())
+ .containsExactly(ALICE);
+ }
+
+ @Test
+ default void delegatedUsersShouldNotReturnUnrelatedUsers() {
+ addUser(BOB);
+ Mono.from(testee().addAuthorizedUser(BOB, ALICE)).block();
+ Mono.from(testee().addAuthorizedUser(BOB, CEDRIC)).block();
+
+ assertThat(Flux.from(testee().delegatedUsers(BOB)).collectList().block())
+ .isEmpty();
+ }
+
+ @Test
+ default void addAuthorizedUserShouldNotThrowWhenUserWithAccessDoesNotExist() {
+ assertThatCode(() -> Mono.from(testee().addAuthorizedUser(BOB, ALICE)).block())
+ .doesNotThrowAnyException();
+ }
+
+
+ @Test
+ default void delegatedUserShouldNotReturnDeletedUsers() {
+ addUser(BOB);
+ Mono.from(testee().addAuthorizedUser(ALICE, BOB)).block();
+ Mono.from(testee().addAuthorizedUser(CEDRIC, BOB)).block();
+
+ Mono.from(testee().removeDelegatedUser(BOB, CEDRIC)).block();
+
+ assertThat(Flux.from(testee().delegatedUsers(BOB)).collectList().block())
+ .containsOnly(ALICE);
+ }
+
+ @Test
+ default void removeDelegatedUserShouldBeIdempotent() {
+ addUser(BOB);
+ Mono.from(testee().addAuthorizedUser(ALICE, BOB)).block();
+ Mono.from(testee().addAuthorizedUser(CEDRIC, BOB)).block();
+
+ Mono.from(testee().removeDelegatedUser(BOB, CEDRIC)).block();
+ Mono.from(testee().removeDelegatedUser(BOB, CEDRIC)).block();
+
+ assertThat(Flux.from(testee().delegatedUsers(BOB)).collectList().block())
+ .containsOnly(ALICE);
+ }
+
+ @Test
+ default void removeDelegatedUserShouldUpdateAuthorizedUserRelated() {
+ addUser(BOB);
+ Mono.from(testee().addAuthorizedUser(ALICE, BOB)).block();
+ Mono.from(testee().addAuthorizedUser(ALICE, CEDRIC)).block();
+
+ Mono.from(testee().removeDelegatedUser(BOB, ALICE)).block();
+
+ assertThat(Flux.from(testee().authorizedUsers(ALICE)).collectList().block())
+ .containsOnly(CEDRIC);
+ }
+
}
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraDelegationStore.java b/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraDelegationStore.java
index c95bb10ece..9fc32c1bfa 100644
--- a/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraDelegationStore.java
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraDelegationStore.java
@@ -52,4 +52,14 @@ public class CassandraDelegationStore implements DelegationStore {
public Publisher<Void> removeAuthorizedUser(Username baseUser, Username userWithAccess) {
return cassandraUsersDAO.removeAuthorizedUser(baseUser, userWithAccess);
}
+
+ @Override
+ public Publisher<Username> delegatedUsers(Username baseUser) {
+ return cassandraUsersDAO.getDelegatedToUsers(baseUser);
+ }
+
+ @Override
+ public Publisher<Void> removeDelegatedUser(Username baseUser, Username delegatedToUser) {
+ return cassandraUsersDAO.removeDelegatedToUser(baseUser, delegatedToUser);
+ }
}
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraUsersDAO.java b/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraUsersDAO.java
index a64bbad2d0..2732a87e08 100644
--- a/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraUsersDAO.java
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraUsersDAO.java
@@ -27,6 +27,7 @@ import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.selectFrom;
import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.update;
import static org.apache.james.user.cassandra.tables.CassandraUserTable.ALGORITHM;
import static org.apache.james.user.cassandra.tables.CassandraUserTable.AUTHORIZED_USERS;
+import static org.apache.james.user.cassandra.tables.CassandraUserTable.DELEGATED_USERS;
import static org.apache.james.user.cassandra.tables.CassandraUserTable.NAME;
import static org.apache.james.user.cassandra.tables.CassandraUserTable.PASSWORD;
import static org.apache.james.user.cassandra.tables.CassandraUserTable.REALNAME;
@@ -46,10 +47,15 @@ import org.apache.james.user.lib.UsersDAO;
import org.apache.james.user.lib.model.Algorithm;
import org.apache.james.user.lib.model.Algorithm.HashingMode;
import org.apache.james.user.lib.model.DefaultUser;
+import org.apache.james.util.FunctionalUtils;
import org.reactivestreams.Publisher;
import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.cql.BatchStatementBuilder;
+import com.datastax.oss.driver.api.core.cql.BatchType;
+import com.datastax.oss.driver.api.core.cql.BoundStatement;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
+import com.datastax.oss.driver.api.core.cql.Statement;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
@@ -72,6 +78,10 @@ public class CassandraUsersDAO implements UsersDAO {
private final PreparedStatement addAuthorizedUsersStatement;
private final PreparedStatement removeAuthorizedUsersStatement;
+ private final PreparedStatement getDelegatedToUsersStatement;
+ private final PreparedStatement addDelegatedToUsersStatement;
+ private final PreparedStatement removeDelegatedToUsersStatement;
+
private final Algorithm preferredAlgorithm;
private final HashingMode fallbackHashingMode;
@@ -134,6 +144,21 @@ public class CassandraUsersDAO implements UsersDAO {
.remove(AUTHORIZED_USERS, bindMarker(AUTHORIZED_USERS))
.whereColumn(NAME).isEqualTo(bindMarker(NAME))
.build());
+
+ this.getDelegatedToUsersStatement = session.prepare(selectFrom(TABLE_NAME)
+ .columns(DELEGATED_USERS)
+ .whereColumn(NAME).isEqualTo(bindMarker(NAME))
+ .build());
+
+ this.addDelegatedToUsersStatement = session.prepare(update(TABLE_NAME)
+ .append(DELEGATED_USERS, bindMarker(DELEGATED_USERS))
+ .whereColumn(NAME).isEqualTo(bindMarker(NAME))
+ .build());
+
+ this.removeDelegatedToUsersStatement = session.prepare(update(TABLE_NAME)
+ .remove(DELEGATED_USERS, bindMarker(DELEGATED_USERS))
+ .whereColumn(NAME).isEqualTo(bindMarker(NAME))
+ .build());
}
@VisibleForTesting
@@ -155,6 +180,11 @@ public class CassandraUsersDAO implements UsersDAO {
Algorithm.of(row.getString(ALGORITHM), fallbackHashingMode), preferredAlgorithm));
}
+ public Mono<Boolean> exist(Username name) {
+ return executor.executeReturnExists(getUserStatement.bind()
+ .setString(NAME, name.asString()));
+ }
+
@Override
public void updateUser(User user) throws UsersRepositoryException {
Preconditions.checkArgument(user instanceof DefaultUser);
@@ -173,21 +203,47 @@ public class CassandraUsersDAO implements UsersDAO {
}
public Mono<Void> addAuthorizedUsers(Username baseUser, Username userWithAccess) {
- return executor.executeVoid(addAuthorizedUsersStatement.bind()
+ BatchStatementBuilder batchBuilder = new BatchStatementBuilder(BatchType.LOGGED);
+ BoundStatement addAuthorizedStatement = addAuthorizedUsersStatement.bind()
.setString(NAME, baseUser.asString())
- .setSet(AUTHORIZED_USERS, ImmutableSet.of(userWithAccess.asString()), String.class));
+ .setSet(AUTHORIZED_USERS, ImmutableSet.of(userWithAccess.asString()), String.class);
+ batchBuilder.addStatement(addAuthorizedStatement);
+ batchBuilder.addStatement(addDelegatedToUsersStatement.bind()
+ .setString(NAME, userWithAccess.asString())
+ .setSet(DELEGATED_USERS, ImmutableSet.of(baseUser.asString()), String.class));
+
+ return getUserByNameReactive(userWithAccess).hasElement()
+ .filter(FunctionalUtils.identityPredicate())
+ .map(existAuthorizedUser -> (Statement) batchBuilder.build())
+ .switchIfEmpty(Mono.just(addAuthorizedStatement))
+ .flatMap(executor::executeVoid);
}
public Mono<Void> removeAuthorizedUser(Username baseUser, Username userWithAccess) {
- return executor.executeVoid(removeAuthorizedUsersStatement.bind()
- .setString(NAME, baseUser.asString())
- .setSet(AUTHORIZED_USERS, ImmutableSet.of(userWithAccess.asString()), String.class));
+ return executor.executeVoid(new BatchStatementBuilder(BatchType.LOGGED)
+ .addStatement(removeAuthorizedUsersStatement.bind()
+ .setString(NAME, baseUser.asString())
+ .setSet(AUTHORIZED_USERS, ImmutableSet.of(userWithAccess.asString()), String.class))
+ .addStatement(removeDelegatedToUsersStatement.bind()
+ .setString(NAME, userWithAccess.asString())
+ .setSet(DELEGATED_USERS, ImmutableSet.of(baseUser.asString()), String.class))
+ .build());
}
public Mono<Void> removeAllAuthorizedUsers(Username baseUser) {
- return executor.executeVoid(
- removeAllAuthorizedUsersStatement.bind()
- .setString(NAME, baseUser.asString()));
+ return getAuthorizedUsers(baseUser)
+ .collectList()
+ .map(authorizedList -> {
+ BatchStatementBuilder batch = new BatchStatementBuilder(BatchType.LOGGED);
+ authorizedList.forEach(username -> batch.addStatement(
+ removeDelegatedToUsersStatement.bind()
+ .setString(NAME, username.asString())
+ .setSet(DELEGATED_USERS, ImmutableSet.of(baseUser.asString()), String.class)));
+ batch.addStatement(removeAllAuthorizedUsersStatement.bind()
+ .setString(NAME, baseUser.asString()));
+ return batch.build();
+ })
+ .flatMap(executor::executeVoid);
}
public Flux<Username> getAuthorizedUsers(Username name) {
@@ -199,6 +255,26 @@ public class CassandraUsersDAO implements UsersDAO {
.map(Username::of);
}
+ public Mono<Void> removeDelegatedToUser(Username baseUser, Username delegatedToUser) {
+ return executor.executeVoid(new BatchStatementBuilder(BatchType.LOGGED)
+ .addStatement(removeAuthorizedUsersStatement.bind()
+ .setString(NAME, delegatedToUser.asString())
+ .setSet(AUTHORIZED_USERS, ImmutableSet.of(baseUser.asString()), String.class))
+ .addStatement(removeDelegatedToUsersStatement.bind()
+ .setString(NAME, baseUser.asString())
+ .setSet(DELEGATED_USERS, ImmutableSet.of(delegatedToUser.asString()), String.class))
+ .build());
+ }
+
+ public Flux<Username> getDelegatedToUsers(Username name) {
+ return executor.executeSingleRow(
+ getDelegatedToUsersStatement.bind()
+ .setString(NAME, name.asString()))
+ .mapNotNull(row -> row.getSet(DELEGATED_USERS, String.class))
+ .flatMapIterable(set -> set)
+ .map(Username::of);
+ }
+
@Override
public void removeUser(Username name) throws UsersRepositoryException {
boolean executed = executor.executeReturnApplied(
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraUsersRepositoryModule.java b/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraUsersRepositoryModule.java
index 6ec22033cf..16e34f96cb 100644
--- a/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraUsersRepositoryModule.java
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/CassandraUsersRepositoryModule.java
@@ -32,6 +32,7 @@ public interface CassandraUsersRepositoryModule {
.withColumn(CassandraUserTable.REALNAME, DataTypes.TEXT)
.withColumn(CassandraUserTable.PASSWORD, DataTypes.TEXT)
.withColumn(CassandraUserTable.ALGORITHM, DataTypes.TEXT)
- .withColumn(CassandraUserTable.AUTHORIZED_USERS, DataTypes.setOf(DataTypes.TEXT)))
+ .withColumn(CassandraUserTable.AUTHORIZED_USERS, DataTypes.setOf(DataTypes.TEXT))
+ .withColumn(CassandraUserTable.DELEGATED_USERS, DataTypes.setOf(DataTypes.TEXT)))
.build();
}
diff --git a/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/tables/CassandraUserTable.java b/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/tables/CassandraUserTable.java
index fbac598ade..2e5c4d0fcd 100644
--- a/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/tables/CassandraUserTable.java
+++ b/server/data/data-cassandra/src/main/java/org/apache/james/user/cassandra/tables/CassandraUserTable.java
@@ -29,4 +29,5 @@ public interface CassandraUserTable {
CqlIdentifier PASSWORD = CqlIdentifier.fromCql("passwd");
CqlIdentifier REALNAME = CqlIdentifier.fromCql("realname");
CqlIdentifier AUTHORIZED_USERS = CqlIdentifier.fromCql("authorized_users");
+ CqlIdentifier DELEGATED_USERS = CqlIdentifier.fromCql("delegated_users");
}
diff --git a/server/data/data-cassandra/src/test/java/org/apache/james/user/cassandra/CassandraDelegationStoreTest.java b/server/data/data-cassandra/src/test/java/org/apache/james/user/cassandra/CassandraDelegationStoreTest.java
index 22067d17f1..f190189e39 100644
--- a/server/data/data-cassandra/src/test/java/org/apache/james/user/cassandra/CassandraDelegationStoreTest.java
+++ b/server/data/data-cassandra/src/test/java/org/apache/james/user/cassandra/CassandraDelegationStoreTest.java
@@ -20,8 +20,10 @@
package org.apache.james.user.cassandra;
import org.apache.james.backends.cassandra.CassandraClusterExtension;
+import org.apache.james.core.Username;
import org.apache.james.user.api.DelegationStore;
import org.apache.james.user.api.DelegationStoreContract;
+import org.apache.james.user.api.UsersRepositoryException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -30,10 +32,12 @@ public class CassandraDelegationStoreTest implements DelegationStoreContract {
static CassandraClusterExtension cassandraCluster = new CassandraClusterExtension(CassandraUsersRepositoryModule.MODULE);
private CassandraDelegationStore testee;
+ private CassandraUsersDAO cassandraUsersDAO;
@BeforeEach
void setUp() {
- testee = new CassandraDelegationStore(new CassandraUsersDAO(cassandraCluster.getCassandraCluster().getConf()));
+ cassandraUsersDAO = new CassandraUsersDAO(cassandraCluster.getCassandraCluster().getConf());
+ testee = new CassandraDelegationStore(cassandraUsersDAO);
}
@Override
@@ -41,4 +45,12 @@ public class CassandraDelegationStoreTest implements DelegationStoreContract {
return testee;
}
+ @Override
+ public void addUser(Username username) {
+ try {
+ cassandraUsersDAO.addUser(username, "password");
+ } catch (UsersRepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/server/data/data-memory/src/main/java/org/apache/james/user/memory/MemoryDelegationStore.java b/server/data/data-memory/src/main/java/org/apache/james/user/memory/MemoryDelegationStore.java
index fed54584db..637ab7cdfd 100644
--- a/server/data/data-memory/src/main/java/org/apache/james/user/memory/MemoryDelegationStore.java
+++ b/server/data/data-memory/src/main/java/org/apache/james/user/memory/MemoryDelegationStore.java
@@ -18,6 +18,8 @@
****************************************************************/
package org.apache.james.user.memory;
+import java.util.Map;
+
import org.apache.james.core.Username;
import org.apache.james.user.api.DelegationStore;
import org.reactivestreams.Publisher;
@@ -52,6 +54,19 @@ public class MemoryDelegationStore implements DelegationStore {
return Mono.fromRunnable(() -> delegations.remove(baseUser, userWithAccess));
}
+ @Override
+ public Publisher<Username> delegatedUsers(Username baseUser) {
+ return Flux.fromIterable(delegations.entries())
+ .filter(entry -> entry.getValue().equals(baseUser))
+ .map(Map.Entry::getKey)
+ .distinct();
+ }
+
+ @Override
+ public Publisher<Void> removeDelegatedUser(Username baseUser, Username delegatedToUser) {
+ return Mono.fromRunnable(() -> delegations.remove(delegatedToUser, baseUser));
+ }
+
@Override
public Publisher<Void> clear(Username baseUser) {
return Mono.fromRunnable(() -> delegations.removeAll(baseUser));
diff --git a/server/data/data-memory/src/main/java/org/apache/james/user/memory/NaiveDelegationStore.java b/server/data/data-memory/src/main/java/org/apache/james/user/memory/NaiveDelegationStore.java
index fbd6e4007c..90db00152c 100644
--- a/server/data/data-memory/src/main/java/org/apache/james/user/memory/NaiveDelegationStore.java
+++ b/server/data/data-memory/src/main/java/org/apache/james/user/memory/NaiveDelegationStore.java
@@ -44,4 +44,14 @@ public class NaiveDelegationStore implements DelegationStore {
public Publisher<Void> removeAuthorizedUser(Username baseUser, Username userWithAccess) {
throw new NotImplementedException();
}
+
+ @Override
+ public Publisher<Username> delegatedUsers(Username baseUser) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public Publisher<Void> removeDelegatedUser(Username baseUser, Username delegatedToUser) {
+ throw new NotImplementedException();
+ }
}
diff --git a/upgrade-instructions.md b/upgrade-instructions.md
index 545238a009..e908deeb96 100644
--- a/upgrade-instructions.md
+++ b/upgrade-instructions.md
@@ -25,6 +25,7 @@ Change list:
- [Blob Store AES upgraded to PBKDF2WithHmacSHA512](#blob-store-aes-upgraded-to-pbkdf2withhmacsha512)
- [Adding saveDate column to messageIdTable and imapUidTable](#adding-savedate-column-to-messageidtable-and-imapuidtable)
- [Adding the saveDate to the OpenSearch index](#adding-the-savedate-to-the-opensearch-index)
+- [Adding delegatedUser column to user_table](#adding-delegatedusers-column-to-user-table)
### Adding the saveDate to the OpenSearch index
@@ -67,6 +68,21 @@ ALTER TABLE james_keyspace.messageIdTable ADD save_date timestamp;
ALTER TABLE james_keyspace.imapUidTable ADD save_date timestamp;
```
+### Adding delegated_users column to user table
+
+Date 05/01/2023
+
+JIRA: https://issues.apache.org/jira/browse/JAMES-3756
+
+Concerned product: Distributed James, Cassandra James Server
+
+Add `delegated_users` column to `user` tables in order to store delegated users that user has access to.
+
+In order to add this `delegated_users` column you need to run the following CQL commands:
+```
+ALTER TABLE james_keyspace.user ADD delegated_users set<text>;
+```
+
### Blob Store AES upgraded to PBKDF2WithHmacSHA512
Date: 06/10/2022
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org