Skip to content

Commit

Permalink
JAMES-1718 AccessToken API should be asynchronous
Browse files Browse the repository at this point in the history
  • Loading branch information
chibenwa committed Apr 15, 2016
1 parent 231beb5 commit 7e39d44
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 24 deletions.
Expand Up @@ -19,6 +19,8 @@

package org.apache.james.jmap.cassandra.access;

import java.util.concurrent.CompletableFuture;

import javax.inject.Inject;
import javax.inject.Named;

Expand All @@ -28,6 +30,7 @@

import com.datastax.driver.core.Session;
import com.google.common.base.Preconditions;
import com.jasongoodwin.monads.Try;

public class CassandraAccessTokenRepository implements AccessTokenRepository {

Expand All @@ -39,27 +42,29 @@ public CassandraAccessTokenRepository(Session session, @Named(TOKEN_EXPIRATION_I
}

@Override
public void addToken(String username, AccessToken accessToken) {
public CompletableFuture<Void> addToken(String username, AccessToken accessToken) {
Preconditions.checkNotNull(username);
Preconditions.checkArgument(! username.isEmpty(), "Username should not be empty");
Preconditions.checkNotNull(accessToken);

cassandraAccessTokenDAO.addToken(username, accessToken).join();
return cassandraAccessTokenDAO.addToken(username, accessToken);
}

@Override
public void removeToken(AccessToken accessToken) {
public CompletableFuture<Void> removeToken(AccessToken accessToken) {
Preconditions.checkNotNull(accessToken);

cassandraAccessTokenDAO.removeToken(accessToken).join();
return cassandraAccessTokenDAO.removeToken(accessToken);
}

@Override
public String getUsernameFromToken(AccessToken accessToken) throws InvalidAccessToken {
public CompletableFuture<Try<String>> getUsernameFromToken(AccessToken accessToken) throws InvalidAccessToken {
Preconditions.checkNotNull(accessToken);

return cassandraAccessTokenDAO.getUsernameFromToken(accessToken)
.join()
.orElseThrow(() -> new InvalidAccessToken(accessToken));
.thenApply(
optional -> Try.ofFailable(
() -> optional.orElseThrow(
() -> new InvalidAccessToken(accessToken))));
}
}
4 changes: 4 additions & 0 deletions server/data/data-jmap/pom.xml
Expand Up @@ -181,6 +181,10 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.jason-goodwin</groupId>
<artifactId>better-monads</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
Expand Down
Expand Up @@ -19,16 +19,20 @@

package org.apache.james.jmap.api.access;

import java.util.concurrent.CompletableFuture;

import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;

import com.jasongoodwin.monads.Try;

public interface AccessTokenRepository {

String TOKEN_EXPIRATION_IN_MS = "tokenExpirationInMs";

void addToken(String username, AccessToken accessToken);
CompletableFuture<Void> addToken(String username, AccessToken accessToken);

void removeToken(AccessToken accessToken);
CompletableFuture<Void> removeToken(AccessToken accessToken);

String getUsernameFromToken(AccessToken accessToken) throws InvalidAccessToken;
CompletableFuture<Try<String>> getUsernameFromToken(AccessToken accessToken) throws InvalidAccessToken;

}
Expand Up @@ -20,6 +20,7 @@
package org.apache.james.jmap.memory.access;

import java.util.Optional;
import java.util.concurrent.CompletableFuture;

import javax.inject.Inject;
import javax.inject.Named;
Expand All @@ -31,6 +32,7 @@
import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;

import com.google.common.base.Preconditions;
import com.jasongoodwin.monads.Try;

@Singleton
public class MemoryAccessTokenRepository implements AccessTokenRepository {
Expand All @@ -43,30 +45,31 @@ public MemoryAccessTokenRepository(@Named(TOKEN_EXPIRATION_IN_MS) long durationI
}

@Override
public void addToken(String username, AccessToken accessToken) {
public CompletableFuture<Void> addToken(String username, AccessToken accessToken) {
Preconditions.checkNotNull(username);
Preconditions.checkArgument(! username.isEmpty(), "Username should not be empty");
Preconditions.checkNotNull(accessToken);
synchronized (tokensExpirationDates) {
tokensExpirationDates.put(accessToken, username);
}
return CompletableFuture.completedFuture(null);
}

@Override
public void removeToken(AccessToken accessToken) {
public CompletableFuture<Void> removeToken(AccessToken accessToken) {
Preconditions.checkNotNull(accessToken);
synchronized (tokensExpirationDates) {
tokensExpirationDates.remove(accessToken);
}
return CompletableFuture.completedFuture(null);
}

@Override
public String getUsernameFromToken(AccessToken accessToken) throws InvalidAccessToken {
public CompletableFuture<Try<String>> getUsernameFromToken(AccessToken accessToken) throws InvalidAccessToken {
Preconditions.checkNotNull(accessToken);
synchronized (tokensExpirationDates) {
return Optional
.ofNullable(tokensExpirationDates.get(accessToken))
.orElseThrow(() -> new InvalidAccessToken(accessToken));
return CompletableFuture.completedFuture(
Try.ofFailable(() -> Optional.ofNullable(tokensExpirationDates.get(accessToken)).orElseThrow(() -> new InvalidAccessToken(accessToken))));
}
}

Expand Down
Expand Up @@ -42,28 +42,28 @@ public void setUp() {
protected abstract AccessTokenRepository createAccessTokenRepository();

@Test
public void validTokenMustBeRetrieved() throws Exception {
public void validTokenMustBeRetrieved() throws Throwable {
accessTokenRepository.addToken(USERNAME, TOKEN);
assertThat(accessTokenRepository.getUsernameFromToken(TOKEN)).isEqualTo(USERNAME);
assertThat(accessTokenRepository.getUsernameFromToken(TOKEN).join().get()).isEqualTo(USERNAME);
}

@Test
public void absentTokensMustBeInvalid() throws Exception {
assertThatThrownBy(() -> accessTokenRepository.getUsernameFromToken(TOKEN)).isInstanceOf(InvalidAccessToken.class);
assertThatThrownBy(() -> accessTokenRepository.getUsernameFromToken(TOKEN).join().get()).isInstanceOf(InvalidAccessToken.class);
}

@Test
public void removedTokensMustBeInvalid() throws Exception {
accessTokenRepository.addToken(USERNAME, TOKEN);
accessTokenRepository.removeToken(TOKEN);
assertThatThrownBy(() -> accessTokenRepository.getUsernameFromToken(TOKEN)).isInstanceOf(InvalidAccessToken.class);
assertThatThrownBy(() -> accessTokenRepository.getUsernameFromToken(TOKEN).join().get()).isInstanceOf(InvalidAccessToken.class);
}

@Test
public void outDatedTokenMustBeInvalid() throws Exception {
accessTokenRepository.addToken(USERNAME, TOKEN);
Thread.sleep(2 * TTL_IN_MS);
assertThatThrownBy(() -> accessTokenRepository.getUsernameFromToken(TOKEN)).isInstanceOf(InvalidAccessToken.class);
assertThatThrownBy(() -> accessTokenRepository.getUsernameFromToken(TOKEN).join().get()).isInstanceOf(InvalidAccessToken.class);
}

@Test(expected = NullPointerException.class)
Expand Down
5 changes: 5 additions & 0 deletions server/pom.xml
Expand Up @@ -890,6 +890,11 @@
<artifactId>geronimo-activation_1.1_spec</artifactId>
<version>${geronimo-activation-spec.version}</version>
</dependency>
<dependency>
<groupId>com.jason-goodwin</groupId>
<artifactId>better-monads</artifactId>
<version>0.4.0</version>
</dependency>
<dependency>
<groupId>commons-daemon</groupId>
<artifactId>commons-daemon</artifactId>
Expand Down
Expand Up @@ -28,6 +28,7 @@
import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;

import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;

@Singleton
public class AccessTokenManagerImpl implements AccessTokenManager {
Expand All @@ -49,7 +50,13 @@ public AccessToken grantAccessToken(String username) {

@Override
public String getUsernameFromToken(AccessToken token) throws InvalidAccessToken {
return accessTokenRepository.getUsernameFromToken(token);
try {
return accessTokenRepository.getUsernameFromToken(token).join().get();
} catch (InvalidAccessToken invalidAccessToken) {
throw invalidAccessToken;
} catch (Throwable throwable) {
throw Throwables.propagate(throwable);
}
}

@Override
Expand All @@ -64,7 +71,7 @@ public boolean isValid(AccessToken token) throws InvalidAccessToken {

@Override
public void revoke(AccessToken token) {
accessTokenRepository.removeToken(token);
accessTokenRepository.removeToken(token).join();
}

}
Expand Up @@ -52,7 +52,7 @@ public void grantShouldGenerateATokenOnUsername() throws Exception {
@Test
public void grantShouldStoreATokenOnUsername() throws Exception {
AccessToken token = accessTokenManager.grantAccessToken("username");
assertThat(accessTokenRepository.getUsernameFromToken(token)).isEqualTo("username");
assertThat(accessTokenRepository.getUsernameFromToken(token).join().get()).isEqualTo("username");
}

@Test(expected=NullPointerException.class)
Expand Down

0 comments on commit 7e39d44

Please sign in to comment.