diff --git a/etc/aslib-config.xml.in b/etc/aslib-config.xml.in index 6d27e5f15..ac8b1f584 100644 --- a/etc/aslib-config.xml.in +++ b/etc/aslib-config.xml.in @@ -105,6 +105,7 @@ false + ../dist/lib/corelib.swc diff --git a/etc/libs-incl.xml b/etc/libs-incl.xml index 0e7a282d9..63edc142f 100644 --- a/etc/libs-incl.xml +++ b/etc/libs-incl.xml @@ -19,5 +19,6 @@ + diff --git a/src/as/com/threerings/bureau/client/BureauClient.as b/src/as/com/threerings/bureau/client/BureauClient.as index 29191897b..d174b1bc4 100644 --- a/src/as/com/threerings/bureau/client/BureauClient.as +++ b/src/as/com/threerings/bureau/client/BureauClient.as @@ -22,15 +22,20 @@ package com.threerings.bureau.client { import com.threerings.presents.client.Client; +import com.threerings.presents.dobj.DObjectManager; + +import com.threerings.bureau.data.BureauAuthName; import com.threerings.bureau.data.BureauCredentials; import com.threerings.bureau.util.BureauContext; -import com.threerings.presents.dobj.DObjectManager; /** * Represents a client embedded in a bureau. */ public class BureauClient extends Client { + // statically reference classes we require + BureauAuthName + /** * Creates a new client. * @param creds the credentials supplied during connection @@ -40,9 +45,7 @@ public class BureauClient extends Client { super(null); _bureauId = bureauId; - var creds :BureauCredentials = new BureauCredentials(_bureauId); - creds.sessionToken = token; - _creds = creds; + _creds = new BureauCredentials(_bureauId, token); _ctx = createContext(); _director = createDirector(); } diff --git a/src/as/com/threerings/bureau/data/BureauAuthName.as b/src/as/com/threerings/bureau/data/BureauAuthName.as new file mode 100644 index 000000000..63327f046 --- /dev/null +++ b/src/as/com/threerings/bureau/data/BureauAuthName.as @@ -0,0 +1,36 @@ +// +// $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.bureau.data { + +import com.threerings.util.Name; + +/** + * Represents an authenticated bureau client. + */ +public class BureauAuthName extends Name +{ + public function BureauAuthName (bureauId :String = "") + { + super(bureauId); + } +} +} diff --git a/src/as/com/threerings/bureau/data/BureauCredentials.as b/src/as/com/threerings/bureau/data/BureauCredentials.as index f32f3f1ce..5537a4364 100644 --- a/src/as/com/threerings/bureau/data/BureauCredentials.as +++ b/src/as/com/threerings/bureau/data/BureauCredentials.as @@ -21,59 +21,19 @@ package com.threerings.bureau.data { -import com.threerings.presents.net.Credentials; -import com.threerings.util.Name; -import com.threerings.io.ObjectInputStream; -import com.threerings.io.ObjectOutputStream; -import com.threerings.util.StringBuilder; +import com.threerings.presents.net.ServiceCreds; /** * Extends the basic credentials to provide bureau-specific fields. */ -public class BureauCredentials extends Credentials +public class BureauCredentials extends ServiceCreds { - /** - * The token to pass to the server when logging in. This is usually just passed to the bureau - * on the command line to guard against outside connections being established. - */ - public var sessionToken :String; - - /** - * The id of the bureau logging in. - */ - public var bureauId :String; - /** * Creates new credentials for a specific bureau. */ - public function BureauCredentials (bureauId :String) + public function BureauCredentials (bureauId :String = null, sharedSecret :String = null) { - super(new Name("@@bureau:" + bureauId + "@@")); - this.bureauId = bureauId; - } - - /** @inheritDoc */ - override protected function toStringBuf (buf :StringBuilder) :void - { - super.toStringBuf(buf); - buf.append(" bureauId=").append(bureauId). - append(" token=").append(sessionToken); - } - - // from interface Streamable - override public function readObject (ins :ObjectInputStream) :void - { - super.readObject(ins); - sessionToken = (ins.readField(String) as String); - bureauId = (ins.readField(String) as String); - } - - // from interface Streamable - override public function writeObject (out :ObjectOutputStream) :void - { - super.writeObject(out); - out.writeField(sessionToken); - out.writeField(bureauId); + super(bureauId, sharedSecret); } } } diff --git a/src/as/com/threerings/presents/net/Credentials.as b/src/as/com/threerings/presents/net/Credentials.as index 9884b5113..ad25b61d4 100644 --- a/src/as/com/threerings/presents/net/Credentials.as +++ b/src/as/com/threerings/presents/net/Credentials.as @@ -21,9 +21,6 @@ package com.threerings.presents.net { -import com.threerings.util.Name; -import com.threerings.util.StringBuilder; - import com.threerings.io.ObjectInputStream; import com.threerings.io.ObjectOutputStream; import com.threerings.io.Streamable; @@ -31,49 +28,22 @@ import com.threerings.io.Streamable; public /* abstract */ class Credentials implements Streamable { - public function Credentials (username :Name) + public function Credentials () { - _username = username; } - public function getUsername () :Name - { - return _username; - } - - public function setUsername (name :Name) :void - { - _username = name; - } - - // documentation inherited from interface Streamable + // from interface Streamable public function writeObject (out :ObjectOutputStream) :void //throws IOError { - out.writeObject(_username); + // nada } - // documentation inherited from interface Streamable + // from interface Streamable public function readObject (ins :ObjectInputStream) :void //throws IOError { - _username = Name(ins.readObject()); + // nada } - - public function toString () :String - { - var buf :StringBuilder = new StringBuilder("["); - toStringBuf(buf); - buf.append("]"); - return buf.toString(); - } - - protected function toStringBuf (buf :StringBuilder) :void - { - buf.append("username=", _username); - } - - /** The username. */ - protected var _username :Name; } } diff --git a/src/as/com/threerings/presents/net/ServiceCreds.as b/src/as/com/threerings/presents/net/ServiceCreds.as new file mode 100644 index 000000000..9c2e35afe --- /dev/null +++ b/src/as/com/threerings/presents/net/ServiceCreds.as @@ -0,0 +1,87 @@ +// +// $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.adobe.crypto.MD5; + +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; + +/** + * Extends the basic credentials to provide bureau-specific fields. + */ +public class ServiceCreds extends Credentials +{ + /** The id of the service client that is authenticating. */ + public var clientId :String; + + /** + * Creates credentials for the specified client. + */ + public function ServiceCreds (clientId :String = null, sharedSecret :String = null) + { + this.clientId = clientId; + _authToken = createAuthToken(clientId, sharedSecret); + } + + /** + * Validates that these credentials were created with the supplied shared secret. + */ + public function areValid (sharedSecret :String) :Boolean + { + return createAuthToken(clientId, sharedSecret) == _authToken; + } + + // from interface Streamable + override public function readObject (ins :ObjectInputStream) :void + { + super.readObject(ins); + clientId = (ins.readField(String) as String); + _authToken = (ins.readField(String) as String); + } + + // from interface Streamable + override public function writeObject (out :ObjectOutputStream) :void + { + super.writeObject(out); + out.writeField(clientId); + out.writeField(_authToken); + } + + // from Object + public function toString () :String + { + return "[id=" + clientId + ", token=" + _authToken + "]"; + } + + /** + * Creates a unique password for the specified node using the supplied shared secret. + */ + protected static function createAuthToken (clientId :String, sharedSecret :String) :String + { + return MD5.hash(clientId + sharedSecret); + } + + /** A token created by a call to {@link #createAuthToken}. */ + protected var _authToken :String; +} +} diff --git a/src/as/com/threerings/presents/net/UsernamePasswordCreds.as b/src/as/com/threerings/presents/net/UsernamePasswordCreds.as index 6643a7850..e16243d47 100644 --- a/src/as/com/threerings/presents/net/UsernamePasswordCreds.as +++ b/src/as/com/threerings/presents/net/UsernamePasswordCreds.as @@ -32,36 +32,52 @@ public class UsernamePasswordCreds extends Credentials /** * Construct credentials with the supplied username and password. */ - public function UsernamePasswordCreds (username :Name, password :String) + public function UsernamePasswordCreds (username :Name = null, password :String = null) { - super(username); + _username = username; _password = password; } + public function getUsername () :Name + { + return _username; + } + public function getPassword () :String { return _password; } + // from Credentials override public function writeObject (out :ObjectOutputStream) :void { - super.writeObject(out); + out.writeObject(_username); out.writeField(_password); } + // from Credentials override public function readObject (ins :ObjectInputStream) :void { - super.readObject(ins); + _username = Name(ins.readObject()); _password = (ins.readField(String) as String); } - // documentation inherited - override protected function toStringBuf (buf :StringBuilder) :void + // from Object + public function toString () :String { - super.toStringBuf(buf); + var buf :StringBuilder = new StringBuilder("["); + toStringBuf(buf); + buf.append("]"); + return buf.toString(); + } + + protected function toStringBuf (buf :StringBuilder) :void + { + buf.append("username=", _username); buf.append(", password=", _password); } + protected var _username :Name; protected var _password :String; } } diff --git a/src/java/com/threerings/bureau/client/BureauClient.java b/src/java/com/threerings/bureau/client/BureauClient.java index 343803ef7..2c4742022 100644 --- a/src/java/com/threerings/bureau/client/BureauClient.java +++ b/src/java/com/threerings/bureau/client/BureauClient.java @@ -39,13 +39,11 @@ public abstract class BureauClient extends Client * Creates a new client. * @param runQueue the place to post tasks required by clients */ - public BureauClient (String token, String bureauId, RunQueue runQueue) + public BureauClient (String bureauId, String sharedSecret, RunQueue runQueue) { super(null, runQueue); _bureauId = bureauId; - BureauCredentials creds = new BureauCredentials(_bureauId); - creds.sessionToken = token; - _creds = creds; + _creds = new BureauCredentials(_bureauId, sharedSecret); _ctx = createContext(); _director = createDirector(); } diff --git a/src/java/com/threerings/bureau/data/BureauAuthName.java b/src/java/com/threerings/bureau/data/BureauAuthName.java new file mode 100644 index 000000000..41e6c058f --- /dev/null +++ b/src/java/com/threerings/bureau/data/BureauAuthName.java @@ -0,0 +1,40 @@ +// +// $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.bureau.data; + +import com.threerings.util.Name; + +/** + * Represents an authenticated bureau client. + */ +public class BureauAuthName extends Name +{ + public BureauAuthName (String bureauId) + { + super(bureauId); + } + + // used when unserializing + public BureauAuthName () + { + } +} diff --git a/src/java/com/threerings/bureau/data/BureauCredentials.java b/src/java/com/threerings/bureau/data/BureauCredentials.java index 9e936aeb0..218e68bdd 100644 --- a/src/java/com/threerings/bureau/data/BureauCredentials.java +++ b/src/java/com/threerings/bureau/data/BureauCredentials.java @@ -21,51 +21,19 @@ package com.threerings.bureau.data; -import com.threerings.util.Name; - -import com.threerings.presents.net.Credentials; +import com.threerings.presents.net.ServiceCreds; /** * Extends the basic credentials to provide bureau-specific fields. */ -public class BureauCredentials extends Credentials +public class BureauCredentials extends ServiceCreds { - public static final String PREFIX = "@@bureau:"; - public static final String SUFFIX = "@@"; - /** - * The token to pass to the server when logging in. This is usually just passed to the bureau - * on the command line to guard against outside connections being established. + * Creates new credentials for a specific bureau. */ - public String sessionToken; - - /** - * The id of the bureau logging in. - */ - public String bureauId; - - /** - * Test if a given name object matches the name that we generate. - */ - public static boolean isBureau (Name name) + public BureauCredentials (String bureauId, String sharedSecret) { - String normal = name.getNormal(); - return normal.startsWith(PREFIX) && normal.endsWith(SUFFIX); - } - - /** - * Extract the buerauId from the name that we generate. - */ - public static String extractBureauId (Name name) - { - String normal = name.getNormal(); - int prefixPos = normal.indexOf(PREFIX); - int suffixPos = normal.lastIndexOf(SUFFIX); - if (prefixPos != 0 || suffixPos != normal.length() - SUFFIX.length()) { - return null; - } - - return normal.substring(prefixPos + PREFIX.length(), suffixPos); + super(bureauId, sharedSecret); } /** @@ -74,21 +42,4 @@ public class BureauCredentials extends Credentials public BureauCredentials () { } - - /** - * Creates new credentials for a specific bureau. - */ - public BureauCredentials (String bureauId) - { - super(new Name("@@bureau:" + bureauId + "@@")); - this.bureauId = bureauId; - } - - @Override // inherit documentation - protected void toString (StringBuilder buf) - { - super.toString(buf); - buf.append(" id=").append(bureauId). - append(" token=").append(sessionToken); - } } diff --git a/src/java/com/threerings/bureau/server/BureauAuthenticator.java b/src/java/com/threerings/bureau/server/BureauAuthenticator.java deleted file mode 100644 index fab9e8f6b..000000000 --- a/src/java/com/threerings/bureau/server/BureauAuthenticator.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.threerings.bureau.server; - -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.server.ChainedAuthenticator; -import com.threerings.presents.server.net.AuthingConnection; - -import com.threerings.bureau.data.BureauCredentials; - -import static com.threerings.bureau.Log.log; - -/** - * Authenticates bureaus only. - */ -public class BureauAuthenticator extends ChainedAuthenticator -{ - /** - * Creates a new bureau authenticator. - */ - public BureauAuthenticator (BureauRegistry registry) - { - _registry = registry; - } - - @Override // from abstract ChainedAuthenticator - protected boolean shouldHandleConnection (AuthingConnection conn) - { - return (conn.getAuthRequest().getCredentials() instanceof BureauCredentials); - } - - @Override // from Authenticator - protected void processAuthentication ( - AuthingConnection conn, - AuthResponse rsp) - { - AuthRequest req = conn.getAuthRequest(); - BureauCredentials creds = (BureauCredentials)req.getCredentials(); - String problem = _registry.checkToken(creds); - - if (problem == null) { - rsp.getData().code = AuthResponseData.SUCCESS; - - } else { - log.warning("Received invalid bureau auth request", - "creds", creds + "], problem: " + problem); - rsp.getData().code = AuthCodes.SERVER_ERROR; - } - } - - protected BureauRegistry _registry; -} diff --git a/src/java/com/threerings/bureau/server/BureauRegistry.java b/src/java/com/threerings/bureau/server/BureauRegistry.java index 5c987a325..fcb5acd93 100644 --- a/src/java/com/threerings/bureau/server/BureauRegistry.java +++ b/src/java/com/threerings/bureau/server/BureauRegistry.java @@ -42,8 +42,12 @@ import com.threerings.presents.dobj.RootDObjectManager; import com.threerings.presents.server.ClientManager; import com.threerings.presents.server.InvocationManager; import com.threerings.presents.server.PresentsSession; +import com.threerings.presents.server.ServiceAuthenticator; +import com.threerings.presents.server.SessionFactory; +import com.threerings.presents.server.net.ConnectionManager; import com.threerings.bureau.data.AgentObject; +import com.threerings.bureau.data.BureauAuthName; import com.threerings.bureau.data.BureauCodes; import com.threerings.bureau.data.BureauCredentials; import com.threerings.bureau.util.BureauLogRedirector; @@ -95,7 +99,8 @@ public class BureauRegistry /** * Creates an uninitialized registry. */ - @Inject public BureauRegistry (InvocationManager invmgr) + @Inject public BureauRegistry ( + InvocationManager invmgr, ConnectionManager conmgr, ClientManager clmgr) { invmgr.registerDispatcher(new BureauDispatcher(new BureauProvider() { public void bureauInitialized (ClientObject client, String bureauId) { @@ -114,59 +119,44 @@ public class BureauRegistry BureauRegistry.this.agentDestroyed(client, agentId); } }), BureauCodes.BUREAU_GROUP); - } - - /** - * Provides the Bureau registry with necessary runtime configuration. Inserts the bureau - * client factory into the client manager and registers observers to track the progress - * of launched bureaus. - * @see BureauSessionFactory - */ - public void init () - { - _clmgr.addClientObserver(new ClientManager.ClientObserver() { + conmgr.addChainedAuthenticator(new ServiceAuthenticator( + 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 getSessionClass () + { + return BureauSession.class; + } + + /** + * Returns the class used to resolve bureau client data. + */ + protected Class 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 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 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 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 getSessionClass (AuthRequest areq) - { - if (areq.getCredentials() instanceof PeerCreds) { - return PeerSession.class; - } else { - return _delegate.getSessionClass(areq); - } - } - - // documentation inherited from interface SessionFactory - public Class 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 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 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 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 _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 getSessionClass (AuthRequest areq); + public static SessionFactory newSessionFactory ( + final Class credsClass, + final Class sessionClass, + final Class nameClass, + final Class resolverClass) + { + return new SessionFactory() { + public Class getSessionClass (AuthRequest areq) { + return credsClass.isInstance(areq.getCredentials()) ? sessionClass : null; + } + public Class 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 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 getClientResolverClass (Name username); + public abstract Class 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));