Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / auth_providers.py @ 469d0997

History | View | Annotate | Download (10.9 kB)

1 d2633501 Kostas Papadimitriou
# Copyright 2011 GRNET S.A. All rights reserved.
2 d2633501 Kostas Papadimitriou
#
3 d2633501 Kostas Papadimitriou
# Redistribution and use in source and binary forms, with or
4 d2633501 Kostas Papadimitriou
# without modification, are permitted provided that the following
5 d2633501 Kostas Papadimitriou
# conditions are met:
6 d2633501 Kostas Papadimitriou
#
7 d2633501 Kostas Papadimitriou
#   1. Redistributions of source code must retain the above
8 d2633501 Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
9 d2633501 Kostas Papadimitriou
#      disclaimer.
10 d2633501 Kostas Papadimitriou
#
11 d2633501 Kostas Papadimitriou
#   2. Redistributions in binary form must reproduce the above
12 d2633501 Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
13 d2633501 Kostas Papadimitriou
#      disclaimer in the documentation and/or other materials
14 d2633501 Kostas Papadimitriou
#      provided with the distribution.
15 d2633501 Kostas Papadimitriou
#
16 d2633501 Kostas Papadimitriou
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 d2633501 Kostas Papadimitriou
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 d2633501 Kostas Papadimitriou
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 d2633501 Kostas Papadimitriou
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 d2633501 Kostas Papadimitriou
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 d2633501 Kostas Papadimitriou
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 d2633501 Kostas Papadimitriou
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 d2633501 Kostas Papadimitriou
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 d2633501 Kostas Papadimitriou
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 d2633501 Kostas Papadimitriou
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 d2633501 Kostas Papadimitriou
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 d2633501 Kostas Papadimitriou
# POSSIBILITY OF SUCH DAMAGE.
28 d2633501 Kostas Papadimitriou
#
29 d2633501 Kostas Papadimitriou
# The views and conclusions contained in the software and
30 d2633501 Kostas Papadimitriou
# documentation are those of the authors and should not be
31 d2633501 Kostas Papadimitriou
# interpreted as representing official policies, either expressed
32 d2633501 Kostas Papadimitriou
# or implied, of GRNET S.A.
33 d2633501 Kostas Papadimitriou
34 d2633501 Kostas Papadimitriou
35 d2633501 Kostas Papadimitriou
from django.core.urlresolvers import reverse
36 d2633501 Kostas Papadimitriou
from django.utils.translation import ugettext as _
37 081070a5 Kostas Papadimitriou
from django.utils.datastructures import SortedDict
38 d2633501 Kostas Papadimitriou
39 081070a5 Kostas Papadimitriou
from django.conf import settings
40 c4b1a172 Kostas Papadimitriou
41 081070a5 Kostas Papadimitriou
from astakos.im import settings as astakos_settings
42 c4b1a172 Kostas Papadimitriou
from astakos.im import messages as astakos_messages
43 d2633501 Kostas Papadimitriou
44 d2633501 Kostas Papadimitriou
import logging
45 dd5f8f4d Kostas Papadimitriou
import urllib
46 d2633501 Kostas Papadimitriou
47 d2633501 Kostas Papadimitriou
logger = logging.getLogger(__name__)
48 d2633501 Kostas Papadimitriou
49 d2633501 Kostas Papadimitriou
# providers registry
50 6012535a Kostas Papadimitriou
PROVIDERS = {}
51 badcb2a9 Kostas Papadimitriou
REQUIRED_PROVIDERS = {}
52 d2633501 Kostas Papadimitriou
53 d2633501 Kostas Papadimitriou
class AuthProviderBase(type):
54 d2633501 Kostas Papadimitriou
55 d2633501 Kostas Papadimitriou
    def __new__(cls, name, bases, dct):
56 d2633501 Kostas Papadimitriou
        include = False
57 d2633501 Kostas Papadimitriou
        if [b for b in bases if isinstance(b, AuthProviderBase)]:
