Skip to content
Snippets Groups Projects
Commit 0bfb2f24 authored by sveseli's avatar sveseli
Browse files

refactored rest service common classes and controllers

parent 48a859fb
No related branches found
No related tags found
No related merge requests found
Showing
with 925 additions and 0 deletions
#!/usr/bin/env python
#
# Base DM controller class.
#
#######################################################################
import cherrypy
import httplib
import json
from sys import exc_info
from dm.common.utility.loggingManager import LoggingManager
from dm.common.constants import dmStatus
from dm.common.constants import dmHttpHeaders
from dm.common.exceptions.dmException import DmException
from dm.common.exceptions import dmExceptionMap
from dm.common.exceptions.internalError import InternalError
#######################################################################
class DmController(object):
""" Base controller class. """
def __init__(self):
self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__)
@classmethod
def getLogger(cls):
logger = LoggingManager.getInstance().getLogger(cls.__name__)
return logger
@classmethod
def addDmResponseHeaders(cls, status=dmStatus.DM_OK, msg='Success', exceptionType=None):
cherrypy.response.headers[dmHttpHeaders.DM_STATUS_CODE_HTTP_HEADER] = status
cherrypy.response.headers[dmHttpHeaders.DM_STATUS_MESSAGE_HTTP_HEADER] = msg
if exceptionType is not None:
cherrypy.response.headers[dmHttpHeaders.DM_EXCEPTION_TYPE_HTTP_HEADER] = exceptionType
@classmethod
def addDmSessionRoleHeaders(cls, role):
cherrypy.response.headers[dmHttpHeaders.DM_SESSION_ROLE_HTTP_HEADER] = role
@classmethod
def addDmExceptionHeaders(cls, ex):
cls.handleException(ex)
@classmethod
def handleCpException(cls):
cherrypy.response.status = httplib.OK
ex = exc_info()[1]
if ex == None:
ex = InternalError('Internal Webservice Error')
cls.handleException(ex)
@classmethod
def handleException(cls, ex):
exClass = ex.__class__.__name__.split('.')[-1]
status = None
msg = '%s' % ex
msg = msg.split('\n')[0]
for code in dmExceptionMap.DM_EXCEPTION_MAP.keys():
exStr = dmExceptionMap.DM_EXCEPTION_MAP.get(code).split('.')[-1]
if exStr == exClass:
status = code
if not status:
status = dmStatus.DM_ERROR
cls.addDmResponseHeaders(status, msg, exClass)
@classmethod
def formatJsonResponse(cls, response):
cherrypy.response.headers['Content-Type'] = 'application/json'
return '%s' % (response)
@classmethod
def toJson(cls, o):
return json.dumps(o)
@classmethod
def fromJson(cls, s):
return json.loads(s)
@classmethod
def listToJson(cls, dmObjectList):
jsonList = []
for dmObject in dmObjectList:
jsonList.append(dmObject.getDictRep(keyList='__all__'))
return json.dumps(jsonList)
@classmethod
def getSessionUser(cls):
return cherrypy.serving.session.get('user')
@classmethod
def getSessionUsername(cls):
return cherrypy.serving.session.get('_cp_username')
# Exception decorator for all exposed method calls
@classmethod
def execute(cls, func):
def decorate(*args, **kwargs):
try:
response = func(*args, **kwargs)
except DmException, ex:
cls.getLogger().error('%s' % ex)
cls.handleException(ex)
response = ex.getFullJsonRep()
except Exception, ex:
cls.getLogger().error('%s' % ex)
cls.handleException(ex)
response = InternalError(ex).getFullJsonRep()
return cls.formatJsonResponse(response)
return decorate
#!/usr/bin/env python
#
# Base web service class.
#
####################################################################
import sys
import os
import cherrypy
from cherrypy.process import plugins
from cherrypy import server
from dm.common.constants import dmStatus
from dm.common.utility.configurationManager import ConfigurationManager
from dm.common.utility.loggingManager import LoggingManager
from dm.common.utility.dmModuleManager import DmModuleManager
from dm.common.exceptions.configurationError import ConfigurationError
####################################################################
class DmRestWebServiceBase:
DEFAULT_N_SERVER_REQUEST_THREADS = 10
DEFAULT_SERVER_SOCKET_TIMEOUT = 30
CONFIG_SECTION_NAME = 'WebService'
CONFIG_OPTION_NAME_LIST = [ 'serviceHost', 'servicePort',
'sslCertFile', 'sslKeyFile', 'sslCaCertFile' ]
class SignalHandler:
def __init__(self, signal, oldSignalHandler):
self.signal = signal
self.oldSignalHandler = oldSignalHandler
self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__)
def signalHandler(self):
self.logger.debug('%s signal handler called' % self.signal)
DmModuleManager.getInstance().stopModules()
self.oldSignalHandler()
def __init__(self, routeMapper):
self.configurationManager = ConfigurationManager.getInstance()
self.routeMapper = routeMapper
self.options = None
self.args = None
self.logger = None
def prepareOptions(self):
from optparse import OptionParser
p = OptionParser()
p.add_option('-d', '--daemon', action="store_true",
dest='daemonFlag', default=False,
help="Run as a daemon.")
p.add_option('-p', '--pid-file',
dest='pidFile', default=None,
help="Store process id in the given file.")
p.add_option('', '--config-file',
dest='configFile', default=None,
help="Service configuration file.")
p.add_option('-P', '--port',
dest='servicePort', default=None,
help="Service port.")
p.add_option('-H', '--host',
dest='serviceHost', default=None,
help="Service host.")
p.add_option('-C', '--ssl-ca-cert',
dest='sslCaCertFile', default=None,
help='SSL CA certificate path (used for client SSL certificate verification). Requires --ssl-key and --ssl-cert.')
p.add_option('-c', '--ssl-cert',
dest='sslCertFile', default=None,
help='SSL certificate path. SSL operation requires both --ssl-key and --ssl-cert. Client SSL certificate verification also requires --ssl-ca-cert.')
p.add_option('-k', '--ssl-key',
dest='sslKeyFile', default=None,
help='SSL key path. SSL operation requires both --ssl-key and --ssl-cert. Client SSL certificate verification also requires --ssl-ca-cert.')
p.add_option('', '--n-server-threads',
dest='nServerThreads', default=DmRestWebServiceBase.DEFAULT_N_SERVER_REQUEST_THREADS,
help='Number of service request handler threads (defaut: %s).' % DmRestWebServiceBase.DEFAULT_N_SERVER_REQUEST_THREADS)
return p
def initDmModules(self):
return None
def getDefaultServerHost(self):
return None
def getDefaultServerPort(self):
return None
# Instantiate modified signal handler that stops dm modules first,
# and then does the default action.
def modifySignalHandlers(self, engine):
pluginsSignalHandler = plugins.SignalHandler(engine)
handlers = pluginsSignalHandler.handlers
# Add handler for interrupt
handlers['SIGINT'] = engine.exit
# Modify all signal handlers
for signal in handlers.keys():
self.logger.debug('Modifying signal: %s' % signal)
oldSignalHandler = handlers[signal]
self.logger.debug('Old signal handler: %s' % oldSignalHandler)
signalHandler = DmRestWebServiceBase.SignalHandler(signal, oldSignalHandler)
self.logger.debug('Setting signal handler to: %s' % signalHandler.signalHandler)
handlers[signal] = signalHandler.signalHandler
pluginsSignalHandler.subscribe()
def initServerLog(self):
cherrypyLogLevel = self.configurationManager.getCherrypyLogLevel()
cherrypy.log.error_log.setLevel(cherrypyLogLevel)
cherrypy.log.error_file = self.configurationManager.getCherrypyLogFile()
cherrypy.log.error_log.propagate = False
cherrypy.log.access_log.setLevel(cherrypyLogLevel)
cherrypy.log.access_file = self.configurationManager.getCherrypyAccessFile()
cherrypy.log.access_log.propagate = False
def updateServerConfig(self):
serviceHost = self.configurationManager.getServiceHost()
servicePort = int(self.configurationManager.getServicePort())
nServerThreads = int(self.options.nServerThreads)
configDict = {
'server.socket_host' : serviceHost,
'server.socket_port' : servicePort,
'server.thread_pool' : nServerThreads,
'log.screen' : (self.options.daemonFlag != True),
}
cherrypy.config.update(configDict)
def readConfigFile(self, configFile):
configFile = self.options.configFile
if not configFile:
configFile = self.configurationManager.getConfigFile()
else:
self.configurationManager.setConfigFile(configFile)
if not os.path.exists(configFile):
raise ConfigurationError('Configuration file %s does not exist.' % configFile)
# Read file and set config options
self.configurationManager.setOptionsFromConfigFile(DmRestWebServiceBase.CONFIG_SECTION_NAME, DmRestWebServiceBase.CONFIG_OPTION_NAME_LIST, configFile)
def readCommandLineOptions(self):
# This method should be called after reading config file
# in case some options are overridden
if self.options.sslCaCertFile != None:
self.configurationManager.setSslCaCertFile(self.options.sslCaCertFile)
if self.options.sslCertFile != None:
self.configurationManager.setSslCertFile(self.options.sslCertFile)
if self.options.sslKeyFile != None:
self.configurationManager.setSslKeyFile(self.options.sslKeyFile)
if self.options.serviceHost != None:
self.configurationManager.setServiceHost(self.options.serviceHost)
if self.options.servicePort != None:
self.configurationManager.setServicePort(self.options.servicePort)
def prepareServer(self):
try:
optionParser = self.prepareOptions()
(self.options, self.args) = optionParser.parse_args()
# Read config file and override with command line options
self.readConfigFile(self.options.configFile)
self.readCommandLineOptions()
# Turn off console log for daemon mode.
self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__)
if self.options.daemonFlag:
LoggingManager.getInstance().setConsoleLogLevel('CRITICAL')
dispatch = self.routeMapper.setupRoutes()
self.logger.debug('Using route dispatch: %s' % dispatch)
config = {
'/' : {
'request.dispatch' : dispatch,
},
}
# No root controller as we provided our own.
cherrypy.tree.mount(root=None, config=config)
self.initServerLog()
self.updateServerConfig()
self.logger.info('Using host %s' % self.configurationManager.getServiceHost())
self.logger.info('Using port %s' % self.configurationManager.getServicePort())
self.logger.debug('Using %s request handler threads' % self.options.nServerThreads)
except Exception, ex:
if self.logger is not None:
self.logger.exception(ex)
else:
import traceback
print '\n%s' % sys.exc_info()[1]
traceback.print_exc(file=sys.stderr)
sys.exit(dmStatus.DM_ERROR)
# Run server.
def __runServer(self):
self.logger.info('Starting service')
engine = cherrypy.engine
# Set up Deamonization
if self.options.daemonFlag:
plugins.Daemonizer(engine).subscribe()
self.logger.debug('Daemon mode: %s' % self.options.daemonFlag)
if self.options.pidFile != None:
plugins.PIDFile(engine, self.options.pidFile).subscribe()
self.logger.debug('Using PID file: %s' % self.options.pidFile)
sslCertFile = self.configurationManager.getSslCertFile()
sslKeyFile = self.configurationManager.getSslKeyFile()
sslCaCertFile = self.configurationManager.getSslCaCertFile()
if sslCertFile != None and sslKeyFile != None:
server.ssl_ca_certificate = None
if sslCaCertFile != None:
server.ssl_ca_certificate = self.options.sslCaCertFile
self.logger.info('Using SSL CA cert file: %s' % sslCaCertFile)
server.ssl_certificate = sslCertFile
self.logger.info('Using SSL cert file: %s' % sslCertFile)
server.ssl_private_key = sslKeyFile
self.logger.info('Using SSL key file: %s' % sslKeyFile)
server.ssl_module = 'builtin'
#server.ssl_module = 'pyopenssl'
# Increase timeout to prevent early SSL connection terminations
server.socket_timeout = DmRestWebServiceBase.DEFAULT_SERVER_SOCKET_TIMEOUT
# Setup the signal handler to stop the application while running.
if hasattr(engine, 'signal_handler'):
self.logger.debug('Subscribing signal handler')
engine.signal_handler.subscribe()
self.modifySignalHandlers(engine)
# Turn off autoreloader.
self.logger.debug('Turning off autoreloader')
engine.autoreload.unsubscribe()
# Start the engine.
try:
self.logger.debug('Starting engine')
engine.start()
# Prepare dm services.
self.logger.debug('Starting modules')
self.initDmModules()
DmModuleManager.getInstance().startModules()
except Exception, ex:
self.logger.exception('Service exiting: %s' % ex)
DmModuleManager.getInstance().stopModules()
return dmStatus.DM_ERROR
self.logger.info('Service ready')
engine.block()
DmModuleManager.getInstance().stopModules()
self.logger.info('Service done')
return dmStatus.DM_OK
# Run server instance.
def run(self):
self.prepareServer()
sys.exit(self.__runServer())
####################################################################
# Testing
if __name__ == '__main__':
pass
#!/usr/bin/env python
#
# Base DM session controller class.
#
#######################################################################
import cherrypy
from dm.common.service.dmController import DmController
from dm.common.service.loginController import LoginController
#######################################################################
class DmSessionController(DmController):
""" Base session controller class. """
_cp_config = {
'tools.sessions.on': True,
'tools.auth.on': True
}
#auth = LoginController()
# Add before_handler for authorization
cherrypy.tools.auth = cherrypy.Tool('before_handler', LoginController.checkAuthorization)
def __init__(self):
DmController.__init__(self)
@classmethod
def require(cls, *conditions):
"""
Decorator that appends conditions to the auth.require config
variable.
"""
def decorate(f):
if not hasattr(f, '_cp_config'):
f._cp_config = dict()
if 'auth.require' not in f._cp_config:
f._cp_config['auth.require'] = []
f._cp_config['auth.require'].extend(conditions)
return f
return decorate
@classmethod
def anyOf(cls, *conditions):
""" Returns True if any of the conditions match. """
def check():
for c in conditions:
if c():
return True
return False
return check
@classmethod
def allOf(cls, *conditions):
""" Returns True if all of the conditions match. """
def check():
for c in conditions:
if not c():
return False
return True
return check
@classmethod
def isLoggedIn(cls):
""" Returns True if session has been established. """
def check():
role = cherrypy.session.get(LoginController.SESSION_ROLE_KEY, None)
if role is not None:
return True
return False
return check
#!/usr/bin/env python
import os
from dm.common.exceptions.authorizationError import AuthorizationError
from dm.common.objects.dmObjectManager import DmObjectManager
from dm.common.utility.configurationManager import ConfigurationManager
from dm.common.utility.objectCache import ObjectCache
from dm.common.utility.cryptUtility import CryptUtility
class AuthorizationManager(DmObjectManager):
DEFAULT_CACHE_SIZE = 10000 # number of items
DEFAULT_CACHE_OBJECT_LIFETIME = 3600 # seconds
CONFIG_SECTION_NAME = 'AuthorizationManager'
ADMIN_GROUP_NAME_KEY = 'admingroupname'
PRINCIPAL_RETRIEVER_KEY = 'principalretriever'
PRINCIPAL_AUTHENTICATOR_KEY = 'principalauthenticator'
# Get singleton instance.
@classmethod
def getInstance(cls):
from dm.common.service.impl.authorizationManager import AuthorizationManager
try:
am = AuthorizationManager()
except AuthorizationManager, ex:
am = ex
return am
# Singleton instance.
__instance = None
def __init__(self):
if AuthorizationManager.__instance:
raise AuthorizationManager.__instance
AuthorizationManager.__instance = self
DmObjectManager.__init__(self)
self.configurationManager = ConfigurationManager.getInstance()
self.principalRetriever = None
self.principalAuthenticatorList = []
self.objectCache = ObjectCache(AuthorizationManager.DEFAULT_CACHE_SIZE, AuthorizationManager.DEFAULT_CACHE_OBJECT_LIFETIME)
self.configure()
def createObjectInstance(self, moduleName, className, constructor):
self.logger.debug('Creating object: %s, %s, %s' % (moduleName, className, constructor))
cmd = 'from %s import %s' % (moduleName, className)
exec cmd
cmd = 'objectInstance = %s' % (constructor)
exec cmd
return objectInstance
@classmethod
def cryptPassword(cls, cleartext):
return CryptUtility.cryptPassword(cleartext)
@classmethod
def cryptPasswordWithPbkdf2(cls, cleartext):
return CryptUtility.cryptPasswordWithPbkdf2(cleartext)
def configure(self):
configItems = self.configurationManager.getConfigItems(AuthorizationManager.CONFIG_SECTION_NAME)
self.logger.debug('Got config items: %s' % configItems)
adminGroupName = self.configurationManager.getConfigOption(AuthorizationManager.CONFIG_SECTION_NAME, AuthorizationManager.ADMIN_GROUP_NAME_KEY)
# Create principal retriever
principalRetriever = self.configurationManager.getConfigOption(AuthorizationManager.CONFIG_SECTION_NAME, AuthorizationManager.PRINCIPAL_RETRIEVER_KEY)
(moduleName,className,constructor) = self.configurationManager.getModuleClassConstructorTuple(principalRetriever)
self.logger.debug('Creating principal retriever class: %s' % className)
self.principalRetriever = self.createObjectInstance(moduleName, className, constructor)
self.principalRetriever.setAdminGroupName(adminGroupName)
self.logger.debug('Authorization principal retriever: %s' % (self.principalRetriever))
# Create principal authenticators
for (key,value) in configItems:
if key.startswith(AuthorizationManager.PRINCIPAL_AUTHENTICATOR_KEY):
(moduleName,className,constructor) = self.configurationManager.getModuleClassConstructorTuple(value)
self.logger.debug('Creating principal authenticator class: %s' % className)
principalAuthenticator = self.createObjectInstance(moduleName, className, constructor)
self.addAuthorizationPrincipalAuthenticator(principalAuthenticator)
self.logger.debug('Authorization principal authenticator: %s' % (principalAuthenticator))
def addAuthorizationPrincipalAuthenticator(self, principalAuthenticator):
self.principalAuthenticatorList.append(principalAuthenticator)
def getAuthorizationPrincipal(self, username, password):
""" Get principal based on a username and password """
# First try cache.
#self.logger.debug('Trying username %s from the cache' % username)
principal = None
principalTuple = self.objectCache.get(username)
if principalTuple is not None:
(id, principal, updateTime, expirationTime) = principalTuple
if principal is None:
# Try principal retriever
principal = self.principalRetriever.getAuthorizationPrincipal(username)
if principal is None:
self.logger.debug('No principal for username: %s' % username)
return
# Try all authorization principal authenticators.
for principalAuthenticator in self.principalAuthenticatorList:
self.logger.debug('Attempting to authenticate %s by %s' % (username, principalAuthenticator.getName()))
authenticatedPrincipal = principalAuthenticator.authenticatePrincipal(principal, password)
if authenticatedPrincipal is not None:
self.logger.debug('Adding authorization principal %s to the cache, authenticated by %s' % (principal.getName(),principalAuthenticator.getName()))
self.objectCache.put(username, authenticatedPrincipal)
return authenticatedPrincipal
return None
def removeAuthorizationPrincipal(self, username):
""" Clear principal from the cache. """
self.objectCache.remove(username)
#######################################################################
# Testing.
if __name__ == '__main__':
am = AuthorizationManager.getInstance()
authPrincipal = am.getAuthorizationPrincipal('sveseli', 'sv')
print 'Auth principal: ', authPrincipal
#!/usr/bin/env python
from dm.common.utility.loggingManager import LoggingManager
class AuthorizationPrincipalAuthenticator:
def __init__(self, name=None):
self.name = name
self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__)
def getName(self):
return self.name
def authenticatePrincipal(self, principal, password):
return None
#######################################################################
# Testing.
if __name__ == '__main__':
pass
#!/usr/bin/env python
from dm.common.utility.loggingManager import LoggingManager
class AuthorizationPrincipalRetriever:
def __init__(self, name=None):
self.adminGroupName = None
self.name = name
self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__)
def getName(self):
return self.name
def setAdminGroupName(self, adminGroupName):
self.adminGroupName = adminGroupName
def getAuthorizationPrincipal(self, username):
return None
#######################################################################
# Testing.
if __name__ == '__main__':
pass
#!/usr/bin/env python
from dm.common.utility.cryptUtility import CryptUtility
from authorizationPrincipalAuthenticator import AuthorizationPrincipalAuthenticator
class CryptedPasswordPrincipalAuthenticator(AuthorizationPrincipalAuthenticator):
def __init__(self):
AuthorizationPrincipalAuthenticator.__init__(self, self.__class__.__name__)
def authenticatePrincipal(self, principal, password):
if principal is not None:
principalToken = principal.getToken()
if principalToken is not None and len(principalToken):
if CryptUtility.verifyPasswordWithPbkdf2(password, principalToken):
self.logger.debug('Authentication successful for %s' % principal.getName())
return principal
else:
self.logger.debug('Authentication failed for %s' % principal.getName())
else:
self.logger.debug('Token is empty for %s, authentication not performed' % principal.getName())
return None
#######################################################################
# Testing.
if __name__ == '__main__':
pass
#!/usr/bin/env python
from dm.common.constants import dmRole
from dm.common.objects.authorizationPrincipal import AuthorizationPrincipal
from dm.common.db.api.userDbApi import UserDbApi
from authorizationPrincipalRetriever import AuthorizationPrincipalRetriever
class DbPrincipalRetriever(AuthorizationPrincipalRetriever):
def __init__(self):
AuthorizationPrincipalRetriever.__init__(self, self.__class__.__name__)
self.dbApi = UserDbApi()
def getAuthorizationPrincipal(self, username):
principal = None
try:
user = self.dbApi.getUserWithPasswordByUsername(username)
principal = AuthorizationPrincipal(username, user.get('password'))
principal.setRole(dmRole.DM_USER_ROLE)
principal.setUserInfo(user)
if self.adminGroupName is not None:
for userGroup in user.get('userGroupList', []):
if userGroup.get('name') == self.adminGroupName:
principal.setRole(dmRole.DM_ADMIN_ROLE)
except Exception, ex:
self.logger.debug(ex)
return principal
#######################################################################
# Testing.
if __name__ == '__main__':
pass
#!/usr/bin/env python
from dm.common.utility.ldapUtility import LdapUtility
from authorizationPrincipalAuthenticator import AuthorizationPrincipalAuthenticator
class LdapPasswordPrincipalAuthenticator(AuthorizationPrincipalAuthenticator):
def __init__(self, serverUrl, dnFormat):
AuthorizationPrincipalAuthenticator.__init__(self, self.__class__.__name__)
self.ldapUtility = LdapUtility(serverUrl, dnFormat)
def authenticatePrincipal(self, principal, password):
if principal is not None:
try:
self.logger.debug('Checking credentials for %s' % principal.getName())
self.ldapUtility.checkCredentials(principal.getName(), password)
return principal
except Exception, ex:
self.logger.debug(ex)
return None
#######################################################################
# Testing.
if __name__ == '__main__':
pass
#!/usr/bin/env python
from dm.common.constants import dmRole
from dm.common.objects.authorizationPrincipal import AuthorizationPrincipal
from dm.common.utility.cryptUtility import CryptUtility
from authorizationPrincipalRetriever import AuthorizationPrincipalRetriever
class NoOpPrincipalRetriever(AuthorizationPrincipalRetriever):
def __init__(self):
AuthorizationPrincipalRetriever.__init__(self, self.__class__.__name__)
def getAuthorizationPrincipal(self, username):
noOpPassword = CryptUtility.cryptPasswordWithPbkdf2(username)
principal = AuthorizationPrincipal(username, noOpPassword)
principal.setRole(dmRole.DM_USER_ROLE)
if self.adminGroupName is not None:
principal.setRole(dmRole.DM_ADMIN_ROLE)
return principal
#######################################################################
# Testing.
if __name__ == '__main__':
pass
#!/usr/bin/env python
import cherrypy
import urllib
from cherrypy.lib import httpauth
from dm.common.constants import dmStatus
from dm.common.constants import dmRole
from dm.common.constants import dmHttpStatus
from dm.common.exceptions.dmException import DmException
from dm.common.exceptions.dmHttpError import DmHttpError
from dm.common.exceptions.authorizationError import AuthorizationError
from dm.common.utility.loggingManager import LoggingManager
from dm.common.service.dmController import DmController
from dm.common.service.impl.authorizationManager import AuthorizationManager
class LoginController(DmController):
""" Controller to provide login and logout actions. """
SESSION_USERNAME_KEY = '_cp_username'
SESSION_USER_KEY = 'user'
SESSION_ROLE_KEY = 'role'
INVALID_SESSION_KEY = 'invalidSession'
_cp_config = {
'tools.sessions.on' : True,
'tools.auth.on' : True
}
def __init__(self):
DmController.__init__(self)
def onLogin(self, username):
""" Called on successful login. """
return
def onLogout(self, username):
""" Called on logout. """
return
@classmethod
def getLoginForm(cls, msg='Enter username and password:', username='', fromPage='/'):
return """
<html>
<body>
<form method='post' action='/auth/login'>
<input type='hidden' name='fromPage' value='%(fromPage)s' />
<h2>DM Service</h2>
<p/>
%(msg)s
<p/>
<table border='0'>
<tr>
<td>Username:</td><td><input type='text' name="username" value='%(username)s'/></td>
</tr>
<tr>
<td>Password:</td><td><input type='password' name='password' /></td>
</tr>
<p/>
<tr>
<td></td>
<td><input type='submit' value='Log In' /></td>
</tr>
</table>
</form>
</body>
</html>""" % locals()
@classmethod
def parseBasicAuthorizationHeaders(cls):
try:
username = None
password = None
authorization = cherrypy.request.headers['authorization']
authorizationHeader = httpauth.parseAuthorization(authorization)
if authorizationHeader['auth_scheme'] == 'basic':
username = authorizationHeader['username']
password = authorizationHeader['password']
if username and password:
return (username, password)
else:
raise AuthorizationError('Username and/or password not supplied.')
except Exception, ex:
errorMsg = 'Could not extract username/password from authorization header: %s' % ex
raise AuthorizationError(errorMsg)
@classmethod
def checkCredentials(cls, username, password):
""" Verifies credentials for username and password."""
logger = LoggingManager.getInstance().getLogger('LoginController:checkCredentials')
logger.debug('Checking credential for User: %s' % (username))
#logger.debug('Checking credential for User: %s, Password: %s' % (username, password))
logger.debug('Session id: %s' % cherrypy.serving.session.id)
principal = AuthorizationManager.getInstance().getAuthorizationPrincipal(username, password)
logger.debug('Principal: %s' % (principal))
if principal:
cherrypy.session[LoginController.SESSION_ROLE_KEY] = principal.getRole()
logger.debug('Successful login from user: %s (role: %s)' % (username, principal.getRole()))
else:
logger.debug('Login denied for user: %s' % username)
username = cherrypy.session.get(LoginController.SESSION_USERNAME_KEY, None)
if username is not None:
cherrypy.request.login = None
cherrypy.session[LoginController.INVALID_DM_SESSION_KEY] = True
raise AuthorizationError('Incorrect username or password.')
cherrypy.session[LoginController.SESSION_USER_KEY] = principal.getUserInfo()
return principal
@classmethod
def checkAuthorization(cls, *args, **kwargs):
"""
A tool that looks in config for 'auth.require'. If found and it
is not None, a login is required and the entry is evaluated as a list of
conditions that the user must fulfill.
"""
logger = LoggingManager.getInstance().getLogger('LoginController:checkAuthorization')
conditions = cherrypy.request.config.get('auth.require', None)
#logger.debug('Headers: %s' % (cherrypy.request.headers))
#logger.debug('Request params: %s' % (cherrypy.request.params))
#logger.debug('Request query string: %s' % (cherrypy.request.query_string))
method = urllib.quote(cherrypy.request.request_line.split()[0])
params = urllib.quote(cherrypy.request.request_line.split()[1])
if conditions is None:
logger.debug('No conditions imposed')
return
sessionId = cherrypy.serving.session.id
sessionCache = cherrypy.session.cache
#logger.debug('Session: %s' % ((cherrypy.session.__dict__)))
#logger.debug('Session cache length: %s' % (len(sessionCache)))
#logger.debug('Session cache: %s' % (sessionCache))
# Check session.
if not sessionCache.has_key(sessionId):
errorMsg = 'Invalid or expired session id: %s.' % sessionId
logger.debug(errorMsg)
raise DmHttpError(dmHttpStatus.DM_HTTP_UNAUTHORIZED, 'User Not Authorized', AuthorizationError(errorMsg))
username = cherrypy.session.get(LoginController.SESSION_USERNAME_KEY)
logger.debug('Session id %s is valid (username: %s)' % (sessionId, username))
if username:
cherrypy.request.login = username
for condition in conditions:
# A condition is just a callable that returns true or false
if not condition():
logger.debug('Authorization check %s failed for username %s' % (condition.func_name, username))
errorMsg = 'Authorization check %s failed for user %s.' % (condition.func_name, username)
raise DmHttpError(dmHttpStatus.DM_HTTP_UNAUTHORIZED, 'User Not Authorized', AuthorizationError(errorMsg))
else:
logger.debug('Username is not supplied')
raise DmHttpError(dmHttpStatus.DM_HTTP_UNAUTHORIZED, 'User Not Authorized', ex)
@cherrypy.expose
def login(self, username=None, password=None, fromPage='/'):
self.logger.debug('Attempting login from username %s' % (username))
try:
if username is None or password is None:
self.logger.debug('Parsing auth headers for username %s' % (username))
(username, password) = LoginController.parseBasicAuthorizationHeaders()
self.logger.debug('Retrieving principal for username %s' % (username))
principal = LoginController.checkCredentials(username, password)
except DmHttpError, ex:
raise
except DmException, ex:
self.logger.debug('Authorization failed (username %s): %s' % (username, ex))
self.addDmExceptionHeaders(ex)
raise DmHttpError(dmHttpStatus.DM_HTTP_UNAUTHORIZED, 'User Not Authorized', ex)
# Authorization worked.
cherrypy.session[LoginController.SESSION_USERNAME_KEY] = cherrypy.request.login = username
self.onLogin(username)
self.addDmSessionRoleHeaders(principal.getRole())
self.addDmResponseHeaders()
@cherrypy.expose
def logout(self, fromPage='/'):
sess = cherrypy.session
username = sess.get(LoginController.SESSION_USERNAME_KEY, None)
if username:
del sess[LoginController.SESSION_USERNAME_KEY]
cherrypy.request.login = None
self.onLogout(username)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment