It is not difficult to extend content types to act as (parts of) users or groups with membrane. The only thing that is needed is to make sure that your content is adaptable to one of the membrane interfaces. You can either make your content type implement the interfaces directly, or register separate adapters.
To expose a content object as a user you must implement the IMembraneUserObject interface. This is a very minimal interface which membrane users to find the userid and the login name for a user. This is a minimal implementation:
from plone.dexterity.content import Item from Products.membrane.interfaces import IMembraneUserAuth from plone.uuid.interfaces import IUUID class MyContent(Item): pass class MyContentUer(object): def __init__(self, context): self.context=context def getUserId(self): return IUUID(self.context, None) def getUserName(self): return self.context.login
Needs to be implemented by anything user-related so we can easily determine the unique user that the object is related to.
This is the base interface that all objects that want to provide a user via membrane much implement or be adaptable to.
Return the name used for login. This can be the same as the userid, but might also be something different such as the users email address.
Return the unique identifier for the user that this piece of content relates to. Historically the Archetype UID of an object is used as userid.
If you use adapters to implement the membrane interfaces, and you also implement one of the other membrane interfaces you do not need to register the IMembraneUserObject adapter separately since all other interfaces are derived from it. It is recommended to use a MyContentUser-like class as base class for all adapters to make sure the getUserId() and getUserName() implemenations are not duplicated.
If you want a user to be able to login in a site you must add authentication support to your user content type. This is handled through the IMembraneUserAuth interface. Below is an example for a very basic authentication handler which users a plaintext password attribute. This uses the BaseUserObject class shown above.
The example below demonstrates a very simple authentication adapter. It uses the MyContentUser class shown earlier, and uses five.grok to create the adapter so you do not need to write any ZCML.
from plone.dexterity.content import Item from five import grok from Products.membrane.interfaces import IMembraneUserAuth from plone.uuid.interfaces import IUUID class MyContent(Item): pass class MyUserAuthentication(grok.Adapter, MyContentUser): grok.context(Content) grok.implements(IMembraneUserAuth) def authenticateCredentials(self, credentials): if self.context.password==credentials["password"]: return (self.getUserId(), self.getUserName()) return None
Extends: Products.membrane.interfaces.user.IMembraneUserObject, Products.PluggableAuthService.interfaces.plugins.IAuthenticationPlugin
Used for objects that can handle user authentication.
Every user in a Plone site will be able to use the standard user property support as provided by PluggableAuthService. Often you want to be able to use a content object to manage properties for a user, for example to be able to use standard edit screens to manage certain properties. You can do this by via the IMembraneUserProperties interface.
Extends: Products.membrane.interfaces.user.IMembraneUserObject, Products.PlonePAS.interfaces.plugins.IMutablePropertiesPlugin
Used for objects that can provide user properties.