58 d2633501 Kostas Papadimitriou
            type_id = dct.get('module')
59 d2633501 Kostas Papadimitriou
            if type_id:
60 d2633501 Kostas Papadimitriou
                include = True
61 081070a5 Kostas Papadimitriou
            if type_id in astakos_settings.IM_MODULES:
62 d2633501 Kostas Papadimitriou
                dct['module_enabled'] = True
63 d2633501 Kostas Papadimitriou
64 d2633501 Kostas Papadimitriou
        newcls = super(AuthProviderBase, cls).__new__(cls, name, bases, dct)
65 d2633501 Kostas Papadimitriou
        if include:
66 6012535a Kostas Papadimitriou
            PROVIDERS[type_id] = newcls
67 badcb2a9 Kostas Papadimitriou
            if newcls().is_required():
68 badcb2a9 Kostas Papadimitriou
                REQUIRED_PROVIDERS[type_id] = newcls
69 d2633501 Kostas Papadimitriou
        return newcls
70 d2633501 Kostas Papadimitriou
71 d2633501 Kostas Papadimitriou
72 d2633501 Kostas Papadimitriou
class AuthProvider(object):
73 d2633501 Kostas Papadimitriou
74 d2633501 Kostas Papadimitriou
    __metaclass__ = AuthProviderBase
75 d2633501 Kostas Papadimitriou
76 d2633501 Kostas Papadimitriou
    module = None
77 d2633501 Kostas Papadimitriou
    module_active = False
78 d2633501 Kostas Papadimitriou
    module_enabled = False
79 4bedb0d8 Kostas Papadimitriou
    one_per_user = True
80 450093ec Kostas Papadimitriou
    login_prompt = _('Login using ')
81 450093ec Kostas Papadimitriou
    primary_login_prompt = _('Login using ')
82 564a2292 Kostas Papadimitriou
    login_message = None
83 9c73d218 Kostas Papadimitriou
    logout_message = 'You may still be logged in %(provider)s though. Consider logging out from there too.'
84 564a2292 Kostas Papadimitriou
    remote_authenticate = True
85 564a2292 Kostas Papadimitriou
    remote_logout_url = None
86 9c73d218 Kostas Papadimitriou
    logout_from_provider_text = None
87 bd0f516a Kostas Papadimitriou
    icon_url = None
88 531fca05 Olga Brani
    icon_medium_url = None
89 ca5148f2 Kostas Papadimitriou
    method_prompt = None
90 d2633501 Kostas Papadimitriou
91 c4b1a172 Kostas Papadimitriou
    def get_message(self, msg, **kwargs):
92 c4b1a172 Kostas Papadimitriou
        params = kwargs
93 c4b1a172 Kostas Papadimitriou
        params.update({'provider': self.get_title_display})
94 c4b1a172 Kostas Papadimitriou
95 c4b1a172 Kostas Papadimitriou
        override_msg = getattr(self, 'get_%s_message_display' % msg.lower(), None)
96 c4b1a172 Kostas Papadimitriou
        msg = 'AUTH_PROVIDER_%s' % msg
97 c4b1a172 Kostas Papadimitriou
        return override_msg or getattr(astakos_messages, msg, msg) % params
98 c4b1a172 Kostas Papadimitriou
99 dd5f8f4d Kostas Papadimitriou
    @property
100 dd5f8f4d Kostas Papadimitriou
    def add_url(self):
101 dd5f8f4d Kostas Papadimitriou
        return reverse(self.login_view)
102 dd5f8f4d Kostas Papadimitriou
103 0e79735c Kostas Papadimitriou
    @property
104 0e79735c Kostas Papadimitriou
    def provider_details(self):
105 0e79735c Kostas Papadimitriou
        if self.user:
106 0e79735c Kostas Papadimitriou
            if self.identifier:
107 0e79735c Kostas Papadimitriou
                self._provider_details = \
