Added ability to login directly using SSH auth; based on patch by hagna.

This commit is contained in:
Griatch 2011-06-18 10:18:17 +00:00
parent 218ae61836
commit 1d93d8295f
4 changed files with 125 additions and 92 deletions

View file

@ -60,83 +60,17 @@ class CmdConnect(MuxCommand):
session.msg("Incorrect password.")
return
# We are logging in, get/setup the player object controlled by player
# Check if this is the first time the
# *player* connects
if player.db.FIRST_LOGIN:
player.at_first_login()
del player.db.FIRST_LOGIN
player.at_pre_login()
character = player.character
if character:
# this player has a character. Check if it's the
# first time *this character* logs in
if character.db.FIRST_LOGIN:
character.at_first_login()
del character.db.FIRST_LOGIN
# run character login hook
character.at_pre_login()
# actually do the login
# actually do the login. This will call all hooks.
session.session_login(player)
# post-login hooks
player.at_post_login()
# we are logged in. Look around.
character = player.character
if character:
character.at_post_login()
character.execute_cmd('look')
character.execute_cmd("look")
else:
player.execute_cmd('look')
# we have no character yet; use player's look, if it exists
player.execute_cmd("look")
# run look
#print "character:", character, character.scripts.all(), character.cmdset.current
#
# character = player.character
# if not character:
# # Create a new character object to tie the player to. This should
# # usually not be needed unless the old character object was manually
# # deleted.
# default_home_id = ServerConfig.objects.conf("default_home")
# default_home = ObjectDB.objects.get_id(default_home_id)
# typeclass = settings.BASE_CHARACTER_TYPECLASS
# character = create.create_object(typeclass=typeclass,
# key=player.name,
# location=default_home,
# home=default_home,
# player=player)
# character.db.FIRST_LOGIN = "True"
# # Getting ready to log the player in.
# # Check if this is the first time the
# # *player* connects
# if player.db.FIRST_LOGIN:
# player.at_first_login()
# del player.db.FIRST_LOGIN
# # check if this is the first time the *character*
# # character (needs not be the first time the player
# # does so, e.g. if the player has several characters)
# if character.db.FIRST_LOGIN:
# character.at_first_login()
# del character.db.FIRST_LOGIN
# # actually do the login, calling
# # customization hooks before and after.
# player.at_pre_login()
# character.at_pre_login()
# session.session_login(player)
# player.at_post_login()
# character.at_post_login()
# # run look
# #print "character:", character, character.scripts.all(), character.cmdset.current
# character.execute_cmd('look')
class CmdCreate(MuxCommand):
"""

View file

@ -58,6 +58,43 @@ class IOdata(object):
self.__dict__.update(**kwargs)
def _login(session, player):
"""
For logging a player in. Removed this from CmdConnect because ssh
wanted to call it for autologin.
"""
# We are logging in, get/setup the player object controlled by player
# Check if this is the first time the
# *player* connects (should be set by the
if player.db.FIRST_LOGIN:
player.at_first_login()
del player.db.FIRST_LOGIN
player.at_pre_login()
character = player.character
if character:
# this player has a character. Check if it's the
# first time *this character* logs in (this should be
# set by the initial create command)
if character.db.FIRST_LOGIN:
character.at_first_login()
del character.db.FIRST_LOGIN
# run character login hook
character.at_pre_login()
# actually do the login
session.session_login(player)
# post-login hooks
player.at_post_login()
if character:
character.at_post_login()
character.execute_cmd('look')
else:
player.execute_cmd('look')
#------------------------------------------------------------
# SessionBase class
#------------------------------------------------------------
@ -110,10 +147,28 @@ class SessionBase(object):
def session_login(self, player):
"""
Private startup mechanisms that need to run at login
Startup mechanisms that need to run at login
player - the connected player
"""
# Check if this is the first time the *player* logs in
if player.db.FIRST_LOGIN:
player.at_first_login()
del player.db.FIRST_LOGIN
player.at_pre_login()
character = player.character
if character:
# this player has a character. Check if it's the
# first time *this character* logs in
if character.db.FIRST_LOGIN:
character.at_first_login()
del character.db.FIRST_LOGIN
# run character login hook
character.at_pre_login()
# actually do the login by assigning session data
self.player = player
self.user = player.user
self.uid = self.user.id
@ -132,9 +187,14 @@ class SessionBase(object):
#add session to connected list
SESSIONS.add_loggedin_session(self)
#call hook
#call login hook
self.at_login(player)
# post-login hooks
player.at_post_login()
if character:
character.at_post_login()
def session_disconnect(self):
"""
Clean up the session, removing it from the game and doing some

View file

