Skip to content
This repository has been archived by the owner on Jul 16, 2019. It is now read-only.

Commit

Permalink
RFES-13: adds session creation policy
Browse files Browse the repository at this point in the history
  • Loading branch information
jrummler committed Oct 8, 2015
1 parent a9ea4b0 commit e198853
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,22 @@
import org.echocat.marquardt.authority.domain.User;
import org.echocat.marquardt.authority.exceptions.CertificateCreationException;
import org.echocat.marquardt.authority.exceptions.ExpiredSessionException;
import org.echocat.marquardt.authority.persistence.SessionCreationPolicy;
import org.echocat.marquardt.authority.persistence.SessionStore;
import org.echocat.marquardt.authority.persistence.UserStore;
import org.echocat.marquardt.common.Signer;
import org.echocat.marquardt.common.domain.certificate.Certificate;
import org.echocat.marquardt.common.domain.Credentials;
import org.echocat.marquardt.common.keyprovisioning.KeyPairProvider;
import org.echocat.marquardt.common.domain.PublicKeyWithMechanism;
import org.echocat.marquardt.common.domain.certificate.Role;
import org.echocat.marquardt.common.domain.Signable;
import org.echocat.marquardt.common.domain.Signature;
import org.echocat.marquardt.common.domain.certificate.Certificate;
import org.echocat.marquardt.common.domain.certificate.Role;
import org.echocat.marquardt.common.exceptions.AlreadyLoggedInException;
import org.echocat.marquardt.common.exceptions.LoginFailedException;
import org.echocat.marquardt.common.exceptions.NoSessionFoundException;
import org.echocat.marquardt.common.exceptions.SignatureValidationFailedException;
import org.echocat.marquardt.common.exceptions.UserAlreadyExistsException;
import org.echocat.marquardt.common.keyprovisioning.KeyPairProvider;
import org.echocat.marquardt.common.util.DateProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -58,21 +59,23 @@ public class Authority<USER extends User<? extends Role>, SESSION extends Sessio

private final UserStore<USER, SIGNABLE> _userStore;
private final SessionStore<SESSION> _sessionStore;
private final SessionCreationPolicy _sessionCreationPolicy;
private final Signer _signer = new Signer();
private final KeyPairProvider _issuerKeyProvider;

private DateProvider _dateProvider = new DateProvider();

