Commit f1473eb4 authored by Jacek Furmankiewicz's avatar Jacek Furmankiewicz

restarting fine-grained security work

parent 1d20fc0e
...@@ -3,12 +3,29 @@ Enhancements to core Twisted security ...@@ -3,12 +3,29 @@ Enhancements to core Twisted security
@author: jacekf @author: jacekf
''' '''
from twisted.cred.checkers import ICredentialsChecker, FilePasswordDB from twisted.cred.checkers import ICredentialsChecker
from zope.interface import implements from zope.interface import implements
from beaker.cache import CacheManager from beaker.cache import CacheManager
from beaker.util import parse_cache_config_options from beaker.util import parse_cache_config_options
class Principal:
'''A security principal with privileges attached to it'''
def __init__(self,userId,privileges=None):
'''
@param userId -- mandatory user ID
@param privileges -- list of privileges assigned to this user
'''
self.__userId = userId
self.__privileges = privileges
@property
def userId(self):
return self.__userId
@property
def privileges(self):
return self.__privileges
class CachedCredentialsChecker: class CachedCredentialsChecker:
"""A cached credentials checker wrapper. It will forward calls to the actual credentials checker only when the cache expires (or on first call)""" """A cached credentials checker wrapper. It will forward calls to the actual credentials checker only when the cache expires (or on first call)"""
...@@ -18,32 +35,27 @@ class CachedCredentialsChecker: ...@@ -18,32 +35,27 @@ class CachedCredentialsChecker:
self.credentialInterfaces = credentialInterfaces self.credentialInterfaces = credentialInterfaces
self.checker = credentialsChecker self.checker = credentialsChecker
def requestAvatarId(self,credentials): #initialize cache
pass cacheOptions = {
'cache.type': 'memory',
class SqlCredentialsChecker: }
"""A SQL checked to compare usernames and passwords to a DB table, with support for custom comparison (plaintext, hash, etc)""" self.cache = CacheManager(**parse_cache_config_options(cacheOptions))
implements(ICredentialsChecker)
def __init__(self,dbpool,userTable,usernameColumn,passwordColumn,passwordChecker = None):
"""Constructor
@param dbpool: adbapi DB connection pool
@param userTable: Name of the table containing list of users
@param userameColumn: Name of column containing the user name
@param passwordColumn: Name of column containing the user password (or its hash)
@param passwordChecker: A lambda that compares the incoming password due what is stored in DB (plaintext comparison (not recommended, insecure), hash, decryption, etc.)
"""
self.dbpool = dbpool
self.userTable = userTable
self.usernameColumn = usernameColumn
self.passwordColumn = passwordColumn
self.passwordChecker = passwordChecker
def requestAvatarId(self,credentials): def requestAvatarId(self,credentials):
pass pass
##################################################################################################
#
# DECORATORS
#
##################################################################################################
def secured(privileges=None):
'''
Main decorator for securing REST endpoints via roles
'''
pass
......
'''
Stand-alone security module with support for role-based authorization and IP address range checking
@author: jacekf
'''
#from IPy import IP
class SecurityRole:
'''Represents a security role'''
def __init__(self,roleId,ipRanges=None):
'''
roleId -- role roleId
ipRanges -- list of ipRanges in any of the formats accepted by the IPy library
'''
self.__roleId = roleId
self.__ipRanges = ipRanges
@property
def roleId(self):
return self.__roleId
class Principal:
'''An enhanced Avatar with roles & IP Address ranges attached to it'''
def __init__(self,userId,roles=None,ipRanges=None):
'''
userId -- mandatory user ID
roles -- list of optional SecurityRole instances
ipRanges - user-specific IP ranges (checked before the role ones)
'''
self.__userId = userId
self.__roles = roles
self.__ipRanges = ipRanges
@property
def userId(self):
return self.__userId
def validateAccess(self,expectedRoles,ipAddress):
'''
Validates if the current Principal has access to any of the expected roles, coming from the current request IP address
'''
pass
class SqlCredentialsFactory:
'''A default credentials factory configured to obtain credentials / roles / IP address ranges via custom SQL queries'''
def __init__(self,
dbConnectionPool,userQuery="SELECT USER_ID FROM USER",
userIpRangeQuery = "SELECT IP_RANGE FROM USER_IP_RANGE WHERE USER_ID = :userId",
roleQuery="SELECT R.ROLE_ID FROM USER_ROLE UR, ROLE R WHERE UR.USER_ID = :userId AND UR.ROLE_PK = R.ROLE_PK",
roleIpRangeQuery="SELECT IP_RANGE FROM ROLE_IP_RANGE WHERE ROLE_ID = :roleId"):
'''
Constructor that allows passing custom SQL queries to get the desired info. Override with your custom queries to fit your data model:
userQuery -- SQL query to retrieve list of users. Should return just the user ID column
userIpRangeQuery -- query to get IP ranges associated with user. Pass in None to disable this functionality
roleQuery -- query to get list of roles for user. Pass in None to disable this functionality
roleIpRangeQuery - query to get IP range string column associated with a particular role. Pass in None to disable this functionality
'''
pass
##################################################################################################
#
# DECORATORS
#
##################################################################################################
def secure(roles=None):
'''
Main decorator for securing REST endpoints via roles
'''
pass
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment