Last active
August 29, 2015 14:02
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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