Skip to content

Commit

Permalink
JAMES-1718 add an implementation for AccessTokenRepository
Browse files Browse the repository at this point in the history
  • Loading branch information
chibenwa committed Apr 15, 2016
1 parent 223dd81 commit e344967
Show file tree
Hide file tree
Showing 13 changed files with 426 additions and 80 deletions.
Expand Up @@ -21,6 +21,7 @@

import org.apache.james.mailbox.cassandra.CassandraId;
import org.apache.james.modules.data.CassandraDomainListModule;
import org.apache.james.modules.data.CassandraJmapModule;
import org.apache.james.modules.data.CassandraRecipientRewriteTableModule;
import org.apache.james.modules.data.CassandraSieveRepositoryModule;
import org.apache.james.modules.data.CassandraUsersRepositoryModule;
Expand All @@ -43,6 +44,7 @@ public class CassandraJamesServerMain {
new CassandraDomainListModule(),
new CassandraRecipientRewriteTableModule(),
new CassandraSieveRepositoryModule(),
new CassandraJmapModule(),
new CassandraMailboxModule(),
new CassandraSessionModule(),
new ElasticSearchMailboxModule(),
Expand Down
@@ -0,0 +1,39 @@
/****************************************************************
* 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.james.modules.data;

import org.apache.james.backends.cassandra.components.CassandraModule;
import org.apache.james.jmap.api.access.AccessTokenRepository;
import org.apache.james.jmap.cassandra.access.CassandraAccessModule;
import org.apache.james.jmap.cassandra.access.CassandraAccessTokenRepository;

import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;

public class CassandraJmapModule extends AbstractModule {

@Override
protected void configure() {
bind(AccessTokenRepository.class).to(CassandraAccessTokenRepository.class);

Multibinder<CassandraModule> cassandraDataDefinitions = Multibinder.newSetBinder(binder(), CassandraModule.class);
cassandraDataDefinitions.addBinding().to(CassandraAccessModule.class);
}
}
Expand Up @@ -28,7 +28,6 @@
import org.apache.james.jmap.crypto.JamesSignatureHandler;
import org.apache.james.jmap.crypto.SignatureHandler;
import org.apache.james.jmap.crypto.SignedContinuationTokenManager;
import org.apache.james.jmap.memory.access.MemoryAccessTokenRepository;
import org.apache.james.jmap.send.MailFactory;
import org.apache.james.jmap.send.MailSpool;
import org.apache.james.jmap.utils.DefaultZonedDateTimeProvider;
Expand All @@ -51,7 +50,6 @@ protected void configure() {
bind(ContinuationTokenManager.class).to(SignedContinuationTokenManager.class);

bindConstant().annotatedWith(Names.named(AccessTokenRepository.TOKEN_EXPIRATION_IN_MS)).to(DEFAULT_TOKEN_EXPIRATION_IN_MS);
bind(AccessTokenRepository.class).to(MemoryAccessTokenRepository.class);
bind(AccessTokenManager.class).to(AccessTokenManagerImpl.class);

bind(MailSpool.class).in(Singleton.class);
Expand Down
Expand Up @@ -21,6 +21,8 @@

import org.apache.james.domainlist.api.DomainList;
import org.apache.james.domainlist.memory.MemoryDomainList;
import org.apache.james.jmap.api.access.AccessTokenRepository;
import org.apache.james.jmap.memory.access.MemoryAccessTokenRepository;
import org.apache.james.rrt.api.RecipientRewriteTable;
import org.apache.james.rrt.memory.MemoryRecipientRewriteTable;
import org.apache.james.sieverepository.api.SieveRepository;
Expand Down Expand Up @@ -56,6 +58,9 @@ protected void configure() {
bind(SieveFileRepository.class).in(Scopes.SINGLETON);
bind(SieveRepository.class).to(SieveFileRepository.class);

bind(MemoryAccessTokenRepository.class).in(Scopes.SINGLETON);
bind(AccessTokenRepository.class).to(MemoryAccessTokenRepository.class);

Multibinder.newSetBinder(binder(), ConfigurationPerformer.class).addBinding().to(MemoryDataConfigurationPerformer.class);
}

Expand Down
10 changes: 10 additions & 0 deletions server/data/data-cassandra/pom.xml
Expand Up @@ -177,6 +177,16 @@
<groupId>${project.groupId}</groupId>
<artifactId>james-server-data-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.james</groupId>
<artifactId>james-server-data-jmap</artifactId>
</dependency>
<dependency>
<groupId>org.apache.james</groupId>
<artifactId>james-server-data-jmap</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>james-server-data-library</artifactId>
Expand Down
@@ -0,0 +1,67 @@
/****************************************************************
* 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.james.jmap.cassandra.access;

import static com.datastax.driver.core.DataType.text;
import static com.datastax.driver.core.DataType.uuid;

import java.util.List;

import org.apache.james.backends.cassandra.components.CassandraIndex;
import org.apache.james.backends.cassandra.components.CassandraModule;
import org.apache.james.backends.cassandra.components.CassandraTable;
import org.apache.james.backends.cassandra.components.CassandraType;
import org.apache.james.jmap.cassandra.access.table.CassandraAccessTokenTable;

import com.datastax.driver.core.schemabuilder.SchemaBuilder;
import com.google.common.collect.ImmutableList;

public class CassandraAccessModule implements CassandraModule {

private final List<CassandraTable> tables;
private final List<CassandraIndex> index;
private final List<CassandraType> types;

public CassandraAccessModule() {
tables = ImmutableList.of(
new CassandraTable(CassandraAccessTokenTable.TABLE_NAME,
SchemaBuilder.createTable(CassandraAccessTokenTable.TABLE_NAME)
.ifNotExists()
.addPartitionKey(CassandraAccessTokenTable.TOKEN, uuid())
.addColumn(CassandraAccessTokenTable.USERNAME, text())));
index = ImmutableList.of();
types = ImmutableList.of();
}

@Override
public List<CassandraTable> moduleTables() {
return tables;
}

@Override
public List<CassandraIndex> moduleIndex() {
return index;
}

@Override
public List<CassandraType> moduleTypes() {
return types;
}
}
@@ -0,0 +1,113 @@
/****************************************************************
* 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.james.jmap.cassandra.access;

import static com.datastax.driver.core.querybuilder.QueryBuilder.bindMarker;
import static com.datastax.driver.core.querybuilder.QueryBuilder.delete;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
import static com.datastax.driver.core.querybuilder.QueryBuilder.ttl;

import java.util.Optional;

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

import org.apache.james.backends.cassandra.utils.CassandraConstants;
import org.apache.james.jmap.api.access.AccessToken;
import org.apache.james.jmap.api.access.AccessTokenRepository;
import org.apache.james.jmap.api.access.exceptions.AccessTokenAlreadyStored;
import org.apache.james.jmap.api.access.exceptions.InvalidAccessToken;
import org.apache.james.jmap.cassandra.access.table.CassandraAccessTokenTable;

import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.Session;
import com.google.common.base.Preconditions;

public class CassandraAccessTokenRepository implements AccessTokenRepository {

private static final String TTL = "ttl";

private final Session session;
private final PreparedStatement removeStatement;
private final PreparedStatement insertStatement;
private final PreparedStatement selectStatement;
private final int durationInSeconds;

@Inject
public CassandraAccessTokenRepository(Session session, @Named(TOKEN_EXPIRATION_IN_MS) long durationInMilliseconds) {
this.session = session;
this.durationInSeconds = (int) (durationInMilliseconds / 1000);

this.removeStatement = this.session.prepare(delete()
.from(CassandraAccessTokenTable.TABLE_NAME)
.where(eq(CassandraAccessTokenTable.TOKEN, bindMarker(CassandraAccessTokenTable.TOKEN))));

this.insertStatement = this.session.prepare(insertInto(CassandraAccessTokenTable.TABLE_NAME)
.ifNotExists()
.value(CassandraAccessTokenTable.TOKEN, bindMarker(CassandraAccessTokenTable.TOKEN))
.value(CassandraAccessTokenTable.USERNAME, bindMarker(CassandraAccessTokenTable.USERNAME))
.using(ttl(bindMarker(TTL))));

this.selectStatement = this.session.prepare(select()
.from(CassandraAccessTokenTable.TABLE_NAME)
.where(eq(CassandraAccessTokenTable.TOKEN, bindMarker(CassandraAccessTokenTable.TOKEN))));
}

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

boolean applied = session.execute(insertStatement.bind()
.setUUID(CassandraAccessTokenTable.TOKEN, accessToken.getToken())
.setString(CassandraAccessTokenTable.USERNAME, username)
.setInt(TTL, durationInSeconds))
.one()
.getBool(CassandraConstants.LIGHTWEIGHT_TRANSACTION_APPLIED);

if (!applied) {
throw new AccessTokenAlreadyStored(accessToken);
}
}

@Override
public void removeToken(AccessToken accessToken) {
Preconditions.checkNotNull(accessToken);

session.execute(removeStatement.bind()
.setUUID(CassandraAccessTokenTable.TOKEN, accessToken.getToken()));
}

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

return Optional.ofNullable(
session.execute(
selectStatement.bind()
.setUUID(CassandraAccessTokenTable.TOKEN, accessToken.getToken()))
.one())
.map(row -> row.getString(CassandraAccessTokenTable.USERNAME))
.orElseThrow(() -> new InvalidAccessToken(accessToken));
}
}
@@ -0,0 +1,29 @@
/****************************************************************
* 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.james.jmap.cassandra.access.table;

public interface CassandraAccessTokenTable {

String TABLE_NAME = "access_token";

String TOKEN = "access_token";
String USERNAME = "username";

}
@@ -0,0 +1,41 @@
/****************************************************************
* 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.james.jmap.cassandra.access;

import org.apache.james.backends.cassandra.CassandraCluster;
import org.apache.james.jmap.api.access.AbstractAccessTokenRepositoryTest;
import org.apache.james.jmap.api.access.AccessTokenRepository;
import org.junit.After;

public class CassandraAccessTokenRepositoryTest extends AbstractAccessTokenRepositoryTest {

private CassandraCluster cassandra;

@Override
protected AccessTokenRepository createAccessTokenRepository() {
cassandra = CassandraCluster.create(new CassandraAccessModule());
return new CassandraAccessTokenRepository(cassandra.getConf(), TTL_IN_MS);
}

@After
public void tearDown() {
cassandra.clearAllTables();
}
}
Expand Up @@ -48,6 +48,10 @@ public String serialize() {
return token.toString();
}

public UUID getToken() {
return token;
}

@Override
public boolean equals(Object o) {
return o != null
Expand Down

0 comments on commit e344967

Please sign in to comment.