Do not show change password in the menu if user provider is not local
[astakos] / snf-astakos-app / astakos / im / api / admin.py
1 # Copyright 2011-2012 GRNET S.A. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6 #
7 #   1. Redistributions of source code must retain the above
8 #      copyright notice, this list of conditions and the following
9 #      disclaimer.
10 #
11 #   2. Redistributions in binary form must reproduce the above
12 #      copyright notice, this list of conditions and the following
13 #      disclaimer in the documentation and/or other materials
14 #      provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 #
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
33
34 import logging
35 import urllib
36
37 from functools import wraps
38 from traceback import format_exc
39 from time import time, mktime
40 from urllib import quote
41 from urlparse import urlparse
42 from collections import defaultdict
43
44 from django.conf import settings
45 from django.http import HttpResponse
46 from django.utils import simplejson as json
47 from django.core.urlresolvers import reverse
48
49 from astakos.im.api.faults import *
50 from astakos.im.models import AstakosUser, Service
51 from astakos.im.settings import INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED
52 from astakos.im.util import epoch
53 from astakos.im.api import _get_user_by_email, _get_user_by_username
54
55 logger = logging.getLogger(__name__)
56 format = ('%a, %d %b %Y %H:%M:%S GMT')
57
58 def render_fault(request, fault):
59     if isinstance(fault, InternalServerError) and settings.DEBUG:
60         fault.details = format_exc(fault)
61
62     request.serialization = 'text'
63     data = fault.message + '\n'
64     if fault.details:
65         data += '\n' + fault.details
66     response = HttpResponse(data, status=fault.code)
67     response['Content-Length'] = len(response.content)
68     return response
69
70 def api_method(http_method=None, token_required=False, perms=None):
71     """Decorator function for views that implement an API method."""
72     if not perms:
73         perms = []
74
75     def decorator(func):
76         @wraps(func)
77         def wrapper(request, *args, **kwargs):
78             try:
79                 if http_method and request.method != http_method:
80                     raise BadRequest('Method not allowed.')
81                 x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN')
82                 if token_required:
83                     if not x_auth_token:
84                         raise Unauthorized('Access denied')
85                     try:
86                         user = AstakosUser.objects.get(auth_token=x_auth_token)
87                         if not user.has_perms(perms):
88                             raise Forbidden('Unauthorized request')
89                     except AstakosUser.DoesNotExist, e:
90                         raise Unauthorized('Invalid X-Auth-Token')
91                     kwargs['user'] = user
92                 response = func(request, *args, **kwargs)
93                 return response
94             except Fault, fault:
95                 return render_fault(request, fault)
96             except BaseException, e:
97                 logger.exception('Unexpected error: %s' % e)
98                 fault = InternalServerError('Unexpected error')
99                 return render_fault(request, fault)
100         return wrapper
101     return decorator
102
103 @api_method(http_method='GET', token_required=True)
104 def authenticate_old(request, user=None):
105     # Normal Response Codes: 204
106     # Error Response Codes: internalServerError (500)
107     #                       badRequest (400)
108     #                       unauthorised (401)
109     if not user:
110         raise BadRequest('No user')
111
112     # Check if the is active.
113     if not user.is_active:
114         raise Unauthorized('User inactive')
115
116     # Check if the token has expired.
117     if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
118         raise Unauthorized('Authentication expired')
119
120     if not user.signed_terms():
121         raise Unauthorized('Pending approval terms')
122
123     response = HttpResponse()
124     response.status=204
125     user_info = {'username':user.username,
126                  'uniq':user.email,
127                  'auth_token':user.auth_token,
128                  'auth_token_created':user.auth_token_created.isoformat(),
129                  'auth_token_expires':user.auth_token_expires.isoformat(),
130                  'has_credits':user.has_credits,
131                  'has_signed_terms':user.signed_terms(),
132                  'groups':[g.name for g in user.groups.all()]}
133     response.content = json.dumps(user_info)
134     response['Content-Type'] = 'application/json; charset=UTF-8'
135     response['Content-Length'] = len(response.content)
136     return response
137
138 @api_method(http_method='GET', token_required=True)
139 def authenticate(request, user=None):
140     # Normal Response Codes: 204
141     # Error Response Codes: internalServerError (500)
142     #                       badRequest (400)
143     #                       unauthorised (401)
144     if not user:
145         raise BadRequest('No user')
146
147     # Check if the is active.
148     if not user.is_active:
149         raise Unauthorized('User inactive')
150
151     # Check if the token has expired.
152     if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
153         raise Unauthorized('Authentication expired')
154
155     if not user.signed_terms():
156         raise Unauthorized('Pending approval terms')
157
158     response = HttpResponse()
159     response.status=204
160     user_info = {'userid':user.username,
161                  'email':[user.email],
162                  'name':user.realname,
163                  'auth_token':user.auth_token,
164                  'auth_token_created':epoch(user.auth_token_created),
165                  'auth_token_expires':epoch(user.auth_token_expires),
166                  'has_credits':user.has_credits,
167                  'is_active':user.is_active,
168                  'groups':[g.name for g in user.groups.all()]}
169     response.content = json.dumps(user_info)
170     response['Content-Type'] = 'application/json; charset=UTF-8'
171     response['Content-Length'] = len(response.content)
172     return response
173
174 @api_method(http_method='GET')
175 def get_services(request):
176     callback = request.GET.get('callback', None)
177     services = Service.objects.all()
178     data = tuple({'id':s.pk, 'name':s.name, 'url':s.url, 'icon':s.icon} for s in services)
179     data = json.dumps(data)
180     mimetype = 'application/json'
181
182     if callback:
183         mimetype = 'application/javascript'
184         data = '%s(%s)' % (callback, data)
185
186     return HttpResponse(content=data, mimetype=mimetype)
187
188 @api_method()
189 def get_menu(request, with_extra_links=False, with_signout=True):
190     index_url = reverse('index')
191     absolute = lambda (url): request.build_absolute_uri(url)
192     l = [{ 'url': absolute(index_url), 'name': "Sign in"}]
193     cookie = urllib.unquote(request.COOKIES.get(COOKIE_NAME, ''))
194     email = cookie.partition('|')[0]
195     try:
196         if not email:
197             raise ValueError
198         user = AstakosUser.objects.get(email=email, is_active=True)
199     except AstakosUser.DoesNotExist:
200         pass
201     except ValueError:
202         pass
203     else:
204         l = []
205         l.append({ 'url': absolute(reverse('astakos.im.views.index')),
206                   'name': user.email})
207         l.append({ 'url': absolute(reverse('astakos.im.views.edit_profile')),
208                   'name': "My account" })
209         if with_extra_links:
210             if user.has_usable_password() and user.provider == 'local':
211                 l.append({ 'url': absolute(reverse('password_change')),
212                           'name': "Change password" })
213             if EMAILCHANGE_ENABLED:
214                 l.append({'url':absolute(reverse('email_change')),
215                           'name': "Change email"})
216             if INVITATIONS_ENABLED:
217                 l.append({ 'url': absolute(reverse('astakos.im.views.invite')),
218                           'name': "Invitations" })
219             l.append({ 'url': absolute(reverse('astakos.im.views.feedback')),
220                       'name': "Feedback" })
221         if with_signout:
222             l.append({ 'url': absolute(reverse('astakos.im.views.logout')),
223                       'name': "Sign out"})
224
225     callback = request.GET.get('callback', None)
226     data = json.dumps(tuple(l))
227     mimetype = 'application/json'
228
229     if callback:
230         mimetype = 'application/javascript'
231         data = '%s(%s)' % (callback, data)
232
233     return HttpResponse(content=data, mimetype=mimetype)
234
235 @api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
236 def get_user_by_email(request, user=None):
237     # Normal Response Codes: 200
238     # Error Response Codes: internalServerError (500)
239     #                       badRequest (400)
240     #                       unauthorised (401)
241     #                       forbidden (403)
242     #                       itemNotFound (404)
243     email = request.GET.get('name')
244     return _get_user_by_email(email)
245
246 @api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
247 def get_user_by_username(request, user_id, user=None):
248     # Normal Response Codes: 200
249     # Error Response Codes: internalServerError (500)
250     #                       badRequest (400)
251     #                       unauthorised (401)
252     #                       forbidden (403)
253     #                       itemNotFound (404)
254     return _get_user_by_username(user_id)