A couple implementation thoughts:
Method gets could return a wrapper on the method, that might look like:
class MethodWrapper(object):
def __init__(self, method, credential):
self.method = method
self.credential = credential
def __call__(self, *args, **kw):
if not credential.execute(self.method, *args, **kw):
raise AccessDenied(...)
return self.method(*args, **kw)
Then the credential could have a more fine-grained access control over
methods. I would think the credential should also be passed the object
in question as well, i.e., credential.write(self, name). Oh, and I
think you want __setattribute__ instead of __setattr__.
That said... hmm. I feel like a proxy would work better. Then you
would wrap an object in a proxy, and that proxy would also be associated
with a user. This way you could wrap global objects for access by
individual users, where in this case you would have to recreate the
object for each user.
There's certainly more to think about in this. For instance, what state
should the credential carry around? How should it be attached to actual
access? Implicit, or explicit? I personally don't see anything wrong
with passing the credential around explicitly. I'm more interested on
what its internal state should look like.
Well, food for thought...
On Thu, 2003-05-08 at 12:50, Matt Feifarek wrote:
> Hello Webware folks.
>
> We've been working on some account-kit type code (we hope to contribute
> the results of this account stuff to the Sandbox soon) and I whomped up
> this security experiment.
>
> My idea was to use the new "metaclass" feature in concert with some of
> its the new low-level methods to implement read, write, execute security
> on the sub-object (attribute) level. Then, an application level class
> (like a CMS object) could inherit from this security class and have
> easy/transparent security.
>
> The code is fairly elementary, and the security tests it is doing are
> not interesting (just a look up table) but we'd appreciate the
> collective wisdom and comments of this group.
>
> This will probably only work in Python 2.2.
>
> Stuff we're interested in:
> - is this likely to be future-proof with python's evolution?
> - is this a good/stupid idea?
> - did someone do this already in a mature package?
> - any ideas for more interesting/useful credential system?
> - anything else you may care to add
>
> Thanks!
>
> ______________________________________________________________________
>
> # Copyright (C) 2003, dAlchemy, Inc: http://www.dalchemy.com
>
> from types import MethodType
>
> class AccessDenied( Exception ):
> '''probably should put some logging code in here or something'''
> pass
>
>
> class Secure(object):
> '''An abstract class that checks for security when accessing attributes and methods'''
> # note that this class defaults to no access; it can easily be extended to also have
> # default full-access, and this could then be set by a switch
> def __init__( self, credential=None ):
> '''Pass in a hook to a credential object...'''
> object.__setattr__(self,'_credential',credential)
>
>
> def __getattribute__( self, name ):
> '''override the base to put in security checks'''
> # this one covers read and execute access...
> # read is for attributes, execute is for methods
> credential = object.__getattribute__(self,"_credential")
>
>
> # this logic tree can no-doubt be tightened-up, but I want it to be clear
> # for illustrative purposees
> thing = object.__getattribute__(self,name)
> if credential:
> if type(thing) == MethodType:
> if credential.execute(name):
> return thing
> else:
> raise AccessDenied, "Don't have execute access for %s()" % name
> else:
> # if it's not a method, we're interested in read access
> if credential.read(name):
> return thing
> else:
> raise AccessDenied, "Don't have read access for %s" % name
> else:
> raise AccessDenied, "No credentials for %s" % name
>
>
> def __setattr__( self, name, value ):
> '''override the base to put in security checks'''
> credential = object.__getattribute__(self,"_credential")
> if credential and credential.write(name):
> object.__setattr__(self,name,value)
> else:
> raise AccessDenied, "Don't have write access to %s" % name
>
>
>
> class Credential(object):
> '''This is a highly bogus object, just set up to provide me something to test against;
> this lookup table can be replaced with application-meaningful tests, of course'''
> def __init__( self ):
> self._read = {
> 'zilch':1,
> 'foo':0,
> 'bar':1,
> }
> self._write = {
> 'zilch':1,
> 'foo':1,
> }
> self._execute = {
> 'bar':0,
> 'baz':1,
> }
>
> def read( self, item ):
> return self._read.get(item)
>
> def write( self, item ):
> return self._write.get(item)
>
> def execute( self, item ):
> return self._execute.get(item)
>
>
>
> ############################################################################################
> ## this is a bogus test class
>
> class Test( Secure ):
> def __init__(self,credential=None):
> Secure.__init__(self,credential)
> self.foo = "foo attr"
> self.zilch ="zilch attr"
>
> def bar(self):
> return "bar method"
>
> def baz( self ):
> return "baz method"
>
>
> ############################################################################################
>
> test = Test( Credential() )
>
> # now, do something like
>
> print test.foo # this will crash
> print test.zilch = "overwrite"
>
> print test.bar() # this will crash
> print test.baz()
>
>
|