Skip to content

Commit

Permalink
Merge pull request #11 from floralvikings/release/0.3.0-alpha
Browse files Browse the repository at this point in the history
Release/0.3.0 alpha
  • Loading branch information
floralvikings committed Dec 15, 2015
2 parents 663a7ee + 9bb4461 commit f65a8cf
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 4 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ plugins {

description = 'Jenjin Core IO API'
group = 'com.jenjinstudios'
version = '0.2.0-alpha'
version = '0.3.0-alpha'

// Core plugins
apply plugin: 'java'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.jenjinstudios.io.authentication;

/**
* Thrown when there is an unexpected error while authenticating.
*
* @author Caleb Brinkman
*/
public class AuthenticationException extends Exception
{
/**
* Construct a new AuthenticationException with the given message and cause.
*
* @param message The message.
* @param cause The cause.
*/
public AuthenticationException(String message, Throwable cause) { super(message, cause); }

/**
* Construct a new AuthenticationException with the given message.
* @param message The message.
*/
public AuthenticationException(String message) {super(message);}
}
166 changes: 166 additions & 0 deletions src/main/java/com/jenjinstudios/io/authentication/Authenticator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package com.jenjinstudios.io.authentication;

import com.jenjinstudios.io.ExecutionContext;

import java.util.Collections;
import java.util.Map;

/**
* Interface defining several methods that are useful for authentication.
*
* @author Caleb Brinkman
*/
public interface Authenticator<T extends ExecutionContext>
{
/** This constant is provided an a convenience, to specify a "password" credential in a {@code Map}. */
String PASSWORD = "jio-password";
/** This constant is provided an a convenience, to specify a "verification code" credential in a {@code Map}. */
String VERIFICATION_CODE = "jio-verification-code";

/**
* Determine whether the user specified by the given unique identifier is authenticated. Behavior when the user
* doesn't exist is implementation-dependent.
*
* @param id The unique user identifier.
*
* @return Whether the user is authenticated.
*
* @throws AuthenticationException If there is an exception when determining authentication status.
*/
boolean isAuthenticated(String id) throws AuthenticationException;

/**
* Determine whether the user specified by the given unique identifier exists.
*
* @param id The unique identifier of the user.
*
* @return Whether the user with the given identifier exists.
*
* @throws AuthenticationException If there is an exception when determining if the user exists/
*/
boolean userExists(String id) throws AuthenticationException;

/**
* Determine whether the given credentials are valid for the user with the specified unique identifier. Behavior
* when the user doesn't exist is implementation-dependent.
*
* @param id The unique user identifier.
* @param credentials A map of credentials containing key-value pairs, where the key is the <b>name</b> of
* credential and the value is the <b>plaintext</b> credential. Several constants that may be helpful key
* choices are provided by this interface.
*
* @return Whether the given credentials are valid.
*
* @throws AuthenticationException If there is an exception when determining the validity of the credentials.
*/
boolean credentialsValid(String id, Map<String, String> credentials) throws AuthenticationException;

/**
* Determine whether the given password is valid. By default, this method assumes that the {@link #PASSWORD}
* constant is the key used for storing passwords in the credentials Map.
*
* @param id The unique user identifier.
* @param password The plaintext password.
*
* @return Whether the password is valid.
*
* @throws AuthenticationException If there is an exception when determining password validity.
*/
default boolean passwordValid(String id, String password) throws AuthenticationException {
return credentialsValid(id, Collections.singletonMap(PASSWORD, password));
}

/**
* Authenticate the user with the given unique id and valid credentials and, if successful, populating the given
* ExecutionContext with the user data from the backing data store. A suggested method behavior is:
* <pre>
* {@code boolean canLogin =
* this.userExists(id) && !this.isAuthenticated(id) && this.credentialsValid(id,credentials);
* if (canLogin) {
* populate(context, id);
* }
* return canLogin;
* }
* </pre>
*
* @param context The context to be populated with user data if authentication is successful. <b>This object
* should not be modified if the authentication was unsuccessful.</b>
* @param id The unique user identifier.
* @param credentials A map of credentials containing key-value pairs, where the key is the <b>name</b> of
* credential and the value is the <b>plaintext</b> credential. Several constants that may be helpful key
* choices are provided by this interface.
*
* @return Whether the user was successfully authenticated. If the user was not successfully authenticated, it is
* imperative that the {@code context} parameter not be modified.
*
* @throws AuthenticationException If there is an exception when authenticating the user.
*/
boolean authenticate(T context, String id, Map<String, String> credentials) throws AuthenticationException;

/**
* Authenticate the user with the given unique id and valid password and, if successful, populating the given
* ExecutionContext with the user data from the backing data store. By default, this method assumes that the
* {@link #PASSWORD} constant is the key used for storing passwords in the credentials Map.
*
* @param context The context to be populated with user data if authentication is successful. <b>This object
* should not be modified if the authentication was unsuccessful.</b>
* @param id The unique user identifier.
* @param password The plaintext password.
*
* @return Whether the user was successfully authenticated. If the user was not successfully authenticated, it is
* imperative that the {@code context} parameter not be modified.
*
* @throws AuthenticationException If there is an exception when authenticating the user.
*/
default boolean authenticate(T context, String id, String password) throws AuthenticationException {
return authenticate(context, id, Collections.singletonMap(PASSWORD, password));
}

/**
* Unauthenticate the user with the given unique id, restoring the given context to an unauthenticated state.
*
* @param context The context for the user to be unauthenticated; should
* @param id The unique user identifier.
*
* @return Whether the user was successfully unauthenticated and the context was modified.
*
* @throws AuthenticationException If there is an exception during unauthentication.
*/
boolean unauthenticate(T context, String id) throws AuthenticationException;

/**
* Populate the given ExecutionContext with data for the user with the given unique identifier.
* <p>
* <blockquote>
* <b>Note:</b> This method should not attempt any sort of authentication; credentials are not provided, it exists
* for the sole purpose of retrieving and populating user data. For authentication, use the {@link
* #credentialsValid(String, Map)}, {@link #authenticate(ExecutionContext, String, Map)}, and {@link
* #authenticate(ExecutionContext, String, String)} methods.
* </blockquote>
*
* @param context The context to populate with user data.
* @param id The unique user identifier.
*
* @throws AuthenticationException If there is an exception when populating the user data.
*/
void populate(T context, String id) throws AuthenticationException;

/**
* Update the backing data store, for the user with the given id, with data from the given context.
*
* @param context The context containing data to be placed in the backing data store.
* @param id The unique user id.
*
* @return {@code true} If the update was successful <b>and</b> the backing data store was modified.
*
* @throws AuthenticationException If there is an exception during the update.
*/
boolean update(T context, String id) throws AuthenticationException;

/**
* Close the backing database connection.
*
* @throws AuthenticationException If there is an exception when closing the backing database connection.
*/
void close() throws AuthenticationException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public void run() {
} else {
LOGGER.warn("Encountered error from MessageQueue", t);
}
LOGGER.debug("Invoking error callback function");
errorCallback.accept(t);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.jenjinstudios.io.ExecutionContext;
import com.jenjinstudios.io.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.List;
Expand All @@ -14,6 +16,7 @@
*/
public class ExecutionTask<T extends ExecutionContext> implements Runnable
{
private static final Logger LOGGER = LoggerFactory.getLogger(ExecutionTask.class);
private final MessageQueue messageQueue;
private final T executionContext;
private final Collection<Consumer<T>> contextualTasks;
Expand All @@ -36,11 +39,19 @@ public ExecutionTask(MessageQueue messageQueue, T executionContext, Collection<C
public void run() {
final List<Message> incoming = messageQueue.getIncomingAndClear();
incoming.forEach(message -> {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Executing message (Type: {})", message.getClass().getName());
}
Message response = message.execute(executionContext);
if (response != null) {
messageQueue.queueOutgoing(response);
}
contextualTasks.forEach(consumer -> consumer.accept(executionContext));
contextualTasks.forEach(consumer -> {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Executing contextual task: {}", consumer);
}
consumer.accept(executionContext);
});
});
}
}
3 changes: 3 additions & 0 deletions src/main/java/com/jenjinstudios/io/concurrency/ReadTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public void run() {
try {
if (noError) {
final Message message = messageReader.read();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Read Message (Type: {})", message.getClass().getName());
}
messageQueue.messageReceived(message);
}
} catch (IOException e) {
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/jenjinstudios/io/concurrency/WriteTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public void run() {
final List<Message> outgoing = messageQueue.getOutgoingAndClear();
outgoing.forEach(message -> {
try {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Attempting to write message. (Type: {})" + message.getClass().getName());
}
messageWriter.write(message);
} catch (IOException e) {
messageQueue.errorEncountered(e);
Expand Down
6 changes: 4 additions & 2 deletions src/test/groovy/com/jenjinstudios/io/server/ServerSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public class ServerSpec extends Specification {
given: "A ServerSocket which returns a mocked connection"
def serverSocket = Mock(ServerSocket)
def socket = Mock(Socket)
serverSocket.accept() >>> [socket, { while (true); }]
serverSocket.accept() >> { args -> socket } >> m_block
def connection = Mock(Connection)
def connectionBuilder = Mock(MultiConnectionBuilder)
connectionBuilder.build(socket) >> connection
Expand Down Expand Up @@ -170,4 +170,6 @@ public class ServerSpec extends Specification {
1 * connection1.sendMessage(message)
1 * connection2.sendMessage(message)
}
}
def m_block = { while (true); }
}

0 comments on commit f65a8cf

Please sign in to comment.