Skip to content

Instantly share code, notes, and snippets.

@jnorthrup
Last active August 29, 2015 14:02
Show Gist options
  • Save jnorthrup/84c5e03c8f6dfb1258c3 to your computer and use it in GitHub Desktop.
Save jnorthrup/84c5e03c8f6dfb1258c3 to your computer and use it in GitHub Desktop.
sample authfilter code. if url is /app and session cookie is not in the database, send 303 to /auth.
package anychime.auth.server;
import one.xio.AsioVisitor;
import one.xio.HttpHeaders;
import one.xio.HttpMethod;
import one.xio.HttpStatus;
import rxf.core.Config;
import rxf.core.Rfc822HeaderState;
import rxf.core.Server;
import rxf.couch.gen.CouchDriver;
import rxf.shared.PreRead;
import rxf.web.inf.*;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.nio.channels.SelectionKey.OP_READ;
import static java.nio.channels.SelectionKey.OP_WRITE;
import static one.xio.HttpMethod.GET;
import static one.xio.HttpMethod.POST;
import static rxf.core.CouchNamespace.NAMESPACE;
/**
* Created by jim on 6/13/14.
*/
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)
@interface SecureScope {
String AUTH = "/auth";
String value() default "SSID";
String authPath() default AUTH;
String authDb() default "session";
}
/**
* Created by jim on 5/24/14.
*/
@SecureScope
class CRCI extends ContentRootCacheImpl {
}
@SecureScope
class CRNCI extends ContentRootNoCacheImpl {
}
@SecureScope
class CRI extends ContentRootImpl {
}
public class AuthFilter {
/**
* duplicated here but just in case...
*/
static ProtocolMethodDispatch ignored = new ProtocolMethodDispatch();
/**
* the PUT protocol handlers, only static for the sake of javadocs
*/
public static Map<Pattern, Class<? extends AsioVisitor.Impl>> POSTmap =
new LinkedHashMap<>();
/**
* the GET protocol handlers, only static for the sake of javadocs
*/
public static Map<Pattern, Class<? extends AsioVisitor.Impl>> GETmap =
new LinkedHashMap<>();
static {
NAMESPACE.put(POST, POSTmap);
NAMESPACE.put(GET, GETmap);
/**
* general purpose httpd static content couch that recognizes .gz and other compression suffixes when convenient
*
* any random config mechanism with a default will suffice here to define the content root.
*
* widest regex last intentionally
* system proprty: {value #RXF_CONTENT_ROOT}
*/
GETmap.put(Pattern.compile("/app/.*cache.*"), CRCI.class);
GETmap.put(Pattern.compile("/app/.*nocache.*"), CRNCI.class);
GETmap.put(Pattern.compile("/app/.*"), CRI.class);
GETmap.put(ContentRootCacheImpl.CACHE_PATTERN, ContentRootCacheImpl.class);
GETmap.put(ContentRootNoCacheImpl.NOCACHE_PATTERN, ContentRootNoCacheImpl.class);
GETmap.put(Pattern.compile(".*"), ContentRootImpl.class);
}
public static final int PORT = Integer.parseInt(Config.get("PORT", String.valueOf(8888)));
public static void main(final String[] args) {
final ExecutorService executorService = Executors.newCachedThreadPool();
final AsioVisitor.Impl protocoldecoder = new AsioVisitor.Impl() {
@Override
public void onAccept(SelectionKey key) throws Exception {
ServerSocketChannel c = (ServerSocketChannel) key.channel();
SocketChannel accept = c.accept();
accept.configureBlocking(false);
Server.enqueue(accept, OP_READ, key.attachment());
}
@Override
public void onRead(final SelectionKey key) throws Exception {
final SocketChannel channel = (SocketChannel) key.channel();
final ByteBuffer cursor = ByteBuffer.allocateDirect(4 << 10);
int read = channel.read(cursor);
if (-1 == read) {
((SocketChannel) key.channel()).socket().close();//cancel();
return;
}
HttpMethod method = null;
final Rfc822HeaderState.HttpRequest httpRequest = (Rfc822HeaderState.HttpRequest) new Rfc822HeaderState().$req().cookieInterest("anychime").read((ByteBuffer) cursor.flip());
String method1 = httpRequest.method();
method = HttpMethod.valueOf(method1);
if (null != method) {
final HttpMethod finalMethod = method;
key.interestOps(0);
Future<?> submit = executorService.submit(new Runnable() {
private Runnable success;
public void run() {
try {
Set<Map.Entry<Pattern, Class<? extends Impl>>> entries;
Map<HttpMethod, Map<Pattern, Class<? extends Impl>>> namespace = NAMESPACE;
Map<Pattern, Class<? extends Impl>> patternClassMap = namespace.get(finalMethod);
entries = patternClassMap.entrySet();
String path = httpRequest.path();
for (Map.Entry<Pattern, Class<? extends Impl>> visitorEntry : entries) {
Matcher matcher = visitorEntry.getKey().matcher(path);
if (matcher.find()) {
Class<? extends Impl> visitorClaz = visitorEntry.getValue();
SecureScope annotation = visitorClaz.getAnnotation(SecureScope.class);
if (null != annotation) {
if (authDetour(annotation.value(), annotation.authPath(), annotation.authDb()))
return;
}
Impl impl = visitorClaz.newInstance();
key.interestOps(SelectionKey.OP_READ).attach(new Object[]{impl, httpRequest, cursor});
if (visitorClaz.isAnnotationPresent(PreRead.class))
impl.onRead(key);
return;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
boolean authDetour(String cookieName, String url, String authDb) {
//session records with valid HEAD are valid. deletion==invalidation
String confirmed = null;
String session = httpRequest.getCookie(cookieName);
if (null != session)
confirmed = new CouchDriver.RevisionFetch().db(authDb).docId(session).to().fire().json();
if (null == confirmed)
if (true) {
Rfc822HeaderState.HttpResponse httpResponse = new Rfc822HeaderState().$res();
httpResponse.status(HttpStatus.$303).headerString(HttpHeaders.Location, url);
success = new Runnable() {
@Override
public void run() {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
key.interestOps(0);
Server.enqueue(channel, OP_WRITE,
new FinishWrite(httpResponse.as(ByteBuffer.class), success));
return true;
}
return false;
}
});
} else {
key.cancel(); //cancel();
}
}
};
executorService.submit(new Runnable() {
@Override
public void run() {
try {
Server.init(protocoldecoder);
} catch (IOException e) {
e.printStackTrace();
}
}
});
try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open().bind(new InetSocketAddress(PORT))) {
Server.enqueue(serverSocketChannel, SelectionKey.OP_ACCEPT);
synchronized (args) {
args.wait();
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment