(
+ BureauCredentials.class, BureauAuthName.class) {
+ protected boolean areValid (BureauCredentials creds) {
+ return checkToken(creds) == null;
+ }
+ });
+ clmgr.addSessionFactory(
+ SessionFactory.newSessionFactory(BureauCredentials.class, getSessionClass(),
+ BureauAuthName.class, getClientResolverClass()));
+ clmgr.addClientObserver(new ClientManager.ClientObserver() {
public void clientSessionDidStart (PresentsSession client) {
- String id = BureauCredentials.extractBureauId(client.getUsername());
- if (id != null) {
- sessionDidStart(client, id);
+ if (client.getCredentials() instanceof BureauCredentials) {
+ sessionDidStart(client, ((BureauCredentials)client.getCredentials()).clientId);
}
}
public void clientSessionDidEnd (PresentsSession client) {
- String id = BureauCredentials.extractBureauId(client.getUsername());
- if (id != null) {
- sessionDidEnd(client, id);
+ if (client.getCredentials() instanceof BureauCredentials) {
+ sessionDidEnd(client, ((BureauCredentials)client.getCredentials()).clientId);
}
}
});
}
- /**
- * Adds the delegating client factory. Note this should be called after all application
- * client factories are in place. The installed factory will simply create presents clients and
- * client objects for bureaus.
- */
- public void setDefaultSessionFactory ()
- {
- // add the client factory, but later, after all the other modules have been initialized
- _clmgr.setSessionFactory(new BureauSessionFactory(_clmgr.getSessionFactory()));
- }
-
/**
* Check the credentials to make sure this is one of our bureaus.
* @return null if all's well, otherwise a string describing the authentication failure
*/
public String checkToken (BureauCredentials creds)
{
- Bureau bureau = _bureaus.get(creds.bureauId);
+ Bureau bureau = _bureaus.get(creds.clientId);
if (bureau == null) {
- return "Bureau " + creds.bureauId + " not found";
+ return "Bureau " + creds.clientId + " not found";
}
if (bureau.clientObj != null) {
- return "Bureau " + creds.bureauId + " already logged in";
+ return "Bureau " + creds.clientId + " already logged in";
}
- if (!bureau.token.equals(creds.sessionToken)) {
- return "Bureau " + creds.bureauId +
- " does not match credentials token";
+ if (!creds.areValid(bureau.token)) {
+ return "Bureau " + creds.clientId + " does not match credentials token";
}
return null;
}
@@ -617,6 +607,22 @@ public class BureauRegistry
_bureaus.remove(bureau.bureauId);
}
+ /**
+ * Returns the class used to handle bureau sessions.
+ */
+ protected Class extends BureauSession> getSessionClass ()
+ {
+ return BureauSession.class;
+ }
+
+ /**
+ * Returns the class used to resolve bureau client data.
+ */
+ protected Class extends BureauClientResolver> getClientResolverClass ()
+ {
+ return BureauClientResolver.class;
+ }
+
/**
* Invoker unit to launch a bureau's process, then assign the result on the main thread.
*/
@@ -795,5 +801,4 @@ public class BureauRegistry
@Inject protected RootDObjectManager _omgr;
@Inject protected @MainInvoker Invoker _invoker;
- @Inject protected ClientManager _clmgr;
}
diff --git a/src/java/com/threerings/bureau/server/BureauSessionFactory.java b/src/java/com/threerings/bureau/server/BureauSessionFactory.java
deleted file mode 100644
index e41bc7bb6..000000000
--- a/src/java/com/threerings/bureau/server/BureauSessionFactory.java
+++ /dev/null
@@ -1,68 +0,0 @@
-//
-// $Id$
-//
-// Narya library - tools for developing networked games
-// Copyright (C) 2002-2007 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.bureau.server;
-
-import com.threerings.util.Name;
-
-import com.threerings.presents.net.AuthRequest;
-import com.threerings.presents.server.SessionFactory;
-import com.threerings.presents.server.ClientResolver;
-import com.threerings.presents.server.PresentsSession;
-
-import com.threerings.bureau.data.BureauCredentials;
-
-/**
- * Handles resolution of bureaus and passes non-bureau resolution requests through to a normal
- * factory. For bureaus, creates base class instances {@link PresentsSession} and
- * {@link ClientResolver}.
- * @see BureauRegistry#setDefaultSessionFactory()
- */
-public class BureauSessionFactory implements SessionFactory
-{
- public BureauSessionFactory (SessionFactory delegate)
- {
- _delegate = delegate;
- }
-
- // from interface SessionFactory
- public Class extends PresentsSession> getSessionClass (AuthRequest areq)
- {
- // Just give bureaus a vanilla PresentsSession client for now.
- if (areq.getCredentials() instanceof BureauCredentials) {
- return BureauSession.class;
- } else {
- return _delegate.getSessionClass(areq);
- }
- }
-
- // from interface SessionFactory
- public Class extends ClientResolver> getClientResolverClass (Name username)
- {
- if (BureauCredentials.isBureau(username)) {
- return BureauClientResolver.class;
- } else {
- return _delegate.getClientResolverClass(username);
- }
- }
-
- protected SessionFactory _delegate;
-}
diff --git a/src/java/com/threerings/crowd/server/CrowdServer.java b/src/java/com/threerings/crowd/server/CrowdServer.java
index 95f35d60e..c28e040d3 100644
--- a/src/java/com/threerings/crowd/server/CrowdServer.java
+++ b/src/java/com/threerings/crowd/server/CrowdServer.java
@@ -63,7 +63,7 @@ public class CrowdServer extends PresentsServer
super.init(injector);
// configure the client manager to use our bits
- _clmgr.setSessionFactory(new SessionFactory() {
+ _clmgr.setDefaultSessionFactory(new SessionFactory() {
public Class extends PresentsSession> getSessionClass (AuthRequest areq) {
return CrowdSession.class;
}
diff --git a/src/java/com/threerings/presents/net/Credentials.java b/src/java/com/threerings/presents/net/Credentials.java
index af87bdaf8..076d2ac12 100644
--- a/src/java/com/threerings/presents/net/Credentials.java
+++ b/src/java/com/threerings/presents/net/Credentials.java
@@ -23,47 +23,17 @@ package com.threerings.presents.net;
import com.threerings.io.Streamable;
-import com.threerings.util.Name;
-
/**
* Credentials are supplied by the client implementation and sent along to the server during the
* authentication process. To provide support for a variety of authentication methods, the
* credentials class is meant to be subclassed for the particular method (ie. password, auth
* digest, etc.) in use in a given system.
*
- * All credentials must provide a username as the username is used to associate network
- * connections with sessions.
- *
*
All derived classes should provide a no argument constructor so that they can be
* instantiated prior to reconstruction from a data input stream.
*/
public abstract class Credentials implements Streamable
{
- /**
- * Constructs a credentials instance with the specified username.
- */
- public Credentials (Name username)
- {
- _username = username;
- }
-
- /**
- * Constructs a blank credentials instance in preparation for unserializing from the network.
- */
- public Credentials ()
- {
- }
-
- public Name getUsername ()
- {
- return _username;
- }
-
- public void setUsername (Name name)
- {
- _username = name;
- }
-
/**
* Returns a string to use in a hash on the datagram contents to authenticate client datagrams.
*/
@@ -71,39 +41,4 @@ public abstract class Credentials implements Streamable
{
return "";
}
-
- @Override
- public int hashCode ()
- {
- return _username.hashCode();
- }
-
- @Override
- public boolean equals (Object other)
- {
- if (other instanceof Credentials) {
- return _username.equals(((Credentials)other)._username);
- } else {
- return false;
- }
- }
-
- @Override
- public String toString ()
- {
- StringBuilder buf = new StringBuilder("[");
- toString(buf);
- return buf.append("]").toString();
- }
-
- /**
- * An easily extensible method via which derived classes can add to {@link #toString()}'s
- * output.
- */
- protected void toString (StringBuilder buf)
- {
- buf.append("username=").append(_username);
- }
-
- protected Name _username;
}
diff --git a/src/java/com/threerings/presents/net/ServiceCreds.java b/src/java/com/threerings/presents/net/ServiceCreds.java
new file mode 100644
index 000000000..98aeb93be
--- /dev/null
+++ b/src/java/com/threerings/presents/net/ServiceCreds.java
@@ -0,0 +1,75 @@
+//
+// $Id$
+//
+// Narya library - tools for developing networked games
+// Copyright (C) 2002-2009 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.net;
+
+import com.samskivert.util.StringUtil;
+
+/**
+ * Credentials used by service clients (peers, bureaus, etc.). A service would extend this class
+ * (so that their service clients can be identified by class name).
+ */
+public abstract class ServiceCreds extends Credentials
+{
+ /** The id of the service client that is authenticating. */
+ public String clientId;
+
+ /**
+ * Creates credentials for the specified client.
+ */
+ public ServiceCreds (String clientId, String sharedSecret)
+ {
+ this.clientId = clientId;
+ _authToken = createAuthToken(clientId, sharedSecret);
+ }
+
+ /**
+ * Used when unserializing an instance from the network.
+ */
+ public ServiceCreds ()
+ {
+ }
+
+ /**
+ * Validates that these credentials were created with the supplied shared secret.
+ */
+ public boolean areValid (String sharedSecret)
+ {
+ return createAuthToken(clientId, sharedSecret).equals(_authToken);
+ }
+
+ @Override // from Object
+ public String toString ()
+ {
+ return getClass().getSimpleName() + "[id=" + clientId + ", token=" + _authToken + "]";
+ }
+
+ /**
+ * Creates a unique password for the specified node using the supplied shared secret.
+ */
+ protected static String createAuthToken (String clientId, String sharedSecret)
+ {
+ return StringUtil.md5hex(clientId + sharedSecret);
+ }
+
+ /** A token created by a call to {@link #createAuthToken}. */
+ protected String _authToken;
+}
diff --git a/src/java/com/threerings/presents/net/UsernamePasswordCreds.java b/src/java/com/threerings/presents/net/UsernamePasswordCreds.java
index 46dcf434f..a1ca84cf8 100644
--- a/src/java/com/threerings/presents/net/UsernamePasswordCreds.java
+++ b/src/java/com/threerings/presents/net/UsernamePasswordCreds.java
@@ -24,7 +24,7 @@ package com.threerings.presents.net;
import com.threerings.util.Name;
/**
- * Extends the basic credentials with a password.
+ * Credentials that use a username and (hashed) password.
*/
public class UsernamePasswordCreds extends Credentials
{
@@ -33,7 +33,6 @@ public class UsernamePasswordCreds extends Credentials
*/
public UsernamePasswordCreds ()
{
- super();
}
/**
@@ -41,10 +40,15 @@ public class UsernamePasswordCreds extends Credentials
*/
public UsernamePasswordCreds (Name username, String password)
{
- super(username);
+ _username = username;
_password = password;
}
+ public Name getUsername ()
+ {
+ return _username;
+ }
+
public String getPassword ()
{
return _password;
@@ -57,29 +61,22 @@ public class UsernamePasswordCreds extends Credentials
}
@Override
- public int hashCode ()
+ public String toString ()
{
- return super.hashCode() ^ _password.hashCode();
+ StringBuilder buf = new StringBuilder("[");
+ toString(buf);
+ return buf.append("]").toString();
}
- @Override
- public boolean equals (Object other)
- {
- if (other instanceof UsernamePasswordCreds) {
- UsernamePasswordCreds upcreds = (UsernamePasswordCreds)other;
- return super.equals(other) &&
- _password.equals(upcreds._password);
- } else {
- return false;
- }
- }
-
- @Override
+ /**
+ * An easily extensible method via which derived classes can add to {@link #toString}'s output.
+ */
protected void toString (StringBuilder buf)
{
- super.toString(buf);
+ buf.append("username=").append(_username);
buf.append(", password=").append(_password);
}
+ protected Name _username;
protected String _password;
}
diff --git a/src/java/com/threerings/presents/peer/data/PeerAuthName.java b/src/java/com/threerings/presents/peer/data/PeerAuthName.java
new file mode 100644
index 000000000..dba8ddf2c
--- /dev/null
+++ b/src/java/com/threerings/presents/peer/data/PeerAuthName.java
@@ -0,0 +1,40 @@
+//
+// $Id$
+//
+// Narya library - tools for developing networked games
+// Copyright (C) 2002-2009 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.peer.data;
+
+import com.threerings.util.Name;
+
+/**
+ * Represents an authenticated peer client.
+ */
+public class PeerAuthName extends Name
+{
+ public PeerAuthName (String nodeName)
+ {
+ super(nodeName);
+ }
+
+ // used when unserializing
+ public PeerAuthName ()
+ {
+ }
+}
diff --git a/src/java/com/threerings/presents/peer/net/PeerCreds.java b/src/java/com/threerings/presents/peer/net/PeerCreds.java
index 3fc16aa88..f0095a0ec 100644
--- a/src/java/com/threerings/presents/peer/net/PeerCreds.java
+++ b/src/java/com/threerings/presents/peer/net/PeerCreds.java
@@ -21,36 +21,16 @@
package com.threerings.presents.peer.net;
-import com.samskivert.util.StringUtil;
-
-import com.threerings.util.Name;
-
-import com.threerings.presents.net.UsernamePasswordCreds;
+import com.threerings.presents.net.ServiceCreds;
/**
* Used by peer servers in a cluster installation to authenticate with one another.
*/
-public class PeerCreds extends UsernamePasswordCreds
+public class PeerCreds extends ServiceCreds
{
- /** A prefix prepended to the node name used as a peer's username to prevent the username from
- * colliding with a normal authenticating user's username. We assume that colons are not
- * allowed in a normal username. */
- public static final String PEER_PREFIX = "peer:";
-
- /**
- * Creates a unique password for the specified node using the supplied shared secret.
- */
- public static String createPassword (String nodeName, String sharedSecret)
- {
- return StringUtil.md5hex(nodeName + sharedSecret);
- }
-
- /**
- * Creates credentials for the specified peer.
- */
public PeerCreds (String nodeName, String sharedSecret)
{
- super(new Name(PEER_PREFIX + nodeName), createPassword(nodeName, sharedSecret));
+ super(nodeName, sharedSecret);
}
/**
@@ -59,13 +39,4 @@ public class PeerCreds extends UsernamePasswordCreds
public PeerCreds ()
{
}
-
- /**
- * Returns the node name of this authenticating peer (which does not include the {@link
- * #PEER_PREFIX}.
- */
- public String getNodeName ()
- {
- return getUsername().toString().substring(PEER_PREFIX.length());
- }
}
diff --git a/src/java/com/threerings/presents/peer/server/PeerAuthenticator.java b/src/java/com/threerings/presents/peer/server/PeerAuthenticator.java
deleted file mode 100644
index fc557a34b..000000000
--- a/src/java/com/threerings/presents/peer/server/PeerAuthenticator.java
+++ /dev/null
@@ -1,71 +0,0 @@
-//
-// $Id$
-//
-// Narya library - tools for developing networked games
-// Copyright (C) 2002-2007 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.peer.server;
-
-import com.samskivert.io.PersistenceException;
-
-import com.threerings.presents.data.AuthCodes;
-import com.threerings.presents.net.AuthRequest;
-import com.threerings.presents.net.AuthResponse;
-import com.threerings.presents.net.AuthResponseData;
-import com.threerings.presents.peer.net.PeerCreds;
-import com.threerings.presents.server.ChainedAuthenticator;
-import com.threerings.presents.server.net.AuthingConnection;
-
-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 ChainedAuthenticator
-{
- public PeerAuthenticator (PeerManager nodemgr)
- {
- _peermgr = nodemgr;
- }
-
- @Override // from abstract ChainedAuthenticator
- protected boolean shouldHandleConnection (AuthingConnection conn)
- {
- return (conn.getAuthRequest().getCredentials() instanceof PeerCreds);
- }
-
- @Override // from abstract Authenticator
- protected void processAuthentication (AuthingConnection conn, AuthResponse rsp)
- throws PersistenceException
- {
- // here, we are ONLY authenticating peers
- AuthRequest req = conn.getAuthRequest();
- PeerCreds pcreds = (PeerCreds) req.getCredentials();
-
- if (_peermgr.isAuthenticPeer(pcreds)) {
- rsp.getData().code = AuthResponseData.SUCCESS;
-
- } else {
- log.warning("Received invalid peer auth request?", "creds", pcreds);
- rsp.getData().code = AuthCodes.SERVER_ERROR;
- }
- }
-
- protected PeerManager _peermgr;
-}
diff --git a/src/java/com/threerings/presents/peer/server/PeerManager.java b/src/java/com/threerings/presents/peer/server/PeerManager.java
index 21bc65544..0902d8cbd 100644
--- a/src/java/com/threerings/presents/peer/server/PeerManager.java
+++ b/src/java/com/threerings/presents/peer/server/PeerManager.java
@@ -69,11 +69,14 @@ import com.threerings.presents.server.InvocationManager;
import com.threerings.presents.server.PresentsDObjectMgr;
import com.threerings.presents.server.PresentsSession;
import com.threerings.presents.server.ReportManager;
+import com.threerings.presents.server.ServiceAuthenticator;
+import com.threerings.presents.server.SessionFactory;
import com.threerings.presents.server.net.ConnectionManager;
import com.threerings.presents.peer.client.PeerService;
import com.threerings.presents.peer.data.ClientInfo;
import com.threerings.presents.peer.data.NodeObject;
+import com.threerings.presents.peer.data.PeerAuthName;
import com.threerings.presents.peer.net.PeerCreds;
import com.threerings.presents.peer.server.persist.NodeRecord;
import com.threerings.presents.peer.server.persist.NodeRepository;
@@ -234,8 +237,15 @@ public abstract class PeerManager
_sharedSecret = sharedSecret;
// wire ourselves into the server
- _conmgr.addChainedAuthenticator(new PeerAuthenticator(this));
- _clmgr.setSessionFactory(new PeerSessionFactory(_clmgr.getSessionFactory()));
+ _conmgr.addChainedAuthenticator(
+ new ServiceAuthenticator(PeerCreds.class, PeerAuthName.class) {
+ protected boolean areValid (PeerCreds creds) {
+ return creds.areValid(_sharedSecret);
+ }
+ });
+ _clmgr.addSessionFactory(
+ SessionFactory.newSessionFactory(PeerCreds.class, PeerSession.class,
+ PeerAuthName.class, PeerClientResolver.class));
// create our node object
_nodeobj = _omgr.registerObject(createNodeObject());
@@ -269,8 +279,7 @@ public abstract class PeerManager
*/
public boolean isAuthenticPeer (PeerCreds creds)
{
- return PeerCreds.createPassword(creds.getNodeName(), _sharedSecret).equals(
- creds.getPassword());
+ return creds.areValid(_sharedSecret);
}
/**
@@ -855,7 +864,7 @@ public abstract class PeerManager
// we scan through the list instead of relying on ClientInfo.getKey() because we want
// derived classes to be able to override that for lookups that happen way more frequently
// than logging off
- Name username = client.getCredentials().getUsername();
+ Name username = client.getUsername();
for (ClientInfo clinfo : _nodeobj.clients) {
if (clinfo.username.equals(username)) {
_nodeobj.startTransaction();
@@ -1015,7 +1024,7 @@ public abstract class PeerManager
*/
protected void initClientInfo (PresentsSession client, ClientInfo info)
{
- info.username = client.getCredentials().getUsername();
+ info.username = client.getUsername();
}
/**
@@ -1391,13 +1400,13 @@ public abstract class PeerManager
protected Stats _stats = new Stats();
// our service dependencies
- @Inject protected ConnectionManager _conmgr;
- @Inject protected ClientManager _clmgr;
- @Inject protected ReportManager _repmgr;
- @Inject protected PresentsDObjectMgr _omgr;
- @Inject protected InvocationManager _invmgr;
@Inject protected @MainInvoker Invoker _invoker;
+ @Inject protected ClientManager _clmgr;
+ @Inject protected ConnectionManager _conmgr;
+ @Inject protected InvocationManager _invmgr;
@Inject protected NodeRepository _noderepo;
+ @Inject protected PresentsDObjectMgr _omgr;
+ @Inject protected ReportManager _repmgr;
/** We wait this long for peer ratification to complete before acquiring/releasing the lock. */
protected static final long LOCK_TIMEOUT = 5000L;
diff --git a/src/java/com/threerings/presents/peer/server/PeerSession.java b/src/java/com/threerings/presents/peer/server/PeerSession.java
index 893a69dd2..fccfcaeab 100644
--- a/src/java/com/threerings/presents/peer/server/PeerSession.java
+++ b/src/java/com/threerings/presents/peer/server/PeerSession.java
@@ -41,8 +41,7 @@ import static com.threerings.presents.Log.log;
public class PeerSession extends PresentsSession
{
/**
- * Creates a peer session and provides it with a reference to the peer manager. This is only
- * done by the {@link PeerSessionFactory}.
+ * Creates a peer session and provides it with a reference to the peer manager.
*/
@Inject public PeerSession (PeerManager peermgr)
{
diff --git a/src/java/com/threerings/presents/peer/server/PeerSessionFactory.java b/src/java/com/threerings/presents/peer/server/PeerSessionFactory.java
deleted file mode 100644
index 540612b25..000000000
--- a/src/java/com/threerings/presents/peer/server/PeerSessionFactory.java
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// $Id$
-//
-// Narya library - tools for developing networked games
-// Copyright (C) 2002-2007 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.peer.server;
-
-import com.threerings.util.Name;
-
-import com.threerings.presents.net.AuthRequest;
-import com.threerings.presents.peer.net.PeerCreds;
-import com.threerings.presents.server.SessionFactory;
-import com.threerings.presents.server.ClientResolver;
-import com.threerings.presents.server.PresentsSession;
-
-/**
- * Handles resolution of peer servers and passes non-peer resolution requests through to a normal
- * factory.
- */
-public class PeerSessionFactory implements SessionFactory
-{
- public PeerSessionFactory (SessionFactory delegate)
- {
- _delegate = delegate;
- }
-
- // documentation inherited from interface SessionFactory
- public Class extends PresentsSession> getSessionClass (AuthRequest areq)
- {
- if (areq.getCredentials() instanceof PeerCreds) {
- return PeerSession.class;
- } else {
- return _delegate.getSessionClass(areq);
- }
- }
-
- // documentation inherited from interface SessionFactory
- public Class extends ClientResolver> getClientResolverClass (Name username)
- {
- if (username.toString().startsWith(PeerCreds.PEER_PREFIX)) {
- return PeerClientResolver.class;
- } else {
- return _delegate.getClientResolverClass(username);
- }
- }
-
- protected SessionFactory _delegate;
-}
diff --git a/src/java/com/threerings/presents/server/Authenticator.java b/src/java/com/threerings/presents/server/Authenticator.java
index f57b0b24b..c5d4208f5 100644
--- a/src/java/com/threerings/presents/server/Authenticator.java
+++ b/src/java/com/threerings/presents/server/Authenticator.java
@@ -55,6 +55,10 @@ public abstract class Authenticator
public boolean invoke () {
try {
processAuthentication(conn, rsp);
+ if (AuthResponseData.SUCCESS.equals(rdata.code) &&
+ conn.getAuthName() == null) { // fail early, fail (less) often
+ throw new IllegalStateException("Authenticator failed to provide authname");
+ }
} catch (Exception e) {
log.warning("Error authenticating user", "areq", req, e);
rdata.code = AuthCodes.SERVER_ERROR;
diff --git a/src/java/com/threerings/presents/server/ChainedAuthenticator.java b/src/java/com/threerings/presents/server/ChainedAuthenticator.java
index bdd455531..61b4271a3 100644
--- a/src/java/com/threerings/presents/server/ChainedAuthenticator.java
+++ b/src/java/com/threerings/presents/server/ChainedAuthenticator.java
@@ -21,11 +21,7 @@
package com.threerings.presents.server;
-import com.samskivert.util.Invoker;
-import com.samskivert.util.ResultListener;
-
import com.threerings.presents.server.net.AuthingConnection;
-import com.threerings.presents.server.net.ConnectionManager;
/**
* Handles certain special kinds of authentications and passes the remainder through to the default
@@ -33,33 +29,9 @@ import com.threerings.presents.server.net.ConnectionManager;
*/
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;
+ public abstract boolean shouldHandleConnection (AuthingConnection conn);
}
diff --git a/src/java/com/threerings/presents/server/ClientManager.java b/src/java/com/threerings/presents/server/ClientManager.java
index fc50b6aec..0c325ca96 100644
--- a/src/java/com/threerings/presents/server/ClientManager.java
+++ b/src/java/com/threerings/presents/server/ClientManager.java
@@ -117,20 +117,23 @@ public class ClientManager
}
/**
- * Configures the client manager with a factory for creating {@link PresentsSession} and {@link
- * ClientResolver} classes for authenticated client connections.
+ * Configures the default factory for creating {@link PresentsSession} and {@link
+ * ClientResolver} classes for authenticated client connections. All factories added via {@link
+ * #addSessionFactory} will be offered a chance to handle sessions before this factory of last
+ * resort.
*/
- public void setSessionFactory (SessionFactory factory)
+ public void setDefaultSessionFactory (SessionFactory factory)
{
- _factory = factory;
+ _factories.set(_factories.size()-1, factory);
}
/**
- * Returns the {@link SessionFactory} currently in use.
+ * Adds a session factory to the chain. This factory will be offered a chance to resolve
+ * sessions before passing the buck to the next factory in the chain.
*/
- public SessionFactory getSessionFactory ()
+ public void addSessionFactory (SessionFactory factory)
{
- return _factory;
+ _factories.add(0, factory);
}
/**
@@ -271,10 +274,18 @@ public class ClientManager
return;
}
+ // figure out our client resolver class
+ Class extends ClientResolver> resolverClass = null;
+ for (SessionFactory factory : _factories) {
+ if ((resolverClass = factory.getClientResolverClass(username)) != null) {
+ break;
+ }
+ }
+
try {
// create a client resolver instance which will create our client object, populate it
// and notify the listeners
- clr = _injector.getInstance(_factory.getClientResolverClass(username));
+ clr = _injector.getInstance(resolverClass);
clr.init(username);
clr.addResolutionListener(this);
clr.addResolutionListener(listener);
@@ -400,28 +411,34 @@ public class ClientManager
* Called by the connection manager to let us know when a new connection has been established.
*/
public synchronized void connectionEstablished (
- Connection conn, AuthRequest req, AuthResponse rsp)
+ Connection conn, Name authname, AuthRequest req, AuthResponse rsp)
{
Credentials creds = req.getCredentials();
- Name username = creds.getUsername();
- String type = username.getClass().getSimpleName();
+ String type = authname.getClass().getSimpleName();
- // see if a client is already registered with these credentials
- PresentsSession client = getClient(username);
+ // see if a client is already registered with this name
+ PresentsSession client = getClient(authname);
if (client != null) {
- log.info("Resuming session", "type", type, "who", username, "conn", conn);
+ log.info("Resuming session", "type", type, "who", authname, "conn", conn);
client.resumeSession(req, conn);
} else {
- log.info("Session initiated", "type", type, "who", username, "conn", conn);
+ log.info("Session initiated", "type", type, "who", authname, "conn", conn);
+ // figure out our session class
+ Class extends PresentsSession> sessionClass = null;
+ for (SessionFactory factory : _factories) {
+ if ((sessionClass = factory.getSessionClass(req)) != null) {
+ break;
+ }
+ }
// create a new client and stick'em in the table
- client = _injector.getInstance(_factory.getSessionClass(req));
- client.startSession(req, conn, rsp.authdata);
+ client = _injector.getInstance(sessionClass);
+ client.startSession(authname, req, conn, rsp.authdata);
// map their client instance
synchronized (_usermap) {
- _usermap.put(username, client);
+ _usermap.put(authname, client);
}
}
@@ -515,10 +532,9 @@ public class ClientManager
protected void clearSession (PresentsSession session)
{
// remove the client from the username map
- Name username = session.getCredentials().getUsername();
PresentsSession rc;
synchronized (_usermap) {
- rc = _usermap.remove(username);
+ rc = _usermap.remove(session.getUsername());
}
// sanity check just because we can
@@ -604,7 +620,7 @@ public class ClientManager
protected Map _penders = Maps.newHashMap();
/** Lets us know what sort of client classes to use. */
- protected SessionFactory _factory = SessionFactory.DEFAULT;
+ protected List _factories = Lists.newArrayList(SessionFactory.DEFAULT);
/** Tracks registered {@link ClientObserver}s. */
protected ObserverList _clobservers = ObserverList.newSafeInOrder();
diff --git a/src/java/com/threerings/presents/server/DummyAuthenticator.java b/src/java/com/threerings/presents/server/DummyAuthenticator.java
index 9dbe45323..33e62d185 100644
--- a/src/java/com/threerings/presents/server/DummyAuthenticator.java
+++ b/src/java/com/threerings/presents/server/DummyAuthenticator.java
@@ -23,8 +23,12 @@ package com.threerings.presents.server;
import com.samskivert.io.PersistenceException;
+import com.threerings.util.Name;
+
import com.threerings.presents.net.AuthResponse;
import com.threerings.presents.net.AuthResponseData;
+import com.threerings.presents.net.Credentials;
+import com.threerings.presents.net.UsernamePasswordCreds;
import com.threerings.presents.server.net.AuthingConnection;
import static com.threerings.presents.Log.log;
@@ -39,6 +43,15 @@ public class DummyAuthenticator extends Authenticator
throws PersistenceException
{
log.info("Accepting request: " + conn.getAuthRequest());
+
+ // we need to provide some sort of authentication username
+ Credentials creds = conn.getAuthRequest().getCredentials();
+ if (creds instanceof UsernamePasswordCreds) {
+ conn.setAuthName(((UsernamePasswordCreds)creds).getUsername());
+ } else {
+ conn.setAuthName(new Name(conn.getInetAddress().getHostAddress()));
+ }
+
rsp.getData().code = AuthResponseData.SUCCESS;
}
}
diff --git a/src/java/com/threerings/presents/server/PresentsSession.java b/src/java/com/threerings/presents/server/PresentsSession.java
index 411d2d271..3944ae02e 100644
--- a/src/java/com/threerings/presents/server/PresentsSession.java
+++ b/src/java/com/threerings/presents/server/PresentsSession.java
@@ -465,15 +465,13 @@ public class PresentsSession
* Initializes this client instance with the specified username, connection instance and client
* object and begins a client session.
*/
- protected void startSession (AuthRequest req, Connection conn, Object authdata)
+ protected void startSession (Name authname, AuthRequest req, Connection conn, Object authdata)
{
+ _username = authname;
_areq = req;
_authdata = authdata;
setConnection(conn);
- // obtain our starting username
- assignStartingUsername();
-
// resolve our client object before we get fully underway
_clmgr.resolveClientObject(_username, this);
@@ -481,17 +479,6 @@ public class PresentsSession
_sessionStamp = System.currentTimeMillis();
}
- /**
- * This is factored out to allow derived classes to use a different starting username than the
- * one supplied in the user's credentials. Generally one only wants to munge the starting
- * username if the user will subsequently choose a "screen name" and it is desirable to avoid
- * collision between the authentication user namespace and the screen namespace.
- */
- protected void assignStartingUsername ()
- {
- _username = getCredentials().getUsername();
- }
-
/**
* Called by the client manager when a new connection arrives that authenticates as this
* already established client. This must only be called from the congmr thread.
diff --git a/src/java/com/threerings/presents/server/ServiceAuthenticator.java b/src/java/com/threerings/presents/server/ServiceAuthenticator.java
new file mode 100644
index 000000000..2db28389e
--- /dev/null
+++ b/src/java/com/threerings/presents/server/ServiceAuthenticator.java
@@ -0,0 +1,92 @@
+//
+// $Id$
+//
+// Narya library - tools for developing networked games
+// Copyright (C) 2002-2009 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 java.lang.reflect.Constructor;
+
+import com.samskivert.io.PersistenceException;
+
+import com.threerings.util.Name;
+
+import com.threerings.presents.data.AuthCodes;
+import com.threerings.presents.net.AuthResponse;
+import com.threerings.presents.net.AuthResponseData;
+import com.threerings.presents.net.ServiceCreds;
+import com.threerings.presents.server.net.AuthingConnection;
+
+import static com.threerings.presents.Log.log;
+
+/**
+ * Works in conjunction with {@link ServiceCreds} to handle the authentication of service clients
+ * (bureaus, peers, etc.).
+ */
+public abstract class ServiceAuthenticator extends ChainedAuthenticator
+{
+ /**
+ * Creates an authenticator that will handle requests using the supplied credentials class and
+ * which will create instances of the supplied auth name class to identify those clients. Note
+ * that the auth name class must have a public constructor that takes a single string.
+ */
+ public ServiceAuthenticator (Class credsClass, Class extends Name> authNameClass)
+ {
+ _credsClass = credsClass;
+ try {
+ _authNamer = authNameClass.getConstructor(String.class);
+ } catch (NoSuchMethodException nsme) {
+ throw new IllegalArgumentException("AuthName must have AuthName(String) constructor.");
+ }
+ }
+
+ @Override // from abstract ChainedAuthenticator
+ public boolean shouldHandleConnection (AuthingConnection conn)
+ {
+ return _credsClass.isInstance(conn.getAuthRequest().getCredentials());
+ }
+
+ @Override // from abstract Authenticator
+ protected void processAuthentication (AuthingConnection conn, AuthResponse rsp)
+ throws PersistenceException
+ {
+ T creds = _credsClass.cast(conn.getAuthRequest().getCredentials());
+ if (!areValid(creds)) {
+ log.warning("Received invalid service auth request?", "creds", creds);
+ rsp.getData().code = AuthCodes.SERVER_ERROR;
+ return;
+ }
+
+ try {
+ conn.setAuthName(_authNamer.newInstance(creds.clientId));
+ rsp.getData().code = AuthResponseData.SUCCESS;
+ } catch (Exception e) {
+ log.warning("Failed to construct auth name", "namer", _authNamer, e);
+ rsp.getData().code = AuthCodes.SERVER_ERROR;
+ }
+ }
+
+ /**
+ * Returns true if the creds in question are valid.
+ */
+ protected abstract boolean areValid (T creds);
+
+ protected Class _credsClass;
+ protected Constructor extends Name> _authNamer;
+}
diff --git a/src/java/com/threerings/presents/server/SessionFactory.java b/src/java/com/threerings/presents/server/SessionFactory.java
index 407535c6a..f51a690d3 100644
--- a/src/java/com/threerings/presents/server/SessionFactory.java
+++ b/src/java/com/threerings/presents/server/SessionFactory.java
@@ -24,12 +24,13 @@ package com.threerings.presents.server;
import com.threerings.util.Name;
import com.threerings.presents.net.AuthRequest;
+import com.threerings.presents.net.Credentials;
/**
* Used to determine what type of {@link PresentsSession} to use to manage an authenticated client
* as well the type of {@link ClientResolver} to use when resolving clients' runtime data.
*/
-public interface SessionFactory
+public abstract class SessionFactory
{
/** The default client factory. */
public static SessionFactory DEFAULT = new SessionFactory() {
@@ -42,14 +43,35 @@ public interface SessionFactory
};
/**
- * Returns the {@link PresentsSession} derived class to use for the session that authenticated
- * with the supplied request.
+ * Creates a session factory that handles clients with the supplied credentials and
+ * authentication name.
*/
- Class extends PresentsSession> getSessionClass (AuthRequest areq);
+ public static SessionFactory newSessionFactory (
+ final Class extends Credentials> credsClass,
+ final Class extends PresentsSession> sessionClass,
+ final Class extends Name> nameClass,
+ final Class extends ClientResolver> resolverClass)
+ {
+ return new SessionFactory() {
+ public Class extends PresentsSession> getSessionClass (AuthRequest areq) {
+ return credsClass.isInstance(areq.getCredentials()) ? sessionClass : null;
+ }
+ public Class extends ClientResolver> getClientResolverClass (Name username) {
+ return nameClass.isInstance(username) ? resolverClass : null;
+ }
+ };
+ }
+
+ /**
+ * Returns the {@link PresentsSession} derived class to use for the session that authenticated
+ * with the supplied request or null if this factory does not handle sessions of the supplied
+ * type.
+ */
+ public abstract Class extends PresentsSession> getSessionClass (AuthRequest areq);
/**
* Returns the {@link ClientResolver} derived class to use to resolve a client with the
- * specified username.
+ * specified username or null if this factory does not handle clients of the supplied type.
*/
- Class extends ClientResolver> getClientResolverClass (Name username);
+ public abstract Class extends ClientResolver> getClientResolverClass (Name username);
}
diff --git a/src/java/com/threerings/presents/server/net/AuthingConnection.java b/src/java/com/threerings/presents/server/net/AuthingConnection.java
index dccd653e2..260cc60f4 100644
--- a/src/java/com/threerings/presents/server/net/AuthingConnection.java
+++ b/src/java/com/threerings/presents/server/net/AuthingConnection.java
@@ -21,6 +21,8 @@
package com.threerings.presents.server.net;
+import com.threerings.util.Name;
+
import com.threerings.presents.net.AuthRequest;
import com.threerings.presents.net.AuthResponse;
import com.threerings.presents.net.Message;
@@ -76,6 +78,24 @@ public class AuthingConnection extends Connection
_authrsp = authrsp;
}
+ /**
+ * Returns the username that uniquely identifies this authenticated session. This will be used
+ * to map Name -> PresentsSession in the ClientManager and used elsewhere.
+ */
+ public Name getAuthName ()
+ {
+ return _authname;
+ }
+
+ /**
+ * During the authentication process, the authenticator must establish the client's
+ * authentication username and configure it via this method.
+ */
+ public void setAuthName (Name authname)
+ {
+ _authname = authname;
+ }
+
@Override
public String toString ()
{
@@ -84,4 +104,5 @@ public class AuthingConnection extends Connection
protected AuthRequest _authreq;
protected AuthResponse _authrsp;
+ protected Name _authname;
}
diff --git a/src/java/com/threerings/presents/server/net/ConnectionManager.java b/src/java/com/threerings/presents/server/net/ConnectionManager.java
index f2a55d504..fadd4fc3b 100644
--- a/src/java/com/threerings/presents/server/net/ConnectionManager.java
+++ b/src/java/com/threerings/presents/server/net/ConnectionManager.java
@@ -24,6 +24,7 @@ package com.threerings.presents.server.net;
import java.net.InetSocketAddress;
import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -39,6 +40,7 @@ import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
+import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -134,12 +136,11 @@ public class ConnectionManager extends LoopingThread
/**
* Adds an authenticator to the authentication chain. This authenticator will be offered a
- * chance to authenticate incoming connections in lieu of the main autuenticator.
+ * chance to authenticate incoming connections before falling back to the main authenticator.
*/
public void addChainedAuthenticator (ChainedAuthenticator author)
{
- author.setChainedAuthenticator(_author);
- _author = author;
+ _authors.add(author);
}
/**
@@ -266,7 +267,14 @@ public class ConnectionManager extends LoopingThread
*/
protected void authenticateConnection (AuthingConnection conn)
{
- _author.authenticateConnection(_authInvoker, conn, new ResultListener() {
+ Authenticator author = _author;
+ for (ChainedAuthenticator cauthor : _authors) {
+ if (cauthor.shouldHandleConnection(conn)) {
+ author = cauthor;
+ break;
+ }
+ }
+ author.authenticateConnection(_authInvoker, conn, new ResultListener() {
public void requestCompleted (AuthingConnection conn) {
_authq.append(conn);
}
@@ -547,7 +555,8 @@ public class ConnectionManager extends LoopingThread
}
// and let the client manager know about our new connection
- _clmgr.connectionEstablished(rconn, conn.getAuthRequest(), conn.getAuthResponse());
+ _clmgr.connectionEstablished(rconn, conn.getAuthName(), conn.getAuthRequest(),
+ conn.getAuthResponse());
} catch (IOException ioe) {
log.warning("Failure upgrading authing connection to running.", ioe);
@@ -1187,6 +1196,7 @@ public class ConnectionManager extends LoopingThread
* like the PeerManager may replace this authenticator with one that intercepts certain types
* of authentication and then passes normal authentications through. */
@Inject(optional=true) protected Authenticator _author = new DummyAuthenticator();
+ protected List _authors = Lists.newArrayList();
protected int[] _ports, _datagramPorts;
protected String _datagramHostname;
diff --git a/tests/src/java/com/threerings/bureau/server/RegistryTester.java b/tests/src/java/com/threerings/bureau/server/RegistryTester.java
index 42e737c2a..fdee98d02 100644
--- a/tests/src/java/com/threerings/bureau/server/RegistryTester.java
+++ b/tests/src/java/com/threerings/bureau/server/RegistryTester.java
@@ -205,7 +205,7 @@ public class RegistryTester
return;
}
- String id = BureauCredentials.extractBureauId(bureau.getUsername());
+ String id = ((BureauCredentials)bureau.getCredentials()).clientId;
log.info("Killing bureau " + id);
bureau.endSession();
diff --git a/tests/src/java/com/threerings/bureau/server/TestServer.java b/tests/src/java/com/threerings/bureau/server/TestServer.java
index 1cabc3d59..30ee8879c 100644
--- a/tests/src/java/com/threerings/bureau/server/TestServer.java
+++ b/tests/src/java/com/threerings/bureau/server/TestServer.java
@@ -68,15 +68,6 @@ public class TestServer extends PresentsServer
};
}
- @Override // from PresentsServer
- public void init (Injector injector)
- throws Exception
- {
- super.init(injector);
- _bureauReg.init();
- _bureauReg.setDefaultSessionFactory();
- }
-
public void setClientTarget (String target)
{
_bureauReg.setCommandGenerator("test", antCommandGenerator(target));