108 0e79735c Kostas Papadimitriou
                    self.user.get_auth_providers().get(module=self.module,
109 0e79735c Kostas Papadimitriou
                                                       identifier=self.identifier).__dict__
110 0e79735c Kostas Papadimitriou
            else:
111 0e79735c Kostas Papadimitriou
                self._provider_details = self.user.get(module=self.module).__dict__
112 0e79735c Kostas Papadimitriou
        return self._provider_details
113 0e79735c Kostas Papadimitriou
114 0e79735c Kostas Papadimitriou
    def __init__(self, user=None, identifier=None, provider_details=None):
115 d2633501 Kostas Papadimitriou
        self.user = user
116 0e79735c Kostas Papadimitriou
        self.identifier = identifier
117 0e79735c Kostas Papadimitriou
118 0e79735c Kostas Papadimitriou
        self._provider_details = None
119 0e79735c Kostas Papadimitriou
        if provider_details:
120 0e79735c Kostas Papadimitriou
            self._provider_details = provider_details
121 0e79735c Kostas Papadimitriou
122 dd5f8f4d Kostas Papadimitriou
        for tpl in ['login_prompt', 'login', 'signup_prompt']:
123 dd5f8f4d Kostas Papadimitriou
            tpl_name = '%s_%s' % (tpl, 'template')
124 dd5f8f4d Kostas Papadimitriou
            override = self.get_setting(tpl_name)
125 dd5f8f4d Kostas Papadimitriou
            if override:
126 dd5f8f4d Kostas Papadimitriou
                setattr(self, tpl_name, override)
127 d2633501 Kostas Papadimitriou
128 91bbc1a4 Kostas Papadimitriou
        for key in ['one_per_user']:
129 91bbc1a4 Kostas Papadimitriou
            override = self.get_setting(key)
130 91bbc1a4 Kostas Papadimitriou
            if override != None:
131 91bbc1a4 Kostas Papadimitriou
                setattr(self, key, override)
132 91bbc1a4 Kostas Papadimitriou
133 564a2292 Kostas Papadimitriou
        self.login_message = self.login_message or self.get_title_display
134 564a2292 Kostas Papadimitriou
        if self.logout_message and "%" in self.logout_message:
135 9c73d218 Kostas Papadimitriou
            logout_text_display = self.logout_from_provider_text or 'at %s' % self.get_title_display
136 564a2292 Kostas Papadimitriou
            self.logout_message = self.logout_message % {'provider':
137 9c73d218 Kostas Papadimitriou
                                                         logout_text_display}
138 564a2292 Kostas Papadimitriou
        else:
139 564a2292 Kostas Papadimitriou
            self.logout_message = self.logout_message or ''
140 564a2292 Kostas Papadimitriou
141 bd0f516a Kostas Papadimitriou
        if not self.icon_url:
142 bd0f516a Kostas Papadimitriou
            self.icon_url = '%s%s' % (settings.MEDIA_URL, 'im/auth/icons/%s.png' %
143 222ad95f Olga Brani
                                       self.module.lower())
144 64492c49 Kostas Papadimitriou
145 531fca05 Olga Brani
        if not self.icon_medium_url:
146 531fca05 Olga Brani
            self.icon_medium_url = '%s%s' % (settings.MEDIA_URL, 'im/auth/icons-medium/%s.png' %
147 64492c49 Kostas Papadimitriou
                                       self.module.lower())
148 bd0f516a Kostas Papadimitriou
149 ca5148f2 Kostas Papadimitriou
        if not self.method_prompt:
150 ca5148f2 Kostas Papadimitriou
            self.method_prompt = _('%s login method') % self.get_title_display
151 d0632ab1 Olga Brani
152 081070a5 Kostas Papadimitriou
    def __getattr__(self, key):
153 081070a5 Kostas Papadimitriou
        if not key.startswith('get_'):
154 6974e526 Kostas Papadimitriou
            return super(AuthProvider, self).__getattribute__(key)
