Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / auth_providers.py @ 7deaaa5f

History | View | Annotate | Download (10.1 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 d2633501 Kostas Papadimitriou
90 c4b1a172 Kostas Papadimitriou
    def get_message(self, msg, **kwargs):
91 c4b1a172 Kostas Papadimitriou
        params = kwargs
92 c4b1a172 Kostas Papadimitriou
        params.update({'provider': self.get_title_display})
93 c4b1a172 Kostas Papadimitriou
94 c4b1a172 Kostas Papadimitriou
        override_msg = getattr(self, 'get_%s_message_display' % msg.lower(), None)
95 c4b1a172 Kostas Papadimitriou
        msg = 'AUTH_PROVIDER_%s' % msg
96 c4b1a172 Kostas Papadimitriou
        return override_msg or getattr(astakos_messages, msg, msg) % params
97 c4b1a172 Kostas Papadimitriou
98 dd5f8f4d Kostas Papadimitriou
    @property
99 dd5f8f4d Kostas Papadimitriou
    def add_url(self):
100 dd5f8f4d Kostas Papadimitriou
        return reverse(self.login_view)
101 dd5f8f4d Kostas Papadimitriou
102 d2633501 Kostas Papadimitriou
    def __init__(self, user=None):
103 d2633501 Kostas Papadimitriou
        self.user = user
104 dd5f8f4d Kostas Papadimitriou
        for tpl in ['login_prompt', 'login', 'signup_prompt']:
105 dd5f8f4d Kostas Papadimitriou
            tpl_name = '%s_%s' % (tpl, 'template')
106 dd5f8f4d Kostas Papadimitriou
            override = self.get_setting(tpl_name)
107 dd5f8f4d Kostas Papadimitriou
            if override:
108 dd5f8f4d Kostas Papadimitriou
                setattr(self, tpl_name, override)
109 d2633501 Kostas Papadimitriou
110 91bbc1a4 Kostas Papadimitriou
        for key in ['one_per_user']:
111 91bbc1a4 Kostas Papadimitriou
            override = self.get_setting(key)
112 91bbc1a4 Kostas Papadimitriou
            if override != None:
113 91bbc1a4 Kostas Papadimitriou
                setattr(self, key, override)
114 91bbc1a4 Kostas Papadimitriou
115 564a2292 Kostas Papadimitriou
        self.login_message = self.login_message or self.get_title_display
116 564a2292 Kostas Papadimitriou
        if self.logout_message and "%" in self.logout_message:
117 9c73d218 Kostas Papadimitriou
            logout_text_display = self.logout_from_provider_text or 'at %s' % self.get_title_display
118 564a2292 Kostas Papadimitriou
            self.logout_message = self.logout_message % {'provider':
119 9c73d218 Kostas Papadimitriou
                                                         logout_text_display}
120 564a2292 Kostas Papadimitriou
        else:
121 564a2292 Kostas Papadimitriou
            self.logout_message = self.logout_message or ''
122 564a2292 Kostas Papadimitriou
123 bd0f516a Kostas Papadimitriou
        if not self.icon_url:
124 bd0f516a Kostas Papadimitriou
            self.icon_url = '%s%s' % (settings.MEDIA_URL, 'im/auth/icons/%s.png' %
125 bd0f516a Kostas Papadimitriou
                                       self.get_title_display.lower())
126 64492c49 Kostas Papadimitriou
127 531fca05 Olga Brani
        if not self.icon_medium_url:
128 531fca05 Olga Brani
            self.icon_medium_url = '%s%s' % (settings.MEDIA_URL, 'im/auth/icons-medium/%s.png' %
129 64492c49 Kostas Papadimitriou
                                       self.module.lower())
130 bd0f516a Kostas Papadimitriou
131 d0632ab1 Olga Brani
132 081070a5 Kostas Papadimitriou
    def __getattr__(self, key):
133 081070a5 Kostas Papadimitriou
        if not key.startswith('get_'):
134 6974e526 Kostas Papadimitriou
            return super(AuthProvider, self).__getattribute__(key)
135 081070a5 Kostas Papadimitriou
136 081070a5 Kostas Papadimitriou
        if key.endswith('_display') or key.endswith('template'):
137 081070a5 Kostas Papadimitriou
            attr = key.replace('_display', '').replace('get_','')
138 081070a5 Kostas Papadimitriou
            settings_attr = self.get_setting(attr.upper())
139 081070a5 Kostas Papadimitriou
            if not settings_attr:
140 081070a5 Kostas Papadimitriou
                return getattr(self, attr)
141 cb4714e7 Kostas Papadimitriou
            return _(settings_attr)
142 081070a5 Kostas Papadimitriou
        else:
143 081070a5 Kostas Papadimitriou
            return super(AuthProvider, self).__getattr__(key)
144 081070a5 Kostas Papadimitriou
145 564a2292 Kostas Papadimitriou
    def get_logout_message(self):
146 564a2292 Kostas Papadimitriou
        content = ''
147 564a2292 Kostas Papadimitriou
        if self.remote_logout_url:
148 564a2292 Kostas Papadimitriou
            content = '<a href="%s" title="Logout from %%s"></a>' % self.remote_logou_url
149 564a2292 Kostas Papadimitriou
        return content % (self.get_logout_message_display % self.get_title_display)
150 564a2292 Kostas Papadimitriou
151 d2633501 Kostas Papadimitriou
    def get_setting(self, name, default=None):
152 081070a5 Kostas Papadimitriou
        attr = 'ASTAKOS_AUTH_PROVIDER_%s_%s' % (self.module.upper(), name.upper())
153 1d59653f Kostas Papadimitriou
        attr_sec = 'ASTAKOS_%s_%s' % (self.module.upper(), name.upper())
154 1d59653f Kostas Papadimitriou
        if not hasattr(settings, attr):
155 1d59653f Kostas Papadimitriou
            return getattr(settings, attr_sec, default)
156 91bbc1a4 Kostas Papadimitriou
157 d2633501 Kostas Papadimitriou
        return getattr(settings, attr, default)
158 d2633501 Kostas Papadimitriou
159 d2633501 Kostas Papadimitriou
    def is_available_for_login(self):
160 d2633501 Kostas Papadimitriou
        """ A user can login using authentication provider"""
161 d2633501 Kostas Papadimitriou
        return self.is_active() and self.get_setting('CAN_LOGIN',
162 d2633501 Kostas Papadimitriou
                                                     self.is_active())
163 d2633501 Kostas Papadimitriou
164 d2633501 Kostas Papadimitriou
    def is_available_for_create(self):
165 d2633501 Kostas Papadimitriou
        """ A user can create an account using this provider"""
166 d2633501 Kostas Papadimitriou
        return self.is_active() and self.get_setting('CAN_CREATE',
167 d2633501 Kostas Papadimitriou
                                                   self.is_active())
168 d2633501 Kostas Papadimitriou
169 d2633501 Kostas Papadimitriou
    def is_available_for_add(self):
170 d2633501 Kostas Papadimitriou
        """ A user can assign provider authentication method"""
171 d2633501 Kostas Papadimitriou
        return self.is_active() and self.get_setting('CAN_ADD',
172 d2633501 Kostas Papadimitriou
                                                   self.is_active())
173 d2633501 Kostas Papadimitriou
174 badcb2a9 Kostas Papadimitriou
    def is_required(self):
175 badcb2a9 Kostas Papadimitriou
        """Provider required (user cannot remove the last one)"""
176 badcb2a9 Kostas Papadimitriou
        return self.is_active() and self.get_setting('REQUIRED', False)
177 badcb2a9 Kostas Papadimitriou
178 d2633501 Kostas Papadimitriou
    def is_active(self):
179 081070a5 Kostas Papadimitriou
        return self.module in astakos_settings.IM_MODULES
180 d2633501 Kostas Papadimitriou
181 d2633501 Kostas Papadimitriou
182 d2633501 Kostas Papadimitriou
class LocalAuthProvider(AuthProvider):
183 d2633501 Kostas Papadimitriou
    module = 'local'
184 d2633501 Kostas Papadimitriou
    title = _('Local password')
185 d2633501 Kostas Papadimitriou
    description = _('Create a local password for your account')
186 081070a5 Kostas Papadimitriou
    add_prompt =  _('Create a local password for your account')
187 cb4714e7 Kostas Papadimitriou
    login_prompt = _('if you already have a username and password')
188 450093ec Kostas Papadimitriou
    signup_prompt = _('New to ~okeanos ?')
189 450093ec Kostas Papadimitriou
    signup_link_prompt = _('create an account now')
190 dd5f8f4d Kostas Papadimitriou
    login_view = 'password_change'
191 564a2292 Kostas Papadimitriou
    remote_authenticate = False
192 564a2292 Kostas Papadimitriou
    logout_message = ''
193 d2633501 Kostas Papadimitriou
194 d2633501 Kostas Papadimitriou
    one_per_user = True
195 081070a5 Kostas Papadimitriou
196 081070a5 Kostas Papadimitriou
    login_template = 'im/auth/local_login_form.html'
197 081070a5 Kostas Papadimitriou
    login_prompt_template = 'im/auth/local_login_prompt.html'
198 081070a5 Kostas Papadimitriou
    signup_prompt_template = 'im/auth/local_signup_prompt.html'
199 d2633501 Kostas Papadimitriou
    details_tpl = _('You can login to your account using your'
200 d2633501 Kostas Papadimitriou
                    ' %(auth_backend)s password.')
201 d2633501 Kostas Papadimitriou
202 d2633501 Kostas Papadimitriou
    @property
203 d2633501 Kostas Papadimitriou
    def extra_actions(self):
204 d2633501 Kostas Papadimitriou
        return [(_('Change password'), reverse('password_change')), ]
205 d2633501 Kostas Papadimitriou
206 74796dd8 Kostas Papadimitriou
207 d2633501 Kostas Papadimitriou
class ShibbolethAuthProvider(AuthProvider):
208 d2633501 Kostas Papadimitriou
    module = 'shibboleth'
209 9c73d218 Kostas Papadimitriou
    title = _('Academic account (Shibboleth)')
210 450093ec Kostas Papadimitriou
    add_prompt = _('Allows you to login to your account using your academic '
211 450093ec Kostas Papadimitriou
                    'account')
212 b9231ded Kostas Papadimitriou
    details_tpl = _('Shibboleth account \'%(identifier)s\' is connected to your '
213 3a72a5d4 Kostas Papadimitriou
                    ' account.')
214 3a72a5d4 Kostas Papadimitriou
    user_title = _('Academic credentials (%(identifier)s)')
215 cb4714e7 Kostas Papadimitriou
    primary_login_prompt = _('If you are a student/researcher/faculty you can'
216 cb4714e7 Kostas Papadimitriou
                             ' login using your university-credentials in'
217 cb4714e7 Kostas Papadimitriou
                             ' the following page')
218 dd5f8f4d Kostas Papadimitriou
    login_view = 'astakos.im.target.shibboleth.login'
219 d2633501 Kostas Papadimitriou
220 081070a5 Kostas Papadimitriou
    login_template = 'im/auth/shibboleth_login.html'
221 dd5f8f4d Kostas Papadimitriou
    login_prompt_template = 'im/auth/third_party_provider_generic_login_prompt.html'
222 9c73d218 Kostas Papadimitriou
    logout_from_provider_text = ' at your Academic account (shibboleth)'
223 d2633501 Kostas Papadimitriou
224 d2633501 Kostas Papadimitriou
225 c101b32b Kostas Papadimitriou
class TwitterAuthProvider(AuthProvider):
226 c101b32b Kostas Papadimitriou
    module = 'twitter'
227 c101b32b Kostas Papadimitriou
    title = _('Twitter')
228 450093ec Kostas Papadimitriou
    add_prompt = _('Allows you to login to your account using Twitter')
229 3a72a5d4 Kostas Papadimitriou
    details_tpl = _('Twitter screen name: %(info_screen_name)s')
230 3a72a5d4 Kostas Papadimitriou
    user_title = _('Twitter (%(info_screen_name)s)')
231 dd5f8f4d Kostas Papadimitriou
    login_view = 'astakos.im.target.twitter.login'
232 c101b32b Kostas Papadimitriou
233 450093ec Kostas Papadimitriou
    login_template = 'im/auth/third_party_provider_generic_login.html'
234 450093ec Kostas Papadimitriou
    login_prompt_template = 'im/auth/third_party_provider_generic_login_prompt.html'
235 c101b32b Kostas Papadimitriou
236 74796dd8 Kostas Papadimitriou
237 74796dd8 Kostas Papadimitriou
class GoogleAuthProvider(AuthProvider):
238 74796dd8 Kostas Papadimitriou
    module = 'google'
239 74796dd8 Kostas Papadimitriou
    title = _('Google')
240 450093ec Kostas Papadimitriou
    add_prompt = _('Allows you to login to your account using Google')
241 74796dd8 Kostas Papadimitriou
    details_tpl = _('Google account: %(info_email)s')
242 74796dd8 Kostas Papadimitriou
    user_title = _('Google (%(info_email)s)')
243 dd5f8f4d Kostas Papadimitriou
    login_view = 'astakos.im.target.google.login'
244 74796dd8 Kostas Papadimitriou
245 74796dd8 Kostas Papadimitriou
    login_template = 'im/auth/third_party_provider_generic_login.html'
246 74796dd8 Kostas Papadimitriou
    login_prompt_template = 'im/auth/third_party_provider_generic_login_prompt.html'
247 74796dd8 Kostas Papadimitriou
248 74796dd8 Kostas Papadimitriou
249 74796dd8 Kostas Papadimitriou
class LinkedInAuthProvider(AuthProvider):
250 74796dd8 Kostas Papadimitriou
    module = 'linkedin'
251 74796dd8 Kostas Papadimitriou
    title = _('LinkedIn')
252 450093ec Kostas Papadimitriou
    add_prompt = _('Allows you to login to your account using LinkedIn')
253 74796dd8 Kostas Papadimitriou
    user_title = _('LinkedIn (%(info_emailAddress)s)')
254 450093ec Kostas Papadimitriou
    details_tpl = _('LinkedIn account: %(info_emailAddress)s')
255 dd5f8f4d Kostas Papadimitriou
    login_view = 'astakos.im.target.linkedin.login'
256 74796dd8 Kostas Papadimitriou
257 74796dd8 Kostas Papadimitriou
    login_template = 'im/auth/third_party_provider_generic_login.html'
258 74796dd8 Kostas Papadimitriou
    login_prompt_template = 'im/auth/third_party_provider_generic_login_prompt.html'
259 74796dd8 Kostas Papadimitriou
260 74796dd8 Kostas Papadimitriou
261 d2633501 Kostas Papadimitriou
def get_provider(id, user_obj=None, default=None):
262 d2633501 Kostas Papadimitriou
    """
263 d2633501 Kostas Papadimitriou
    Return a provider instance from the auth providers registry.
264 d2633501 Kostas Papadimitriou
    """
265 d2633501 Kostas Papadimitriou
    return PROVIDERS.get(id, default)(user_obj)