diff --git a/src/java/com/threerings/presents/annotation/AuthInvoker.java b/src/java/com/threerings/presents/annotation/AuthInvoker.java new file mode 100644 index 000000000..eee60e404 --- /dev/null +++ b/src/java/com/threerings/presents/annotation/AuthInvoker.java @@ -0,0 +1,43 @@ +// +// $Id$ +// +// Narya library - tools for developing networked games +// Copyright (C) 2002-2008 Three Rings Design, Inc., All Rights Reserved +// http://www.threerings.net/code/narya/ +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.threerings.presents.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.google.inject.BindingAnnotation; + +import com.samskivert.util.Invoker; + +/** + * An annotation that identifies the invoker on which we do client authentication. This would + * generally only be used to bind the auth invoker to a different invoker than the default (which + * is the main invoker). + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.PARAMETER }) +@BindingAnnotation +public @interface AuthInvoker +{ +} diff --git a/src/java/com/threerings/presents/peer/server/PeerAuthenticator.java b/src/java/com/threerings/presents/peer/server/PeerAuthenticator.java index 21a84807d..ef12b9465 100644 --- a/src/java/com/threerings/presents/peer/server/PeerAuthenticator.java +++ b/src/java/com/threerings/presents/peer/server/PeerAuthenticator.java @@ -31,6 +31,7 @@ import com.threerings.presents.net.AuthRequest; import com.threerings.presents.net.AuthResponse; import com.threerings.presents.net.AuthResponseData; import com.threerings.presents.server.Authenticator; +import com.threerings.presents.server.ChainedAuthenticator; import com.threerings.presents.server.net.AuthingConnection; import com.threerings.presents.peer.net.PeerCreds; @@ -41,33 +42,20 @@ import static com.threerings.presents.Log.log; * Handles authentication of peer servers and passes non-peer authentication requests through to a * normal authenticator. */ -public class PeerAuthenticator extends Authenticator +public class PeerAuthenticator extends ChainedAuthenticator { - /** - * Creates an authenticator that will handle peer authentications and pass non-peer - * authentications through to the supplied delegate. - */ - public PeerAuthenticator (PeerManager nodemgr, Authenticator delegate) + public PeerAuthenticator (PeerManager nodemgr) { _peermgr = nodemgr; - _delegate = delegate; } - @Override - public void authenticateConnection (AuthingConnection conn) + @Override // from abstract ChainedAuthenticator + protected boolean shouldHandleConnection (AuthingConnection conn) { - // if this is a peer server, we check their credentials specially - AuthRequest req = conn.getAuthRequest(); - if (req.getCredentials() instanceof PeerCreds) { - super.authenticateConnection(conn); - - } else { - // otherwise pass the request on to our delegate - _delegate.authenticateConnection(conn); - } + return (conn.getAuthRequest().getCredentials() instanceof PeerCreds); } - // from abstract Authenticator + @Override // from abstract Authenticator protected void processAuthentication (AuthingConnection conn, AuthResponse rsp) throws PersistenceException { diff --git a/src/java/com/threerings/presents/peer/server/PeerManager.java b/src/java/com/threerings/presents/peer/server/PeerManager.java index b3d1a840d..743002c37 100644 --- a/src/java/com/threerings/presents/peer/server/PeerManager.java +++ b/src/java/com/threerings/presents/peer/server/PeerManager.java @@ -219,7 +219,7 @@ public class PeerManager _sharedSecret = sharedSecret; // wire ourselves into the server - _conmgr.setAuthenticator(new PeerAuthenticator(this, _conmgr.getAuthenticator())); + _conmgr.addChainedAuthenticator(new PeerAuthenticator(this)); _clmgr.setClientFactory(new PeerClientFactory(this, _clmgr.getClientFactory())); // create our node object @@ -840,6 +840,7 @@ public class PeerManager PeerNode peer = _peers.get(record.nodeName); if (peer == null) { _peers.put(record.nodeName, peer = createPeerNode(record)); + peer.init(this, _omgr, record); } peer.refresh(record); } @@ -943,7 +944,7 @@ public class PeerManager */ protected PeerNode createPeerNode (NodeRecord record) { - return new PeerNode(this, record); + return new PeerNode(); } /** diff --git a/src/java/com/threerings/presents/peer/server/PeerNode.java b/src/java/com/threerings/presents/peer/server/PeerNode.java index 55396ddcc..a8c269167 100644 --- a/src/java/com/threerings/presents/peer/server/PeerNode.java +++ b/src/java/com/threerings/presents/peer/server/PeerNode.java @@ -30,6 +30,7 @@ import com.threerings.presents.client.BlockingCommunicator; import com.threerings.presents.client.Client; import com.threerings.presents.client.ClientObserver; import com.threerings.presents.client.Communicator; +import com.threerings.presents.server.PresentsDObjectMgr; import com.threerings.presents.server.PresentsServer; import com.threerings.presents.dobj.AttributeChangeListener; @@ -55,11 +56,12 @@ public class PeerNode /** This peer's node object. */ public NodeObject nodeobj; - public PeerNode (PeerManager peermgr, NodeRecord record) + public void init (PeerManager peermgr, PresentsDObjectMgr omgr, NodeRecord record) { _peermgr = peermgr; + _omgr = omgr; _record = record; - _client = new Client(null, PresentsServer.omgr) { + _client = new Client(null, _omgr) { protected void convertFromRemote (DObject target, DEvent event) { super.convertFromRemote(target, event); // rewrite the event's target oid using the oid currently configured on the @@ -68,7 +70,7 @@ public class PeerNode event.setTargetOid(target.getOid()); // assign an eventId to this event so that our stale event detection code can // properly deal with it - event.eventId = PresentsServer.omgr.getNextEventId(true); + event.eventId = PeerNode.this._omgr.getNextEventId(true); } protected Communicator createCommunicator () { // TODO: make a custom communicator that uses the ClientManager NIO system to do @@ -268,6 +270,7 @@ public class PeerNode } protected PeerManager _peermgr; + protected PresentsDObjectMgr _omgr; protected NodeRecord _record; protected Client _client; protected long _lastConnectStamp; diff --git a/src/java/com/threerings/presents/server/Authenticator.java b/src/java/com/threerings/presents/server/Authenticator.java index 512392f62..2b1a65b14 100644 --- a/src/java/com/threerings/presents/server/Authenticator.java +++ b/src/java/com/threerings/presents/server/Authenticator.java @@ -24,7 +24,9 @@ package com.threerings.presents.server; import com.samskivert.io.PersistenceException; import com.samskivert.util.Invoker; +import com.samskivert.util.ResultListener; +import com.threerings.presents.annotation.MainInvoker; import com.threerings.presents.data.AuthCodes; import com.threerings.presents.net.AuthRequest; @@ -43,26 +45,18 @@ import static com.threerings.presents.Log.log; */ public abstract class Authenticator { - /** - * Called by the connection manager to give us a reference to it for reporting authenticated - * connections. - */ - public void setConnectionManager (ConnectionManager conmgr) - { - _conmgr = conmgr; - } - /** * Called by the connection management code when an authenticating connection has received its * authentication request from the client. */ - public void authenticateConnection (final AuthingConnection conn) + public void authenticateConnection (Invoker invoker, final AuthingConnection conn, + final ResultListener onComplete) { final AuthRequest req = conn.getAuthRequest(); final AuthResponseData rdata = createResponseData(); final AuthResponse rsp = new AuthResponse(rdata); - getInvoker().postUnit(new Invoker.Unit("authenticateConnection") { + invoker.postUnit(new Invoker.Unit("authenticateConnection") { public boolean invoke() { try { processAuthentication(conn, rsp); @@ -84,21 +78,12 @@ public abstract class Authenticator // if the authentication request was granted, let the connection manager know that // we just authed if (AuthResponseData.SUCCESS.equals(rdata.code)) { - _conmgr.connectionDidAuthenticate(conn); + onComplete.requestCompleted(conn); } } }); } - /** - * Return the invoker on which to process the authentication. The default implementation - * returns PresentsServer.invoker. - */ - protected Invoker getInvoker () - { - return PresentsServer.invoker; - } - /** * Create a new AuthResponseData instance to use for authenticating a connection. */ @@ -117,7 +102,4 @@ public abstract class Authenticator */ protected abstract void processAuthentication (AuthingConnection conn, AuthResponse rsp) throws PersistenceException; - - /** The connection manager with which we're working. */ - protected ConnectionManager _conmgr; } diff --git a/src/java/com/threerings/presents/server/ChainedAuthenticator.java b/src/java/com/threerings/presents/server/ChainedAuthenticator.java new file mode 100644 index 000000000..ee90c6cb4 --- /dev/null +++ b/src/java/com/threerings/presents/server/ChainedAuthenticator.java @@ -0,0 +1,64 @@ +// +// $Id$ +// +// Narya library - tools for developing networked games +// Copyright (C) 2002-2008 Three Rings Design, Inc., All Rights Reserved +// http://www.threerings.net/code/narya/ +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.threerings.presents.server; + +import com.samskivert.util.Invoker; +import com.samskivert.util.ResultListener; + +import com.threerings.presents.server.net.AuthingConnection; + +/** + * Handles certain special kinds of authentications and passes the remainder through to the default + * authenticator. + */ +public abstract class ChainedAuthenticator extends Authenticator +{ + /** + * Called by the {@link ConnectionManager} to initialize our delegate. + */ + public void setChainedAuthenticator (Authenticator author) + { + _delegate = author; + } + + @Override // from Authenticator + public void authenticateConnection (Invoker invoker, AuthingConnection conn, + ResultListener onComplete) + { + // if we handle this sort of authentication, then do so + if (shouldHandleConnection(conn)) { + super.authenticateConnection(invoker, conn, onComplete); + + } else { + // otherwise pass the request on to our delegate + _delegate.authenticateConnection(invoker, conn, onComplete); + } + } + + /** + * Derived classes should implement this method and return true if the supplied connection is + * one that they should authenticate. + */ + protected abstract boolean shouldHandleConnection (AuthingConnection conn); + + protected Authenticator _delegate; +} diff --git a/src/java/com/threerings/presents/server/ClientManager.java b/src/java/com/threerings/presents/server/ClientManager.java index 87bd55bc6..7dace047c 100644 --- a/src/java/com/threerings/presents/server/ClientManager.java +++ b/src/java/com/threerings/presents/server/ClientManager.java @@ -98,8 +98,10 @@ public class ClientManager @Inject public ClientManager (ConnectionManager conmgr, ReportManager repmgr, ShutdownManager shutmgr) { - // register ourselves as a connection observer + // register as a connection observer, a "state of server" reporter and a shutdowner conmgr.addConnectionObserver(this); + repmgr.registerReporter(this); + shutmgr.registerShutdowner(this); // start up an interval that will check for expired clients and flush them from the bowels // of the server @@ -108,10 +110,6 @@ public class ClientManager flushClients(); } }.schedule(CLIENT_FLUSH_INTERVAL, true); - - // register as a "state of server" reporter and a shutdowner - repmgr.registerReporter(this); - shutmgr.registerShutdowner(this); } // from interface ShutdownManager.Shutdowner diff --git a/src/java/com/threerings/presents/server/PresentsServer.java b/src/java/com/threerings/presents/server/PresentsServer.java index c8260ca2e..997967f2d 100644 --- a/src/java/com/threerings/presents/server/PresentsServer.java +++ b/src/java/com/threerings/presents/server/PresentsServer.java @@ -31,6 +31,7 @@ import com.samskivert.util.Invoker; import com.samskivert.util.RunQueue; import com.samskivert.util.SystemInfo; +import com.threerings.presents.annotation.AuthInvoker; import com.threerings.presents.annotation.EventQueue; import com.threerings.presents.annotation.MainInvoker; import com.threerings.presents.client.Client; @@ -57,9 +58,11 @@ public class PresentsServer { @Override protected void configure () { bind(Invoker.class).annotatedWith(MainInvoker.class).to(PresentsInvoker.class); + bind(Invoker.class).annotatedWith(AuthInvoker.class).to(PresentsInvoker.class); bind(RunQueue.class).annotatedWith(EventQueue.class).to(PresentsDObjectMgr.class); bind(DObjectManager.class).to(PresentsDObjectMgr.class); bind(RootDObjectManager.class).to(PresentsDObjectMgr.class); + bind(Authenticator.class).to(DummyAuthenticator.class); } } @@ -149,36 +152,11 @@ public class PresentsServer // configure our connection manager _conmgr.init(getListenPorts(), getDatagramPorts()); - _conmgr.setAuthenticator(createAuthenticator()); // initialize the time base services TimeBaseProvider.init(invmgr, omgr); } - /** - * Creates the client Authenticator to be used on this server. - */ - protected Authenticator createAuthenticator () - { - return new DummyAuthenticator(); - } - -// /** -// * Creates the client manager to be used on this server. -// */ -// protected ClientManager createClientManager (ConnectionManager conmgr) -// { -// return new ClientManager(conmgr); -// } - -// /** -// * Creates the distributed object manager to be used on this server. -// */ -// protected PresentsDObjectMgr createDObjectManager () -// { -// return new PresentsDObjectMgr(); -// } - /** * Defines the default object access policy for all {@link DObject} instances. The default * default policy is to allow all subscribers but reject all modifications by the client. diff --git a/src/java/com/threerings/presents/server/Rejector.java b/src/java/com/threerings/presents/server/Rejector.java index e2d406f48..bc11ae579 100644 --- a/src/java/com/threerings/presents/server/Rejector.java +++ b/src/java/com/threerings/presents/server/Rejector.java @@ -42,12 +42,13 @@ import static com.threerings.presents.Log.log; */ public class Rejector extends PresentsServer { - @Override // from PresentsServer - public void init (Injector injector) - throws Exception + /** Configures dependencies needed by the Rejector. */ + public static class Module extends PresentsServer.Module { - super.init(injector); - _conmgr.setAuthenticator(new RejectingAuthenticator()); + @Override protected void configure () { + super.configure(); + bind(Authenticator.class).to(RejectingAuthenticator.class); + } } // documentation inherited diff --git a/src/java/com/threerings/presents/server/net/AuthingConnection.java b/src/java/com/threerings/presents/server/net/AuthingConnection.java index 9c25c3779..9a558699c 100644 --- a/src/java/com/threerings/presents/server/net/AuthingConnection.java +++ b/src/java/com/threerings/presents/server/net/AuthingConnection.java @@ -64,7 +64,7 @@ public class AuthingConnection extends Connection _authreq = (AuthRequest)msg; // post ourselves for processing by the authmgr - _cmgr.getAuthenticator().authenticateConnection(this); + _cmgr.authenticateConnection(this); } catch (ClassCastException cce) { log.warning("Received non-authreq message during " + diff --git a/src/java/com/threerings/presents/server/net/ConnectionManager.java b/src/java/com/threerings/presents/server/net/ConnectionManager.java index ca70c5434..20b439a48 100644 --- a/src/java/com/threerings/presents/server/net/ConnectionManager.java +++ b/src/java/com/threerings/presents/server/net/ConnectionManager.java @@ -48,6 +48,7 @@ import com.google.inject.Singleton; import com.samskivert.util.IntMap; import com.samskivert.util.IntMaps; +import com.samskivert.util.Invoker; import com.samskivert.util.LoopingThread; import com.samskivert.util.Queue; import com.samskivert.util.ResultListener; @@ -57,6 +58,7 @@ import com.samskivert.util.Tuple; import com.threerings.io.FramingOutputStream; import com.threerings.io.ObjectOutputStream; +import com.threerings.presents.annotation.AuthInvoker; import com.threerings.presents.client.Client; import com.threerings.presents.data.ConMgrStats; import com.threerings.presents.net.AuthRequest; @@ -65,6 +67,7 @@ import com.threerings.presents.net.DownstreamMessage; import com.threerings.presents.util.DatagramSequencer; import com.threerings.presents.server.Authenticator; +import com.threerings.presents.server.ChainedAuthenticator; import com.threerings.presents.server.PresentsDObjectMgr; import com.threerings.presents.server.ReportManager; import com.threerings.presents.server.ShutdownManager; @@ -129,22 +132,13 @@ public class ConnectionManager extends LoopingThread } /** - * Specifies the authenticator that should be used by the connection manager to authenticate - * logon requests. + * Adds an authenticator to the authentication chain. This authenticator will be offered a + * chance to authenticate incoming connections in lieu of the main autuenticator. */ - public void setAuthenticator (Authenticator author) + public void addChainedAuthenticator (ChainedAuthenticator author) { - // say hello to our new authenticator + author.setChainedAuthenticator(_author); _author = author; - _author.setConnectionManager(this); - } - - /** - * Returns the entity that is being used to authenticate connections. - */ - public Authenticator getAuthenticator () - { - return _author; } /** @@ -209,12 +203,19 @@ public class ConnectionManager extends LoopingThread } /** - * Called by the authenticator to indicate that a connection was successfully authenticated. + * Performs the authentication process on the specified connection. This is called by {@link + * AuthingConnection} itself once it receives its auth request. */ - public void connectionDidAuthenticate (AuthingConnection conn) + public void authenticateConnection (AuthingConnection conn) { - // slap this sucker onto the authenticated connections queue - _authq.append(conn); + _author.authenticateConnection(_authInvoker, conn, new ResultListener() { + public void requestCompleted (AuthingConnection conn) { + _authq.append(conn); + } + public void requestFailed (Exception cause) { + // this never happens + } + }); } // documentation inherited from interface ReportManager.Reporter @@ -1048,8 +1049,12 @@ public class ConnectionManager extends LoopingThread } }; + /** Handles client authentication. The base authenticator is injected but optional services + * like the PeerManager may replace this authenticator with one that intercepts certain types + * of authentication and then passes normal authentications through. */ + @Inject protected Authenticator _author; + protected int[] _ports, _datagramPorts; - protected Authenticator _author; protected Selector _selector; protected ServerSocketChannel _ssocket; protected DatagramChannel _datagramChannel; @@ -1093,7 +1098,10 @@ public class ConnectionManager extends LoopingThread /** A runnable to execute when the connection manager thread exits. */ protected volatile Runnable _onExit; - // injected dependencies + /** The invoker on which we do our authenticating. */ + @Inject @AuthInvoker protected Invoker _authInvoker; + + /** The distributed object manager with which we operate. */ @Inject protected PresentsDObjectMgr _omgr; /** How long we wait for network events before checking our running flag to see if we should