155 081070a5 Kostas Papadimitriou
156 081070a5 Kostas Papadimitriou
        if key.endswith('_display') or key.endswith('template'):
157 081070a5 Kostas Papadimitriou
            attr = key.replace('_display', '').replace('get_','')
158 081070a5 Kostas Papadimitriou
            settings_attr = self.get_setting(attr.upper())
159 081070a5 Kostas Papadimitriou
            if not settings_attr:
160 081070a5 Kostas Papadimitriou
                return getattr(self, attr)
161 cb4714e7 Kostas Papadimitriou
            return _(settings_attr)
162 081070a5 Kostas Papadimitriou
        else:
163 081070a5 Kostas Papadimitriou
            return super(AuthProvider, self).__getattr__(key)
164 081070a5 Kostas Papadimitriou
165 564a2292 Kostas Papadimitriou
    def get_logout_message(self):
166 564a2292 Kostas Papadimitriou
        content = ''
167 564a2292 Kostas Papadimitriou
        if self.remote_logout_url:
168 564a2292 Kostas Papadimitriou
            content = '<a href="%s" title="Logout from %%s"></a>' % self.remote_logou_url
169 564a2292 Kostas Papadimitriou
        return content % (self.get_logout_message_display % self.get_title_display)
170 564a2292 Kostas Papadimitriou
171 d2633501 Kostas Papadimitriou
    def get_setting(self, name, default=None):
172 081070a5 Kostas Papadimitriou
        attr = 'ASTAKOS_AUTH_PROVIDER_%s_%s' % (self.module.upper(), name.upper())
173 1d59653f Kostas Papadimitriou
        attr_sec = 'ASTAKOS_%s_%s' % (self.module.upper(), name.upper())
174 1d59653f Kostas Papadimitriou
        if not hasattr(settings, attr):
175 1d59653f Kostas Papadimitriou
            return getattr(settings, attr_sec, default)
176 91bbc1a4 Kostas Papadimitriou
177 d2633501 Kostas Papadimitriou
        return getattr(settings, attr, default)
178 d2633501 Kostas Papadimitriou
179 e9e692be Kostas Papadimitriou
    def is_available_for_remove(self):
180 e9e692be Kostas Papadimitriou
        return self.is_active() and self.get_setting('CAN_REMOVE', True)
181 e9e692be Kostas Papadimitriou
182 d2633501 Kostas Papadimitriou
    def is_available_for_login(self):
183 d2633501 Kostas Papadimitriou
        """ A user can login using authentication provider"""
184 d2633501 Kostas Papadimitriou
        return self.is_active() and self.get_setting('CAN_LOGIN',
185 d2633501 Kostas Papadimitriou
                                                     self.is_active())
186 d2633501 Kostas Papadimitriou
187 d2633501 Kostas Papadimitriou
    def is_available_for_create(self):
188 d2633501 Kostas Papadimitriou
        """ A user can create an account using this provider"""
189 d2633501 Kostas Papadimitriou
        return self.is_active() and self.get_setting('CAN_CREATE',
190 d2633501 Kostas Papadimitriou
                                                   self.is_active())
191 d2633501 Kostas Papadimitriou
192 d2633501 Kostas Papadimitriou
    def is_available_for_add(self):
193 d2633501 Kostas Papadimitriou
        """ A user can assign provider authentication method"""
194 d2633501 Kostas Papadimitriou
        return self.is_active() and self.get_setting('CAN_ADD',
195 d2633501 Kostas Papadimitriou
                                                   self.is_active())
196 d2633501 Kostas Papadimitriou
197 badcb2a9 Kostas Papadimitriou
    def is_required(self):
198 badcb2a9 Kostas Papadimitriou
        """Provider required (user cannot remove the last one)"""
199 badcb2a9 Kostas Papadimitriou
        return self.is_active() and self.get_setting('REQUIRED', False)
200 badcb2a9 Kostas Papadimitriou
201 d2633501 Kostas Papadimitriou
    def is_active(self):