@ -19,10 +19,13 @@ from twisted.conch.insults import insults
from twisted.conch.manhole_ssh import TerminalRealm, _Glue, ConchFactory
from twisted.conch.manhole import Manhole, recvline
from twisted.internet import defer
from twisted.conch import interfaces as iconch
from twisted.python import components
from django.conf import settings
from src.server import session
from src.players.models import PlayerDB
from src.utils import ansi, utils, logger
#from src.commands.default.unloggedin import _login
ENCODINGS = settings.ENCODINGS
@ -37,6 +40,13 @@ class SshProtocol(Manhole, session.Session):
them. All communication between game and player goes through
here.
"""
def __init__(self, player):
"""
For setting up the player. If player is not None then we'll
login automatically.
"""
self.player = player
def terminalSize(self, width, height):
"""
@ -48,9 +58,12 @@ class SshProtocol(Manhole, session.Session):
self.terminal.cursorHome()
self.width = width
self.height = height
# initialize the session
self.session_connect(self.getClientAddress())
if self.player is not None:
self.session_login(self.player)
self.execute_cmd('look')
def connectionMade(self):
"""
@ -161,7 +174,6 @@ class SshProtocol(Manhole, session.Session):
"""
self.telnet_markup = True
# show connection screen
self.execute_cmd('look')
def at_login(self, player):
"""
@ -184,7 +196,7 @@ class SshProtocol(Manhole, session.Session):
def at_data_out(self, string, data=None):
"""
Data Evennia -> Player access hook. 'data' argument is ignored.
Data Evennia -> Player access hook. 'data' argument is a dict parsed for string settings.
"""
try:
string = utils.to_str(string, encoding=self.encoding)
@ -216,6 +228,7 @@ class SshProtocol(Manhole, session.Session):
logger.log_errmsg(str(e))
class ExtraInfoAuthServer(SSHUserAuthServer):
def auth_password(self, packet):
"""
@ -230,10 +243,11 @@ class ExtraInfoAuthServer(SSHUserAuthServer):
return self.portal.login(c, None, IConchUser).addErrback(
self._ebPassword)
class AnyAuth(object):
class PlayerDBPasswordChecker(object):
"""
Special auth method that accepts any credentials.
Checks the django db for the correct credentials for
username/password otherwise it returns the player or None which is
useful for the Realm.
"""
credentialInterfaces = (credentials.IUsernamePassword,)
@ -242,8 +256,31 @@ class AnyAuth(object):
up = credentials.IUsernamePassword(c, None)
username = up.username
password = up.password
src_ip = str(up.transport.transport.getPeer().host)
return defer.succeed(username)
player = PlayerDB.objects.get_player_from_name(username)
res = None
if player and player.user.check_password(password):
res = player
return defer.succeed(res)
class PassAvatarIdTerminalRealm(TerminalRealm):
"""
Returns an avatar that passes the avatarId through to the
protocol. This is probably not the best way to do it.
"""
def _getAvatar(self, avatarId):
comp = components.Componentized()
user = self.userFactory(comp, avatarId)
sess = self.sessionFactory(comp)
sess.transportFactory = self.transportFactory
sess.chainedProtocolFactory = lambda : self.chainedProtocolFactory(avatarId)
comp.setComponent(iconch.IConchUser, user)
comp.setComponent(iconch.ISession, sess)
return user
class TerminalSessionTransport_getPeer:
@ -276,6 +313,7 @@ class TerminalSessionTransport_getPeer:
self.chainedProtocol.terminalProtocol.terminalSize(width, height)
def getKeyPair(pubkeyfile, privkeyfile):
"""
This function looks for RSA keypair files in the current directory. If they
@ -302,6 +340,7 @@ def getKeyPair(pubkeyfile, privkeyfile):
return Key.fromString(publicKeyString), Key.fromString(privateKeyString)
def makeFactory(configdict):
"""
Creates the ssh server factory.
@ -310,21 +349,21 @@ def makeFactory(configdict):
pubkeyfile = "ssh-public.key"
privkeyfile = "ssh-private.key"
def chainProtocolFactory():
def chainProtocolFactory(username=None):
return insults.ServerProtocol(
configdict['protocolFactory'],
*configdict.get('protocolConfigdict', ()),
*configdict.get('protocolConfigdict', (username,)),
**configdict.get('protocolKwArgs', {}))
rlm = TerminalRealm()
rlm = PassAvatarIdTerminalRealm()
rlm.transportFactory = TerminalSessionTransport_getPeer
rlm.chainedProtocolFactory = chainProtocolFactory
factory = ConchFactory(Portal(rlm))
try:
# create/get RSA keypair
# create/get RSA keypair
publicKey, privateKey = getKeyPair(pubkeyfile, privkeyfile)
factory.publicKeys = {'ssh-rsa': publicKey}
factory.publicKeys = {'ssh-rsa': publicKey}
factory.privateKeys = {'ssh-rsa': privateKey}
except Exception, e:
print " getKeyPair error: %s\n WARNING: Evennia could not auto-generate SSH keypair. Using conch default keys instead." % e
@ -333,6 +372,6 @@ def makeFactory(configdict):
factory.services = factory.services.copy()
factory.services['ssh-userauth'] = ExtraInfoAuthServer
factory.portal.registerChecker(AnyAuth())
factory.portal.registerChecker(PlayerDBPasswordChecker())
return factory

View file

@ -105,7 +105,7 @@ class TelnetProtocol(StatefulTelnetProtocol, session.Session):
def at_data_out(self, string, data=None):
"""
Data Evennia -> Player access hook. 'data' argument is ignored.
Data Evennia -> Player access hook. 'data' argument is a dict parsed for string settings.
"""
try:
string = utils.to_str(string, encoding=self.encoding)