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/>.
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
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'),
78 username
= request
.POST
.get('user', None)
80 messages
.error(request
, _('Username missing'))
81 return HttpResponseRedirect(login_page
)
83 password
= request
.POST
.get('pwd', None)
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
)
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
99 messages
.error(request
, _('You have deleted your account, '
100 'but you can register again'))
101 return HttpResponseRedirect(login_page
)
104 messages
.error(request
,
105 _('Please activate your account first.'))
106 return HttpResponseRedirect(login_page
)
108 # set up the user's session
112 if is_safe_url(next_page
):
113 return HttpResponseRedirect(next_page
)
116 # TODO: log a warning that next_page is not
117 # considered a safe redirect target
120 return HttpResponseRedirect(DEFAULT_LOGIN_REDIRECT
)
123 def get_user(username
, email
, is_active
=None):
125 return User
.get_user(username
, is_active
=None)
128 return User
.get_user_by_email(email
, is_active
=None)
134 def restore_password(request
):
136 if request
.method
== 'GET':
137 form
= RestorePasswordForm()
138 return render(request
, 'restore_password.html', {
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)
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
)
169 @repeat_on_conflict(['user'])
170 def _set_active(user
, is_active
=True):
171 user
.is_active
= is_active
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', {
185 site
= RequestSite(request
)
186 form
= ResendActivationForm(request
.POST
)
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)
194 raise ValueError(_('User does not exist.'))
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', {
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')
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']
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(
257 return HttpResponseRedirect(DEFAULT_LOGIN_REDIRECT
)
259 # Check if Google account is connected
260 user
= user_by_google_email(email
)
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')))
271 return HttpResponseRedirect(DEFAULT_LOGIN_REDIRECT
)