202 081070a5 Kostas Papadimitriou
        return self.module in astakos_settings.IM_MODULES
203 d2633501 Kostas Papadimitriou
204 d2633501 Kostas Papadimitriou
205 d2633501 Kostas Papadimitriou
class LocalAuthProvider(AuthProvider):
206 d2633501 Kostas Papadimitriou
    module = 'local'
207 d2633501 Kostas Papadimitriou
    title = _('Local password')
208 d2633501 Kostas Papadimitriou
    description = _('Create a local password for your account')
209 47c2fb43 Constantinos Venetsanopoulos
    add_prompt =  _('Enable Classic login for your account')
210 47c2fb43 Constantinos Venetsanopoulos
    details_tpl = _('Username: %(username)s')
211 8a084e9f Constantinos Venetsanopoulos
    login_prompt = _('Classic login (username/password)')
212 450093ec Kostas Papadimitriou
    signup_prompt = _('New to ~okeanos ?')
213 450093ec Kostas Papadimitriou
    signup_link_prompt = _('create an account now')
214 dd5f8f4d Kostas Papadimitriou
    login_view = 'password_change'
215 564a2292 Kostas Papadimitriou
    remote_authenticate = False
216 564a2292 Kostas Papadimitriou
    logout_message = ''
217 d2633501 Kostas Papadimitriou
218 d2633501 Kostas Papadimitriou
    one_per_user = True
219 081070a5 Kostas Papadimitriou
220 081070a5 Kostas Papadimitriou
    login_template = 'im/auth/local_login_form.html'
221 081070a5 Kostas Papadimitriou
    login_prompt_template = 'im/auth/local_login_prompt.html'
222 081070a5 Kostas Papadimitriou
    signup_prompt_template = 'im/auth/local_signup_prompt.html'
223 d2633501 Kostas Papadimitriou
224 d2633501 Kostas Papadimitriou
    @property
225 d2633501 Kostas Papadimitriou
    def extra_actions(self):
226 d2633501 Kostas Papadimitriou
        return [(_('Change password'), reverse('password_change')), ]
227 d2633501 Kostas Papadimitriou
228 74796dd8 Kostas Papadimitriou
229 d2633501 Kostas Papadimitriou
class ShibbolethAuthProvider(AuthProvider):
230 d2633501 Kostas Papadimitriou
    module = 'shibboleth'
231 8a084e9f Constantinos Venetsanopoulos
    title = _('Academic account')
232 47c2fb43 Constantinos Venetsanopoulos
    add_prompt = _('Enable Academic login for your account')
233 47c2fb43 Constantinos Venetsanopoulos
    details_tpl = _('Identifier: %(identifier)s')
234 18945296 Kostas Papadimitriou
    user_title = _('Academic account (%(identifier)s)')
235 8a084e9f Constantinos Venetsanopoulos
    primary_login_prompt = _('If you are a student, professor or researcher you '
236 8a084e9f Constantinos Venetsanopoulos
                             'can login using your academic account.')
237 dd5f8f4d Kostas Papadimitriou
    login_view = 'astakos.im.target.shibboleth.login'
238 d2633501 Kostas Papadimitriou
239 081070a5 Kostas Papadimitriou
    login_template = 'im/auth/shibboleth_login.html'
240 dd5f8f4d Kostas Papadimitriou
    login_prompt_template = 'im/auth/third_party_provider_generic_login_prompt.html'
241 47c2fb43 Constantinos Venetsanopoulos
    logout_from_provider_text = 'Please close all browser windows to complete logout from your Academic account, too.'
242 d2633501 Kostas Papadimitriou
243 18945296 Kostas Papadimitriou
    method_prompt = _('Academic account')
244 ca5148f2 Kostas Papadimitriou
245 d2633501 Kostas Papadimitriou
246 c101b32b Kostas Papadimitriou
class TwitterAuthProvider(AuthProvider):
247 c101b32b Kostas Papadimitriou
    module = 'twitter'
