fix typo
[mygpo.git] / mygpo / web / views / users.py
blobef656164b04dfcb842e7be7bf9f88c3aeee5c778
2 # This file is part of my.gpodder.org.
4 # my.gpodder.org is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or (at your
7 # option) any later version.
9 # my.gpodder.org is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12 # License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with my.gpodder.org. If not, see <http://www.gnu.org/licenses/>.
18 import string
19 import random
21 from django.shortcuts import render
22 from django.http import HttpResponseRedirect
23 from django.contrib.auth import authenticate
24 from django.contrib.auth.decorators import login_required
25 from django.contrib import messages
26 from django.contrib.sites.models import RequestSite
27 from django.conf import settings
28 from django.utils.translation import ugettext as _
29 from django.views.decorators.cache import never_cache
30 from django.views.generic.base import View, TemplateView
31 from django.utils.decorators import method_decorator
32 from django.core.urlresolvers import reverse
33 from django.utils.http import is_safe_url
35 from oauth2client.client import FlowExchangeError
37 from mygpo.db.couchdb.user import user_by_google_email, set_users_google_email
38 from mygpo.decorators import allowed_methods, repeat_on_conflict
39 from mygpo.web.forms import RestorePasswordForm
40 from mygpo.users.models import User
41 from mygpo.web.forms import ResendActivationForm
42 from mygpo.constants import DEFAULT_LOGIN_REDIRECT
43 from mygpo.web.auth import get_google_oauth_flow
45 import logging
46 logger = logging.getLogger(__name__)
49 @repeat_on_conflict(['user'])
50 def login(request, user):
51 from django.contrib.auth import login
52 login(request, user)
55 class LoginView(View):
56 """ View to login a user """
58 def get(self, request):
59 """ Shows the login page """
61 # Do not show login page for already-logged-in users
62 if request.user.is_authenticated():
63 return HttpResponseRedirect(DEFAULT_LOGIN_REDIRECT)
65 return render(request, 'login.html', {
66 'url': RequestSite(request),
67 'next': request.GET.get('next', ''),
71 @method_decorator(never_cache)
72 def post(self, request):
73 """ Carries out the login, redirects to get if it fails """
75 # redirect target on successful login
76 next_page = request.POST.get('next', '')
78 # redirect target on failed login
79 login_page = '{page}?next={next_page}'.format(page=reverse('login'),
80 next_page=next_page)
83 username = request.POST.get('user', None)
84 if not username:
85 messages.error(request, _('Username missing'))
86 return HttpResponseRedirect(login_page)
88 password = request.POST.get('pwd', None)
89 if not password:
90 messages.error(request, _('Password missing'))
91 return HttpResponseRedirect(login_page)
93 # find the user from the configured login systems, and verify pwd
94 user = authenticate(username=username, password=password)
96 if not user:
97 messages.error(request, _('Wrong username or password.'))
98 return HttpResponseRedirect(login_page)
101 if not user.is_active:
102 # if the user is not active, find the reason
103 if user.deleted:
104 messages.error(request, _('You have deleted your account, '
105 'but you can register again'))
106 return HttpResponseRedirect(login_page)
108 else:
109 messages.error(request,
110 _('Please activate your account first.'))
111 return HttpResponseRedirect(login_page)
113 # set up the user's session
114 login(request, user)
116 if next_page:
117 if is_safe_url(next_page):
118 return HttpResponseRedirect(next_page)
120 else:
121 # TODO: log a warning that next_page is not
122 # considered a safe redirect target
123 pass
125 return HttpResponseRedirect(DEFAULT_LOGIN_REDIRECT)
128 def get_user(username, email, is_active=None):
129 if username:
130 return User.get_user(username, is_active=None)
132 elif email:
133 return User.get_user_by_email(email, is_active=None)
135 return None
138 @never_cache
139 def restore_password(request):
141 if request.method == 'GET':
142 form = RestorePasswordForm()
143 return render(request, 'restore_password.html', {
144 'form': form,
148 form = RestorePasswordForm(request.POST)
149 if not form.is_valid():
150 return HttpResponseRedirect('/login/')
152 user = get_user(form.cleaned_data['username'], form.cleaned_data['email'], is_active=None)
154 if not user:
155 messages.error(request, _('User does not exist.'))
157 return render(request, 'password_reset_failed.html')
159 site = RequestSite(request)
160 pwd = "".join(random.sample(string.letters+string.digits, 8))
161 subject = _('Reset password for your account on %s') % site
162 message = _('Here is your new password for your account %(username)s on %(site)s: %(password)s') % {'username': user.username, 'site': site, 'password': pwd}
163 user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
164 _set_password(user, pwd)
165 return render(request, 'password_reset.html')
168 @repeat_on_conflict(['user'])
169 def _set_password(user, password):
170 user.set_password(password)
171 user.save()
174 @repeat_on_conflict(['user'])
175 def _set_active(user, is_active=True):
176 user.is_active = is_active
177 user.save()
180 @never_cache
181 @allowed_methods(['GET', 'POST'])
182 def resend_activation(request):
184 if request.method == 'GET':
185 form = ResendActivationForm()
186 return render(request, 'registration/resend_activation.html', {
187 'form': form,
190 site = RequestSite(request)
191 form = ResendActivationForm(request.POST)
193 try:
194 if not form.is_valid():
195 raise ValueError(_('Invalid Username entered'))
197 user = get_user(form.cleaned_data['username'], form.cleaned_data['email'], is_active=None)
198 if not user:
199 raise ValueError(_('User does not exist.'))
201 if user.deleted:
202 raise ValueError(_('You have deleted your account, but you can register again.'))
204 if user.activation_key is None:
205 _set_active(user=user, is_active=True)
206 raise ValueError(_('Your account already has been activated. Go ahead and log in.'))
208 elif user.activation_key_expired():
209 raise ValueError(_('Your activation key has expired. Please try another username, or retry with the same one tomorrow.'))
211 except ValueError, e:
212 messages.error(request, unicode(e))
214 return render(request, 'registration/resend_activation.html', {
215 'form': form,
219 try:
220 user.send_activation_email(site)
222 except AttributeError:
223 user.send_activation_email(site)
225 return render(request, 'registration/resent_activation.html')
229 class GoogleLogin(View):
230 """ Redirects to Google Authentication page """
232 def get(self, request):
233 flow = get_google_oauth_flow(request)
234 auth_uri = flow.step1_get_authorize_url()
235 return HttpResponseRedirect(auth_uri)
238 class GoogleLoginCallback(TemplateView):
239 """ Logs user in, or connects user to account """
241 def get(self, request):
243 if request.GET.get('error'):
244 messages.error(request, _('Login failed.'))
245 return HttpResponseRedirect(reverse('login'))
247 code = request.GET.get('code')
248 if not code:
249 messages.error(request, _('Login failed.'))
250 return HttpResponseRedirect(reverse('login'))
252 flow = get_google_oauth_flow(request)
254 try:
255 credentials = flow.step2_exchange(code)
256 except FlowExchangeError:
257 messages.error(request, _('Login with Google is currently not possible.'))
258 logger.exception('Login with Google failed')
259 return HttpResponseRedirect(reverse('login'))
261 email = credentials.token_response['id_token']['email']
263 # Connect account
264 if request.user.is_authenticated():
265 set_users_google_email(request.user, email)
266 messages.success(request, _('Your account has been connected with '
267 '{google}. Open Settings to change this.'.format(
268 google=email)))
269 return HttpResponseRedirect(DEFAULT_LOGIN_REDIRECT)
271 # Check if Google account is connected
272 user = user_by_google_email(email)
274 if not user:
275 # Connect account
276 messages.error(request, _('No account connected with your Google '
277 'account %s. Please log in to connect.' % email))
278 return HttpResponseRedirect('{login}?next={connect}'.format(
279 login=reverse('login'), connect=reverse('login-google')))
281 # Log in user
282 login(request, user)
283 return HttpResponseRedirect(DEFAULT_LOGIN_REDIRECT)