add some stale='update_after' for better performance
[mygpo.git] / mygpo / web / views / users.py
blobb097382ac408e9064fa91451057eb737a49cb110
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
34 from mygpo.db.couchdb.user import user_by_google_email, set_users_google_email
36 from mygpo.decorators import allowed_methods, repeat_on_conflict
37 from mygpo.web.forms import RestorePasswordForm
38 from mygpo.users.models import User
39 from mygpo.web.forms import ResendActivationForm
40 from mygpo.constants import DEFAULT_LOGIN_REDIRECT
41 from mygpo.web.auth import get_google_oauth_flow
44 @repeat_on_conflict(['user'])
45 def login(request, user):
46 from django.contrib.auth import login
47 login(request, user)
50 class LoginView(View):
51 """ View to login a user """
53 def get(self, request):
54 """ Shows the login page """
56 # Do not show login page for already-logged-in users
57 if request.user.is_authenticated():
58 return HttpResponseRedirect(DEFAULT_LOGIN_REDIRECT)
60 return render(request, 'login.html', {
61 'url': RequestSite(request),
62 'next': request.GET.get('next', ''),
66 @method_decorator(never_cache)
67 def post(self, request):
68 """ Carries out the login, redirects to get if it fails """
70 # redirect target on successful login
71 next_page = request.POST.get('next', '')
73 # redirect target on failed login
74 login_page = '{page}?next={next_page}'.format(page=reverse('login'),
75 next_page=next_page)
78 username = request.POST.get('user', None)
79 if not username:
80 messages.error(request, _('Username missing'))
81 return HttpResponseRedirect(login_page)
83 password = request.POST.get('pwd', None)
84 if not password:
85 messages.error(request, _('Password missing'))
86 return HttpResponseRedirect(login_page)
88 # find the user from the configured login systems, and verify pwd
89 user = authenticate(username=username, password=password)
91 if not user:
92 messages.error(request, _('Wrong username or password.'))
93 return HttpResponseRedirect(login_page)
96 if not user.is_active:
97 # if the user is not active, find the reason
98 if user.deleted:
99 messages.error(request, _('You have deleted your account, '
100 'but you can register again'))
101 return HttpResponseRedirect(login_page)
103 else:
104 messages.error(request,
105 _('Please activate your account first.'))
106 return HttpResponseRedirect(login_page)
108 # set up the user's session
109 login(request, user)
111 if next_page:
112 if is_safe_url(next_page):
113 return HttpResponseRedirect(next_page)
115 else:
116 # TODO: log a warning that next_page is not
117 # considered a safe redirect target
118 pass
120 return HttpResponseRedirect(DEFAULT_LOGIN_REDIRECT)
123 def get_user(username, email, is_active=None):
124 if username:
125 return User.get_user(username, is_active=None)
127 elif email:
128 return User.get_user_by_email(email, is_active=None)
130 return None
133 @never_cache
134 def restore_password(request):
136 if request.method == 'GET':
137 form = RestorePasswordForm()
138 return render(request, 'restore_password.html', {
139 'form': form,
143 form = RestorePasswordForm(request.POST)
144 if not form.is_valid():
145 return HttpResponseRedirect('/login/')
147 user = get_user(form.cleaned_data['username'], form.cleaned_data['email'], is_active=None)
149 if not user:
150 messages.error(request, _('User does not exist.'))
152 return render(request, 'password_reset_failed.html')
154 site = RequestSite(request)
155 pwd = "".join(random.sample(string.letters+string.digits, 8))
156 subject = _('Reset password for your account on %s') % site
157 message = _('Here is your new password for your account %(username)s on %(site)s: %(password)s') % {'username': user.username, 'site': site, 'password': pwd}
158 user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
159 _set_password(user, pwd)
160 return render(request, 'password_reset.html')
163 @repeat_on_conflict(['user'])
164 def _set_password(user, password):
165 user.set_password(password)
166 user.save()
169 @repeat_on_conflict(['user'])
170 def _set_active(user, is_active=True):
171 user.is_active = is_active
172 user.save()
175 @never_cache
176 @allowed_methods(['GET', 'POST'])
177 def resend_activation(request):
179 if request.method == 'GET':
180 form = ResendActivationForm()
181 return render(request, 'registration/resend_activation.html', {
182 'form': form,
185 site = RequestSite(request)
186 form = ResendActivationForm(request.POST)
188 try:
189 if not form.is_valid():
190 raise ValueError(_('Invalid Username entered'))
192 user = get_user(form.cleaned_data['username'], form.cleaned_data['email'], is_active=None)
193 if not user:
194 raise ValueError(_('User does not exist.'))
196 if user.deleted:
197 raise ValueError(_('You have deleted your account, but you can regster again.'))
199 if user.activation_key is None:
200 _set_active(user=user, is_active=True)
201 raise ValueError(_('Your account already has been activated. Go ahead and log in.'))
203 elif user.activation_key_expired():
204 raise ValueError(_('Your activation key has expired. Please try another username, or retry with the same one tomorrow.'))
206 except ValueError, e:
207 messages.error(request, unicode(e))
209 return render(request, 'registration/resend_activation.html', {
210 'form': form,
214 try:
215 user.send_activation_email(site)
217 except AttributeError:
218 user.send_activation_email(site)
220 return render(request, 'registration/resent_activation.html')
224 class GoogleLogin(View):
225 """ Redirects to Google Authentication page """
227 def get(self, request):
228 flow = get_google_oauth_flow(request)
229 auth_uri = flow.step1_get_authorize_url()
230 return HttpResponseRedirect(auth_uri)
233 class GoogleLoginCallback(TemplateView):
234 """ Logs user in, or connects user to account """
236 def get(self, request):
238 if request.GET.get('error'):
239 messages.error(request, _('Login failed.'))
240 return HttpResponseRedirect(reverse('login'))
242 code = request.GET.get('code')
243 if not code:
244 messages.error(request, _('Login failed.'))
245 return HttpResponseRedirect(reverse('login'))
247 flow = get_google_oauth_flow(request)
248 credentials = flow.step2_exchange(code)
249 email = credentials.token_response['id_token']['email']
251 # Connect account
252 if request.user.is_authenticated():
253 set_users_google_email(request.user, email)
254 messages.success(request, _('Your account has been connected with '
255 '{google}. Open Settings to change this.'.format(
256 google=email)))
257 return HttpResponseRedirect(DEFAULT_LOGIN_REDIRECT)
259 # Check if Google account is connected
260 user = user_by_google_email(email)
262 if not user:
263 # Connect account
264 messages.error(request, _('No account connected with your Google '
265 'account %s. Please log in to connect.' % email))
266 return HttpResponseRedirect('{login}?next={connect}'.format(
267 login=reverse('login'), connect=reverse('login-google')))
269 # Log in user
270 login(request, user)
271 return HttpResponseRedirect(DEFAULT_LOGIN_REDIRECT)