248 c101b32b Kostas Papadimitriou
    title = _('Twitter')
249 47c2fb43 Constantinos Venetsanopoulos
    add_prompt = _('Enable Twitter login for your account')
250 47c2fb43 Constantinos Venetsanopoulos
    details_tpl = _('Username: %(info_screen_name)s')
251 3a72a5d4 Kostas Papadimitriou
    user_title = _('Twitter (%(info_screen_name)s)')
252 dd5f8f4d Kostas Papadimitriou
    login_view = 'astakos.im.target.twitter.login'
253 c101b32b Kostas Papadimitriou
254 450093ec Kostas Papadimitriou
    login_template = 'im/auth/third_party_provider_generic_login.html'
255 450093ec Kostas Papadimitriou
    login_prompt_template = 'im/auth/third_party_provider_generic_login_prompt.html'
256 c101b32b Kostas Papadimitriou
257 74796dd8 Kostas Papadimitriou
258 74796dd8 Kostas Papadimitriou
class GoogleAuthProvider(AuthProvider):
259 74796dd8 Kostas Papadimitriou
    module = 'google'
260 74796dd8 Kostas Papadimitriou
    title = _('Google')
261 47c2fb43 Constantinos Venetsanopoulos
    add_prompt = _('Enable Google login for your account')
262 47c2fb43 Constantinos Venetsanopoulos
    details_tpl = _('Email: %(info_email)s')
263 74796dd8 Kostas Papadimitriou
    user_title = _('Google (%(info_email)s)')
264 dd5f8f4d Kostas Papadimitriou
    login_view = 'astakos.im.target.google.login'
265 74796dd8 Kostas Papadimitriou
266 74796dd8 Kostas Papadimitriou
    login_template = 'im/auth/third_party_provider_generic_login.html'
267 74796dd8 Kostas Papadimitriou
    login_prompt_template = 'im/auth/third_party_provider_generic_login_prompt.html'
268 74796dd8 Kostas Papadimitriou
269 74796dd8 Kostas Papadimitriou
270 74796dd8 Kostas Papadimitriou
class LinkedInAuthProvider(AuthProvider):
271 74796dd8 Kostas Papadimitriou
    module = 'linkedin'
272 74796dd8 Kostas Papadimitriou
    title = _('LinkedIn')
273 47c2fb43 Constantinos Venetsanopoulos
    add_prompt = _('Enable LinkedIn login for your account')
274 47c2fb43 Constantinos Venetsanopoulos
    details_tpl = _('Email: %(info_emailAddress)s')
275 8a084e9f Constantinos Venetsanopoulos
    user_title = _('LinkedIn (%(info_emailAddress)s)')
276 dd5f8f4d Kostas Papadimitriou
    login_view = 'astakos.im.target.linkedin.login'
277 74796dd8 Kostas Papadimitriou
278 74796dd8 Kostas Papadimitriou
    login_template = 'im/auth/third_party_provider_generic_login.html'
279 74796dd8 Kostas Papadimitriou
    login_prompt_template = 'im/auth/third_party_provider_generic_login_prompt.html'
280 74796dd8 Kostas Papadimitriou
281 74796dd8 Kostas Papadimitriou
282 0e79735c Kostas Papadimitriou
def get_provider(id, user_obj=None, default=None, identifier=None, provider_details={}):
283 d2633501 Kostas Papadimitriou
    """
284 d2633501 Kostas Papadimitriou
    Return a provider instance from the auth providers registry.
285 d2633501 Kostas Papadimitriou
    """
286 4a13d054 Kostas Papadimitriou
    if not id in PROVIDERS:
287 4a13d054 Kostas Papadimitriou
        raise Exception('Invalid auth provider requested "%s"' % id)
288 4a13d054 Kostas Papadimitriou
289 0e79735c Kostas Papadimitriou
    return PROVIDERS.get(id, default)(user_obj, identifier, provider_details)