/**
* Sets up a new Authority singleton.
*
* @param userStore Your user store.
* @param userStore Your user store.
* @param sessionStore Your session store.
* @param sessionCreationPolicy How the authority decides wether to allow clients to create more than one session / or to black- or whitelist clients
* @param issuerKeyProvider KeyPairProvider of the authority. Public key should be trusted by the clients and services.
*/
public Authority(final UserStore<USER, SIGNABLE> userStore, final SessionStore<SESSION> sessionStore, final KeyPairProvider issuerKeyProvider) {
public Authority(final UserStore<USER, SIGNABLE> userStore, final SessionStore<SESSION> sessionStore, SessionCreationPolicy sessionCreationPolicy, final KeyPairProvider issuerKeyProvider) {
_userStore = userStore;
_sessionStore = sessionStore;
_sessionCreationPolicy = sessionCreationPolicy;
_issuerKeyProvider = issuerKeyProvider;
}

Expand All @@ -98,15 +101,15 @@ public byte[] signUp(final Credentials credentials) {
* @param credentials Credentials of the user with the client's public key.
* @return certificate for the client.
* @throws LoginFailedException If user does not exist or password does not match.
* @throws AlreadyLoggedInException If the user already has an active session for this client's public key.
* @throws AlreadyLoggedInException If the user is not allowed to obtain (another) session.
* @throws CertificateCreationException If there were problems creating the certificate.
*/
public byte[] signIn(final Credentials credentials) {
final USER user = _userStore.findByCredentials(credentials).orElseThrow(() -> new LoginFailedException("Login failed"));
if (user.passwordMatches(credentials.getPassword())) {
// create new session
final PublicKeyWithMechanism publicKeyWithMechanism = new PublicKeyWithMechanism(credentials.getPublicKey());
if (_sessionStore.existsActiveSession(user.getUserId(), publicKeyWithMechanism.getValue(), _dateProvider.now())) {
if (!_sessionCreationPolicy.mayCreateSession(user.getUserId(), publicKeyWithMechanism.getValue())) {
throw new AlreadyLoggedInException("User with id " + user.getUserId() + " is already logged in for current client.");
} else {
return createCertificateAndSession(credentials, user);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* echocat Marquardt Java SDK, Copyright (c) 2015 echocat
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

package org.echocat.marquardt.authority.persistence;

import java.util.UUID;

public interface SessionCreationPolicy {

/**
* You can use this to implement different policies how access to create a session is granted.
* This may be a one session per user per client policy, or maybe you want some clients to create more sessions than
* others.
*
* @param userId User id of the user.
* @param clientPublicKey Public key of the client.
* @return True if (another) session may be created, false if not.
*/
boolean mayCreateSession(UUID userId, byte[] clientPublicKey);

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.echocat.marquardt.authority.domain.Session;
import org.echocat.marquardt.authority.domain.User;
import org.echocat.marquardt.authority.exceptions.ExpiredSessionException;
import org.echocat.marquardt.authority.persistence.SessionCreationPolicy;
import org.echocat.marquardt.authority.persistence.SessionStore;
import org.echocat.marquardt.authority.persistence.UserStore;
import org.echocat.marquardt.common.domain.Credentials;
Expand Down Expand Up @@ -61,13 +62,13 @@ public abstract class SpringAuthorityController<USER extends User<? extends Role

/**
* Wire this with your stores.
*
* @param userStore Your user store implementation.
* @param userStore Your user store implementation.
* @param sessionStore Your session store implementation.
* @param sessionAccess
* @param issuerKeyProvider Your KeyPairProvider. The public key from this must be trusted by clients and services.
*/
public SpringAuthorityController(UserStore<USER, SIGNABLE> userStore, final SessionStore<SESSION> sessionStore, final KeyPairProvider issuerKeyProvider) {
_authority = new Authority<>(userStore, sessionStore, issuerKeyProvider);
public SpringAuthorityController(UserStore<USER, SIGNABLE> userStore, final SessionStore<SESSION> sessionStore, SessionCreationPolicy sessionAccess, final KeyPairProvider issuerKeyProvider) {
_authority = new Authority<>(userStore, sessionStore, sessionAccess, issuerKeyProvider);
}

@RequestMapping(value = "/signup", method = RequestMethod.POST)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,25 @@ public class AuthorityIntegrationTest extends AuthorityTest {

@Override
@Before
public void setup() throws Exception{
_testHttpAuthorityServer = new TestHttpAuthorityServer(getUserStore(), getSessionStore());
public void setup() throws Exception {
_testHttpAuthorityServer = new TestHttpAuthorityServer(getUserStore(), getSessionStore(), getSessionCreationPolicy());
_testHttpAuthorityServer.start();
super.setup();
}

@Test
public void shouldSignupUserWithCorrectCredentials() throws Exception {
givenSignupCall();
givenUserDoesNotExist();
givenSessionCreationPolicyAllowsAnotherSession();
givenSignupCall();
whenCallingAuthority();
thenSignedCertificateIsProduced();
}

@Test
public void shouldSigninUserWithCorrectCredentials() throws Exception {
givenUserExists();
givenSessionCreationPolicyAllowsAnotherSession();
givenSigninCall();
whenCallingAuthority();
thenSignedCertificateIsProduced();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

package org.echocat.marquardt.authority;

import org.echocat.marquardt.authority.persistence.SessionCreationPolicy;
import org.echocat.marquardt.authority.persistence.SessionStore;
import org.echocat.marquardt.authority.persistence.UserStore;
import org.echocat.marquardt.authority.testdomain.TestSession;
Expand All @@ -25,7 +26,6 @@
import java.util.concurrent.TimeUnit;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;

@SuppressWarnings("AbstractClassWithoutAbstractMethods")
Expand All @@ -44,6 +44,9 @@ public abstract class AuthorityTest {
@Mock
private SessionStore<TestSession> _sessionStore;

@Mock
private SessionCreationPolicy _sessionCreationPolicy;

@Before
public void setup() throws Exception {
when(getSessionStore().createTransient()).thenReturn(createTestSession());
Expand All @@ -65,14 +68,10 @@ protected void givenUserExists() {
}

protected void givenExistingSession() {
//noinspection UseOfObsoleteDateTimeApi
when(getSessionStore().existsActiveSession(any(UUID.class), any(byte[].class), any(Date.class))).thenReturn(true);
when(getSessionStore().findByCertificate(any(byte[].class))).thenReturn(Optional.of(getValidSession()));
}

protected void givenNoExistingSession() {
//noinspection UseOfObsoleteDateTimeApi
when(getSessionStore().existsActiveSession(eq(USER_ID), any(byte[].class), any(Date.class))).thenReturn(false);
when(getSessionStore().findByCertificate(any(byte[].class))).thenReturn(Optional.empty());
}

Expand All @@ -83,6 +82,10 @@ protected void givenUserDoesNotExist() {
when(getUserStore().createSignableFromUser(any(TestUser.class))).thenReturn(TEST_USER_INFO);
}

protected void givenSessionCreationPolicyAllowsAnotherSession() {
when(getSessionCreationPolicy().mayCreateSession(any(UUID.class), any(byte[].class))).thenReturn(true);
}

protected TestSession getValidSession() {
return _validSession;
}
Expand All @@ -98,4 +101,8 @@ protected UserStore<TestUser, TestUserInfo> getUserStore() {
protected SessionStore<TestSession> getSessionStore() {
return _sessionStore;
}

public SessionCreationPolicy getSessionCreationPolicy() {
return _sessionCreationPolicy;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public void shouldThrowExceptionWhenUserAlreadyExistsWhenSignUp() throws Excepti
public void shouldSigninUser() throws Exception {
givenUserExists();
givenNoExistingSession();
givenSessionCreationPolicyAllowsAnotherSession();
whenSigningIn();
thenSessionIsCreated();
thenCertificateIsMade();
Expand All @@ -93,6 +94,7 @@ public void shouldSigninUser() throws Exception {
public void shouldThrowCerificateCreationFailedExceptionWhenSigningInButPayloadCannotBeSigned() throws Exception {
givenUserExists();
givenNoExistingSession();
givenSessionCreationPolicyAllowsAnotherSession();
givenSignableThrowingException();
whenSigningIn();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import org.echocat.marquardt.authority.persistence.SessionCreationPolicy;
import org.echocat.marquardt.authority.persistence.SessionStore;
import org.echocat.marquardt.authority.persistence.UserStore;
import org.echocat.marquardt.authority.testdomain.TestSession;
Expand Down Expand Up @@ -41,10 +42,10 @@ public class TestHttpAuthorityServer {
private final Authority<TestUser, TestSession, TestUserInfo> _authority;
private final Signature _signature = mock(Signature.class);

public TestHttpAuthorityServer(final UserStore<TestUser, TestUserInfo> userStore, final SessionStore<TestSession> sessionStore) throws IOException {
public TestHttpAuthorityServer(final UserStore<TestUser, TestUserInfo> userStore, final SessionStore<TestSession> sessionStore, SessionCreationPolicy sessionAccess) throws IOException {
_server = HttpServer.create(new InetSocketAddress(8000), 0);
_objectMapper = new ObjectMapper();
_authority = new Authority<>(userStore, sessionStore, TestKeyPairProvider.create());
_authority = new Authority<>(userStore, sessionStore, sessionAccess, TestKeyPairProvider.create());
when(_signature.isValidFor(any(), any())).thenReturn(true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

package org.echocat.marquardt.example;

import org.echocat.marquardt.authority.persistence.SessionCreationPolicy;
import org.echocat.marquardt.authority.persistence.SessionStore;
import org.echocat.marquardt.authority.persistence.UserStore;
import org.echocat.marquardt.authority.spring.SpringAuthorityController;
Expand All @@ -25,9 +26,8 @@
public class ExampleAuthorityController extends SpringAuthorityController<PersistentUser, PersistentSession, UserInfo, UserCredentials, UserCredentials> {

@Autowired
public ExampleAuthorityController(final SessionStore<PersistentSession> sessionStore,
final KeyPairProvider issuerKeyProvider,
final UserStore<PersistentUser, UserInfo> userStore) {
super(userStore, sessionStore, issuerKeyProvider);
public ExampleAuthorityController(final UserStore<PersistentUser, UserInfo> userStore, final SessionStore<PersistentSession> sessionStore, SessionCreationPolicy sessionCreationPolicy,
final KeyPairProvider issuerKeyProvider) {
super(userStore, sessionStore, sessionCreationPolicy, issuerKeyProvider);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* echocat Marquardt Java SDK, Copyright (c) 2015 echocat
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

package org.echocat.marquardt.example.persistence;

import org.echocat.marquardt.authority.persistence.SessionCreationPolicy;
import org.echocat.marquardt.authority.persistence.SessionStore;
import org.echocat.marquardt.common.util.DateProvider;
import org.echocat.marquardt.example.domain.PersistentSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Service
public class OneSessionPerClientPolicy implements SessionCreationPolicy {

private final SessionStore<PersistentSession> _sessionStore;
private DateProvider _dateProvider;

@Autowired
public OneSessionPerClientPolicy(SessionStore<PersistentSession> sessionStore) {
_sessionStore = sessionStore;
_dateProvider = new DateProvider();
}

@Override
public boolean mayCreateSession(UUID userId, byte[] clientPublicKey) {
return !_sessionStore.existsActiveSession(userId, clientPublicKey, _dateProvider.now());
}

public void setDateProvider(DateProvider dateProvider) {
_dateProvider = dateProvider;
}
}

0 comments on commit e198853

Please sign in to comment.