src/pyams_security/utility.py
changeset 0 f04e1d0a0723
child 2 94e76f8e9828
equal deleted inserted replaced
-1:000000000000 0:f04e1d0a0723
       
     1 #
       
     2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
       
     3 # All Rights Reserved.
       
     4 #
       
     5 # This software is subject to the provisions of the Zope Public License,
       
     6 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
       
     7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
       
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
       
    10 # FOR A PARTICULAR PURPOSE.
       
    11 #
       
    12 
       
    13 __docformat__ = 'restructuredtext'
       
    14 
       
    15 
       
    16 # import standard library
       
    17 
       
    18 # import interfaces
       
    19 from pyams_security.interfaces import ISecurityManager, ICredentialsPlugin, IAuthenticationPlugin, \
       
    20     IDirectoryPlugin, AuthenticatedPrincipalEvent
       
    21 from pyramid.interfaces import IAuthenticationPolicy
       
    22 
       
    23 # import packages
       
    24 from pyams_security.principal import UnknownPrincipal, MissingPrincipal
       
    25 from pyams_utils.registry import query_utility
       
    26 from pyams_utils.request import check_request
       
    27 from pyramid.authentication import AuthTktCookieHelper
       
    28 from pyramid.decorator import reify
       
    29 from pyramid.security import Everyone, Authenticated
       
    30 from zope.container.folder import Folder
       
    31 from zope.interface import implementer
       
    32 from zope.schema.fieldproperty import FieldProperty
       
    33 
       
    34 
       
    35 @implementer(ISecurityManager)
       
    36 class SecurityManager(Folder):
       
    37     """Security manager utility"""
       
    38 
       
    39     enable_social_login = FieldProperty(ISecurityManager['enable_social_login'])
       
    40     authomatic_secret = FieldProperty(ISecurityManager['authomatic_secret'])
       
    41     social_login_use_popup = FieldProperty(ISecurityManager['social_login_use_popup'])
       
    42     open_registration = FieldProperty(ISecurityManager['open_registration'])
       
    43     users_folder = FieldProperty(ISecurityManager['users_folder'])
       
    44 
       
    45     authentication_plugins_names = FieldProperty(ISecurityManager['authentication_plugins_names'])
       
    46     directory_plugins_names = FieldProperty(ISecurityManager['directory_plugins_names'])
       
    47 
       
    48     @property
       
    49     def credentials_plugins_names(self):
       
    50         request = check_request()
       
    51         policy = request.registry.queryUtility(IAuthenticationPolicy)
       
    52         return policy.credentials_names
       
    53 
       
    54     def __setitem__(self, key, value):
       
    55         super(SecurityManager, self).__setitem__(key, value)
       
    56         if IAuthenticationPlugin.providedBy(value):
       
    57             self.authentication_plugins_names += (key,)
       
    58         if IDirectoryPlugin.providedBy(value):
       
    59             self.directory_plugins_names += (key,)
       
    60 
       
    61     def __delitem__(self, key):
       
    62         super(SecurityManager, self).__delitem__(key)
       
    63         if key in self.authentication_plugins_names:
       
    64             del self.authentication_plugins_names[self.authentication_plugins_names.index(key)]
       
    65         if key in self.directory_plugins_names:
       
    66             del self.directory_plugins_names[self.directory_plugins_names.index(key)]
       
    67 
       
    68     def get_plugin(self, name):
       
    69         if name in self.credentials_plugins_names:
       
    70             return query_utility(ICredentialsPlugin, name=name)
       
    71         elif name:
       
    72             return self.get(name)
       
    73 
       
    74     def get_credentials_plugins(self, request=None):
       
    75         if request is None:
       
    76             request = check_request()
       
    77         policy = request.registry.queryUtility(IAuthenticationPolicy)
       
    78         for plugin in policy.credentials_plugins:
       
    79             if plugin is not None:
       
    80                 yield plugin
       
    81 
       
    82     def get_authentication_plugins(self):
       
    83         for name in self.authentication_plugins_names or ():
       
    84             plugin = self.get(name)
       
    85             if IAuthenticationPlugin.providedBy(plugin):
       
    86                 yield plugin
       
    87 
       
    88     def get_directory_plugins(self):
       
    89         for name in self.directory_plugins_names or ():
       
    90             plugin = self.get(name)
       
    91             if IDirectoryPlugin.providedBy(plugin):
       
    92                 yield plugin
       
    93 
       
    94     # IAuthenticationInfo interface methods
       
    95     def extract_credentials(self, request, **kwargs):
       
    96         for plugin in self.get_credentials_plugins():
       
    97             credentials = plugin.extract_credentials(request, **kwargs)
       
    98             if credentials:
       
    99                 return credentials
       
   100 
       
   101     def authenticate(self, credentials, request):
       
   102         for plugin in self.get_authentication_plugins():
       
   103             principal_id = plugin.authenticate(credentials, request)
       
   104             if principal_id is not None:
       
   105                 request.registry.notify(AuthenticatedPrincipalEvent(plugin.prefix, principal_id))
       
   106                 return principal_id
       
   107 
       
   108     def authenticated_userid(self, request):
       
   109         credentials = self.extract_credentials(request)
       
   110         if credentials is None:
       
   111             return None
       
   112         principal = self.authenticate(credentials, request)
       
   113         if principal is not None:
       
   114             principal = self.get_principal(principal.id)
       
   115             if principal is not None:
       
   116                 return principal.id
       
   117 
       
   118     def effective_principals(self, principal_id):
       
   119         principals = set()
       
   120         for plugin in self.get_directory_plugins():
       
   121             principals |= set(plugin.get_all_principals(principal_id))
       
   122         return principals
       
   123 
       
   124     # IDirectoryPlugin interface methods
       
   125     def get_principal(self, principal_id):
       
   126         if not principal_id:
       
   127             return UnknownPrincipal
       
   128         for plugin in self.get_directory_plugins():
       
   129             principal = plugin.get_principal(principal_id)
       
   130             if principal is not None:
       
   131                 return principal
       
   132         return MissingPrincipal(id=principal_id)
       
   133 
       
   134     def find_principals(self, query):
       
   135         principals = set()
       
   136         for plugin in self.get_directory_plugins():
       
   137             principals |= set(plugin.find_principals(query) or set())
       
   138         return sorted(principals, key=lambda x: x.title)
       
   139 
       
   140 
       
   141 @implementer(IAuthenticationPolicy)
       
   142 class PyAMSAuthenticationPolicy(object):
       
   143     """PyAMS authentication policy
       
   144 
       
   145     This authentication policy relies on a registered ISecurityManager utility.
       
   146     Use same authentication ticket as AuthTktAuthenticationPolicy.
       
   147 
       
   148     ``credentials`` is the list of credentials extraction utilities which can be
       
   149     used to get credentials.
       
   150 
       
   151     See `pyramid.authentication.AuthTktAuthenticationPolicy` to get description
       
   152     of other constructor arguments.
       
   153     """
       
   154 
       
   155     def __init__(self, secret,
       
   156                  credentials=('http', 'session'),
       
   157                  cookie_name='auth_ticket',
       
   158                  secure=False,
       
   159                  include_ip=False,
       
   160                  timeout=None,
       
   161                  reissue_time=None,
       
   162                  max_age=None,
       
   163                  path="/",
       
   164                  http_only=False,
       
   165                  wild_domain=True,
       
   166                  hashalg='sha256',
       
   167                  parent_domain=False,
       
   168                  domain=None):
       
   169         self.credentials_names = credentials
       
   170         self.cookie = AuthTktCookieHelper(secret,
       
   171                                           cookie_name=cookie_name,
       
   172                                           secure=secure,
       
   173                                           include_ip=include_ip,
       
   174                                           timeout=timeout,
       
   175                                           reissue_time=reissue_time,
       
   176                                           max_age=max_age,
       
   177                                           http_only=http_only,
       
   178                                           path=path,
       
   179                                           wild_domain=wild_domain,
       
   180                                           hashalg=hashalg,
       
   181                                           parent_domain=parent_domain,
       
   182                                           domain=domain)
       
   183 
       
   184     @reify
       
   185     def credentials_plugins(self):
       
   186         return [query_utility(ICredentialsPlugin, name=name)
       
   187                 for name in self.credentials_names]
       
   188 
       
   189     def _get_security_manager(self, request):
       
   190         return query_utility(ISecurityManager)
       
   191 
       
   192     def unauthenticated_userid(self, request):
       
   193         result = self.cookie.identify(request)
       
   194         if result:
       
   195             return result['userid']
       
   196         for plugin in self.credentials_plugins:
       
   197             if plugin is not None:
       
   198                 credentials = plugin.extract_credentials(request)
       
   199                 if credentials is not None:
       
   200                     return credentials.id
       
   201 
       
   202     def authenticated_userid(self, request):
       
   203         principal_id = self.unauthenticated_userid(request)
       
   204         if principal_id:
       
   205             return principal_id
       
   206         manager = self._get_security_manager(request)
       
   207         if manager is not None:
       
   208             return manager.authenticated_userid(request)
       
   209 
       
   210     def effective_principals(self, request):
       
   211         principals = {Everyone}
       
   212         principal_id = self.unauthenticated_userid(request)
       
   213         if principal_id:
       
   214             principals.add(Authenticated)
       
   215             principals.add(principal_id)
       
   216             manager = self._get_security_manager(request)
       
   217             if manager is not None:
       
   218                 principals |= set(manager.effective_principals(principal_id))
       
   219         print(principals)
       
   220         return principals
       
   221 
       
   222     def remember(self, request, principal, **kw):
       
   223         return self.cookie.remember(request, principal, **kw)
       
   224 
       
   225     def forget(self, request):
       
   226         return self.cookie.forget(request)
       
   227 
       
   228 
       
   229 def get_principal(request):
       
   230     """Get principal associated with given request"""
       
   231     manager = query_utility(ISecurityManager)
       
   232     if manager is not None:
       
   233         principal_id = request.authenticated_userid
       
   234         if principal_id:
       
   235             return manager.get_principal(principal_id)
       
   236         else:
       
   237             return UnknownPrincipal