Merge lp:~salgado/lava-dashboard/openid into lp:lava-dashboard

Proposed by Guilherme Salgado
Status: Merged
Merged at revision: 95
Proposed branch: lp:~salgado/lava-dashboard/openid
Merge into: lp:lava-dashboard
Diff against target: 354 lines (+197/-13)
10 files modified
dashboard_app/tests/__init__.py (+1/-0)
dashboard_app/tests/other/csrf.py (+39/-7)
dashboard_app/tests/other/login.py (+116/-0)
dashboard_server/context_processors.py (+4/-0)
dashboard_server/default_settings.py (+26/-1)
dashboard_server/manage.py (+1/-0)
dashboard_server/settings.py (+4/-0)
dashboard_server/templates/base.html (+2/-3)
dashboard_server/templates/dashboard_app/bundle_stream_list.html (+1/-1)
dashboard_server/urls.py (+3/-1)
To merge this branch: bzr merge lp:~salgado/lava-dashboard/openid
Reviewer Review Type Date Requested Status
Zygmunt Krynicki (community) Needs Fixing
James Westby (community) Approve
Review via email: mp+38470@code.launchpad.net

Description of the change

Switch from password login to OpenID, using login.lp.net as the SSO.

To post a comment you must log in.
Revision history for this message
James Westby (james-w) wrote :

60 +
61 +class NoCSRFProtectionOnXMLRPCConfigurationTestCase(CSRFTestCase):
62 +

How crucial is it to split this test?

I pushed for it all to be one TestClass in order to get more confidence that
the test was passing because it was working, and not just because django
disables the checks for tests.

85 + <table>{{ form.as_table }}</table>

Are you still running under 1.1 on lucid? Have you tried this on 1.2? I
have a suspicion that you need another tag in there to put the CSRF token
in the HTML under 1.2.

175 + def tearDown(self):

We need an upcall to the super's tearDown.

177 + settings.OPENID_SSO_SERVER_URL = self.orig_setting

(optional) make the instance variable "orig_sso_server_url" or something,
in case we have to override a second later.

Will that code handle the SSO option being removed from settings.py?

182 + useropenid = UserOpenID(
183 + user=user, claimed_id=self._identity_url,
184 + display_id=self._identity_url)

What does that do?

258 +oidutil.log = lambda msg, level=0: None

Maybe settings.py?

272 +TEMPLATE_CONTEXT_PROCESSORS = (

Is there a reason that's not in default_settings.py?

Overall this looks great though, thanks.

James

review: Needs Information
Revision history for this message
Guilherme Salgado (salgado) wrote :

Hi James,

Thanks for the review!

On Thu, 2010-10-14 at 22:44 +0000, James Westby wrote:
> Review: Needs Information
> 60 +
> 61 +class NoCSRFProtectionOnXMLRPCConfigurationTestCase(CSRFTestCase):
> 62 +
>
> How crucial is it to split this test?
>
> I pushed for it all to be one TestClass in order to get more confidence that
> the test was passing because it was working, and not just because django
> disables the checks for tests.

I did that because the other one has the urls property, and that doesn't
include any of the app's urlpatterns as it only uses the test view
created for the test. That means the test method here can't post to the
'xml-rpc' endpoint. One benefit of splitting, though, is that this test
doesn't need to use CSRFTestCase -- the regular TestCase will do.

I could try and combine them together again, by having the urls property
include all the app's urls plus the one that is specific for testing, if
you think it's worth the effort.

>
> 85 + <table>{{ form.as_table }}</table>
>
> Are you still running under 1.1 on lucid? Have you tried this on 1.2? I
> have a suspicion that you need another tag in there to put the CSRF token
> in the HTML under 1.2.

As we discussed on IRC, launch-control is using the legacy method of
protection against CSRF, which doesn't require a template tag. Once we
drop support for django 1.1 we can move away from the legacy method.
I've added a comment explaining that to the relevant bit of code.

>
> 175 + def tearDown(self):
>
> We need an upcall to the super's tearDown.
>

Good catch; added.

> 177 + settings.OPENID_SSO_SERVER_URL = self.orig_setting
>
> (optional) make the instance variable "orig_sso_server_url" or something,
> in case we have to override a second later.

Renamed.

>
> Will that code handle the SSO option being removed from settings.py?

Probably not, so I've changed it to cope with that.

>
> 182 + useropenid = UserOpenID(
> 183 + user=user, claimed_id=self._identity_url,
> 184 + display_id=self._identity_url)
>
> What does that do?

Associates the newly created user with its identity URL.
(added this as a comment to the code)

>
> 258 +oidutil.log = lambda msg, level=0: None
>
> Maybe settings.py?

WFM.

>
> 272 +TEMPLATE_CONTEXT_PROCESSORS = (
>
> Is there a reason that's not in default_settings.py?

Mostly ignorance. I know close to nothing about what should go in each
of the settings files of a django project, so I added it to the same
file that Michael added the lexbuilder one. Happy to move that to
default_settings.py, though.

--
Guilherme Salgado <https://launchpad.net/~salgado>

Revision history for this message
James Westby (james-w) wrote :

> I did that because the other one has the urls property, and that doesn't
> include any of the app's urlpatterns as it only uses the test view
> created for the test. That means the test method here can't post to the
> 'xml-rpc' endpoint. One benefit of splitting, though, is that this test
> doesn't need to use CSRFTestCase -- the regular TestCase will do.

It does need CSRFTestCase, otherwise Django disables the csrf checks
globally, meaning that it is not testing in the right environment. That's
the sort of issue that means I want to stick to one test class.

> I could try and combine them together again, by having the urls property
> include all the app's urls plus the one that is specific for testing, if
> you think it's worth the effort.

I'm don't feel strongly about it, but I would have more confidence in the
test. Doesn't it just need the xml-rpc view hooked up?

Maybe the test urls can be loaded and extended in the property, rather than
overwritten?

> As we discussed on IRC, launch-control is using the legacy method of
> protection against CSRF, which doesn't require a template tag. Once we
> drop support for django 1.1 we can move away from the legacy method.
> I've added a comment explaining that to the relevant bit of code.

Great, thanks. The test will fail when the settings are changed, so we can
be sure to check all the tests still make sense.

> > 182 + useropenid = UserOpenID(
> > 183 + user=user, claimed_id=self._identity_url,
> > 184 + display_id=self._identity_url)
> >
> > What does that do?
>
> Associates the newly created user with its identity URL.
> (added this as a comment to the code)

Ah, of course, I was assuming that we were testing user creation,
but that doesn't make sense.

> Mostly ignorance. I know close to nothing about what should go in each
> of the settings files of a django project, so I added it to the same
> file that Michael added the lexbuilder one. Happy to move that to
> default_settings.py, though.

launch-control doesn't really use a django standard I don't think.

It has three files two of which can override the defaults. What you
have done is set that variable such that it can't be overriden in
local_settings or deployment_settings like the others can. I would
suggest moving it to default_settings for consistency.

Thanks,

James

Revision history for this message
James Westby (james-w) :
review: Approve
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

221 -LOGIN_REDIRECT_URL = '/dashboard/'

This is a bug and cannot be removed.
It will work in development environment but will fail in production.

Salgado: would you consider merging the changes james mentioned above (settings) from my branch?

review: Needs Fixing
Revision history for this message
Guilherme Salgado (salgado) wrote :

On Fri, 2010-10-15 at 15:28 +0000, James Westby wrote:
> > I did that because the other one has the urls property, and that doesn't
> > include any of the app's urlpatterns as it only uses the test view
> > created for the test. That means the test method here can't post to the
> > 'xml-rpc' endpoint. One benefit of splitting, though, is that this test
> > doesn't need to use CSRFTestCase -- the regular TestCase will do.
>
> It does need CSRFTestCase, otherwise Django disables the csrf checks
> globally, meaning that it is not testing in the right environment. That's
> the sort of issue that means I want to stick to one test class.
>
> > I could try and combine them together again, by having the urls property
> > include all the app's urls plus the one that is specific for testing, if
> > you think it's worth the effort.
>
> I'm don't feel strongly about it, but I would have more confidence in the
> test. Doesn't it just need the xml-rpc view hooked up?
>
> Maybe the test urls can be loaded and extended in the property, rather than
> overwritten?

That's what I was thinking. It was easy to do except for the fact that
reverse("xml-rpc") stopped working, so I had to do
reverse(dashboard_xml_rpc_handler) instead.

> > Mostly ignorance. I know close to nothing about what should go in each
> > of the settings files of a django project, so I added it to the same
> > file that Michael added the lexbuilder one. Happy to move that to
> > default_settings.py, though.
>
> launch-control doesn't really use a django standard I don't think.
>
> It has three files two of which can override the defaults. What you
> have done is set that variable such that it can't be overriden in
> local_settings or deployment_settings like the others can. I would
> suggest moving it to default_settings for consistency.

Fair enough; I've moved it there.

I've also re-added the LOGIN_REDIRECT_URL line as Zygmunt requested.

Now we just need to decide whether we want to have only-OpenID login
enabled or both OpenID and password.

lp:~salgado/lava-dashboard/openid updated
79. By Guilherme Salgado

A bunch of tweaks suggested by James and Zygmunt

Revision history for this message
James Westby (james-w) wrote :

On Fri, 15 Oct 2010 19:49:41 -0000, Guilherme Salgado <email address hidden> wrote:
> Now we just need to decide whether we want to have only-OpenID login
> enabled or both OpenID and password.

I suggest that we land this code as-is, and can change this in a follow
branch if desired.

Thanks,

James

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'dashboard_app/tests/__init__.py'
--- dashboard_app/tests/__init__.py 2010-10-14 09:08:24 +0000
+++ dashboard_app/tests/__init__.py 2010-10-15 20:12:47 +0000
@@ -21,6 +21,7 @@
21 'other.csrf',21 'other.csrf',
22 'other.dashboard_api',22 'other.dashboard_api',
23 'other.deserialization',23 'other.deserialization',
24 'other.login',
24 'other.misc',25 'other.misc',
25 'other.test_client',26 'other.test_client',
26 'other.xml_rpc',27 'other.xml_rpc',
2728
=== modified file 'dashboard_app/tests/other/csrf.py'
--- dashboard_app/tests/other/csrf.py 2010-10-12 14:16:37 +0000
+++ dashboard_app/tests/other/csrf.py 2010-10-15 20:12:47 +0000
@@ -22,35 +22,67 @@
22import xmlrpclib22import xmlrpclib
2323
24import django24import django
25from django import forms
26from django.conf.urls.defaults import patterns, url
25from django.core.urlresolvers import reverse27from django.core.urlresolvers import reverse
28from django.http import HttpResponse
29from django.template import Template, Context
2630
27from dashboard_app.tests.utils import CSRFTestCase31from dashboard_app.tests.utils import CSRFTestCase
32from dashboard_app.views import dashboard_xml_rpc_handler
33from dashboard_app import urls
2834
2935
30class CSRFConfigurationTestCase(CSRFTestCase):36class CSRFConfigurationTestCase(CSRFTestCase):
3137
38 @property
39 def urls(self):
40 urlpatterns = urls.urlpatterns
41 urlpatterns += patterns('', url(r'^test-form/', test_form))
42 return type('urls', (), dict(urlpatterns=urlpatterns))
43
32 def setUp(self):44 def setUp(self):
33 super(CSRFConfigurationTestCase, self).setUp()45 super(CSRFConfigurationTestCase, self).setUp()
34 self.login_path = reverse("django.contrib.auth.views.login")46 self.form_path = reverse(test_form)
3547
36 def test_csrf_token_present_in_login_page(self):48 def test_csrf_token_present_in_form(self):
37 if django.VERSION[:2] == (1, 1):49 if django.VERSION[:2] == (1, 1):
38 # This feature is not supported on django 1.150 # This feature is not supported on django 1.1
39 return51 return
40 response = self.client.get(self.login_path)52 response = self.client.get(self.form_path)
41 self.assertContains(response, "csrfmiddlewaretoken")53 self.assertContains(response, "csrfmiddlewaretoken")
4254
43 def test_cross_site_login_fails(self):55 def test_cross_site_form_submission_fails(self):
44 if django.VERSION[:2] == (1, 1):56 if django.VERSION[:2] == (1, 1):
45 # This feature is not supported on django 1.157 # This feature is not supported on django 1.1
46 return58 return
47 response = self.client.post(self.login_path, {59 response = self.client.post(self.form_path, {'text': 'text'})
48 'user': 'user', 'pass': 'pass'})
49 self.assertEquals(response.status_code, 403)60 self.assertEquals(response.status_code, 403)
5061
51 def test_csrf_not_protecting_xml_rpc_views(self):62 def test_csrf_not_protecting_xml_rpc_views(self):
52 """call version and check that we didn't get 403"""63 """call version and check that we didn't get 403"""
53 endpoint_path = reverse("xml-rpc")64 endpoint_path = reverse(dashboard_xml_rpc_handler)
54 request_body = xmlrpclib.dumps((), methodname="version")65 request_body = xmlrpclib.dumps((), methodname="version")
55 response = self.client.post(endpoint_path, request_body, "text/xml")66 response = self.client.post(endpoint_path, request_body, "text/xml")
56 self.assertContains(response, "<methodResponse>", status_code=200)67 self.assertContains(response, "<methodResponse>", status_code=200)
68
69
70def test_form(request):
71 t = Template(template)
72 html = t.render(Context({'form': SingleTextFieldForm()}))
73 return HttpResponse(html)
74
75
76class SingleTextFieldForm(forms.Form):
77 text = forms.CharField()
78
79
80template = """
81 <html>
82 <body>
83 <form action="." method="POST">
84 <table>{{ form.as_table }}</table>
85 </form>
86 </body>
87 </html>
88 """
5789
=== added file 'dashboard_app/tests/other/login.py'
--- dashboard_app/tests/other/login.py 1970-01-01 00:00:00 +0000
+++ dashboard_app/tests/other/login.py 2010-10-15 20:12:47 +0000
@@ -0,0 +1,116 @@
1# Copyright (C) 2010 Linaro Limited
2#
3# Author: Guilherme Salgado <guilherme.salgado@linaro.org>
4#
5# This file is part of Launch Control.
6#
7# Launch Control is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License version 3
9# as published by the Free Software Foundation
10#
11# Launch Control is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU Affero General Public License
17# along with Launch Control. If not, see <http://www.gnu.org/licenses/>.
18
19"""
20Tests for the login bits of Launch Control.
21"""
22import cgi
23import httplib
24
25from django.conf import settings
26from django.contrib.auth.models import User
27from django.test import TestCase
28
29from django_openid_auth.models import UserOpenID
30from django_openid_auth.tests.test_views import StubOpenIDProvider
31
32from openid.fetchers import setDefaultFetcher
33
34from dashboard_app.tests.utils import TestClient
35
36
37class TestOpenIDLogin(TestCase):
38
39 urls = 'django_openid_auth.tests.urls'
40 _username = 'someuser'
41 _identity_url = 'http://example.com/identity'
42
43 def test_positive_response_from_provider(self):
44 self.create_user()
45 openid_request = self.initiate_login()
46
47 # Simulate a positive assertion from the server.
48 openid_response = openid_request.answer(True)
49
50 # Use that response to complete the authentication.
51 response = self.complete(openid_response)
52
53 # And the user is now logged in.
54 response = self.client.get('/getuser/')
55 self.assertEquals(response.content, self._username)
56
57 def test_negative_response_from_provider(self):
58 openid_request = self.initiate_login()
59
60 # Simulate a negative assertion from the server.
61 openid_response = openid_request.answer(False)
62
63 # Use that response to complete the authentication.
64 response = self.complete(openid_response)
65
66 # Since we got a negative assertion from the server, no user is logged
67 # in.
68 response = self.client.get('/getuser/')
69 self.assertEquals(response.content, '')
70
71 def setUp(self):
72 super(TestOpenIDLogin, self).setUp()
73 # Use StubOpenIDProvider and _identity_url as our fixed SSO so that
74 # we always get a successful OpenID response for _identity_url.
75 self.provider = StubOpenIDProvider('http://example.com/')
76 setDefaultFetcher(self.provider, wrap_exceptions=False)
77 self.missing_sso_server_url = object()
78 self.orig_sso_server_url = getattr(
79 settings, 'OPENID_SSO_SERVER_URL', self.missing_sso_server_url)
80 settings.OPENID_SSO_SERVER_URL = self._identity_url
81 self.client = TestClient()
82
83 def tearDown(self):
84 super(TestOpenIDLogin, self).tearDown()
85 setDefaultFetcher(None)
86 if self.orig_sso_server_url == self.missing_sso_server_url:
87 del settings.OPENID_SSO_SERVER_URL
88 else:
89 settings.OPENID_SSO_SERVER_URL = self.orig_sso_server_url
90
91 def create_user(self):
92 user = User(username=self._username)
93 user.save()
94 # Associate our newly created user with the identity URL.
95 useropenid = UserOpenID(
96 user=user, claimed_id=self._identity_url,
97 display_id=self._identity_url)
98 useropenid.save()
99
100 def initiate_login(self):
101 response = self.client.get('/openid/login/')
102 self.assertEqual(httplib.OK, response.status_code)
103 openid_request = self.provider.parseFormPost(response.content)
104 self.assertTrue(openid_request.return_to.startswith(
105 'http://testserver/openid/complete/'))
106 return openid_request
107
108 def complete(self, openid_response):
109 """Complete an OpenID authentication request."""
110 webresponse = self.provider.server.encodeResponse(openid_response)
111 self.assertEquals(webresponse.code, 302)
112 redirect_to = webresponse.headers['location']
113 self.assertTrue(redirect_to.startswith(
114 'http://testserver/openid/complete/'))
115 return self.client.get('/openid/complete/',
116 dict(cgi.parse_qsl(redirect_to.split('?', 1)[1])))
0117
=== added file 'dashboard_server/context_processors.py'
--- dashboard_server/context_processors.py 1970-01-01 00:00:00 +0000
+++ dashboard_server/context_processors.py 2010-10-15 20:12:47 +0000
@@ -0,0 +1,4 @@
1from django.conf import settings
2
3def login_url(request):
4 return dict(login_url=settings.LOGIN_URL)
05
=== modified file 'dashboard_server/default_settings.py'
--- dashboard_server/default_settings.py 2010-10-02 21:25:06 +0000
+++ dashboard_server/default_settings.py 2010-10-15 20:12:47 +0000
@@ -87,6 +87,10 @@
87SITE_ID = 187SITE_ID = 1
8888
89LOGIN_REDIRECT_URL = '/dashboard/'89LOGIN_REDIRECT_URL = '/dashboard/'
90LOGIN_URL = '/openid/login/'
91# If you want login to work against your local user DB, uncomment the line
92# below.
93# LOGIN_URL = '/accounts/login/'
9094
91# List of callables that know how to import templates from various sources.95# List of callables that know how to import templates from various sources.
92TEMPLATE_LOADERS = (96TEMPLATE_LOADERS = (
@@ -110,7 +114,8 @@
110# 1.2. In 1.1 we explicitly use the contrib package in 1.2 we use the114# 1.2. In 1.1 we explicitly use the contrib package in 1.2 we use the
111# legacy package. This has a small performance hit as the legacy115# legacy package. This has a small performance hit as the legacy
112# middleware in 1.2 rewrites the whole response. Once we drop support116# middleware in 1.2 rewrites the whole response. Once we drop support
113# for 1.1 we can remove this section.117# for 1.1 we can remove this section and use just CsrfViewMiddleware instead
118# of the legacy CsrfMiddleware.
114if django.VERSION[:2] == (1, 1):119if django.VERSION[:2] == (1, 1):
115 MIDLEWARE_CLASSES = MIDDLEWARE_CLASSES + (120 MIDLEWARE_CLASSES = MIDDLEWARE_CLASSES + (
116 'django.contrib.csrf.middleware.CsrfMiddleware',121 'django.contrib.csrf.middleware.CsrfMiddleware',
@@ -129,7 +134,27 @@
129 'django.contrib.sites',134 'django.contrib.sites',
130 'django.contrib.databrowse',135 'django.contrib.databrowse',
131 'django.contrib.humanize',136 'django.contrib.humanize',
137 'django_openid_auth',
132 'dashboard_app',138 'dashboard_app',
133)139)
134140
141AUTHENTICATION_BACKENDS = (
142 'django_openid_auth.auth.OpenIDBackend',
143 'django.contrib.auth.backends.ModelBackend',
144)
145
146OPENID_CREATE_USERS = True
147OPENID_UPDATE_DETAILS_FROM_SREG = True
148OPENID_SSO_SERVER_URL = 'https://login.launchpad.net/'
149
135SERVE_ASSETS_FROM_DJANGO = False150SERVE_ASSETS_FROM_DJANGO = False
151
152TEMPLATE_CONTEXT_PROCESSORS = (
153 # Django provided context processors
154 'django.core.context_processors.auth',
155 "django.core.context_processors.debug",
156 "django.core.context_processors.i18n",
157 "django.core.context_processors.media",
158 # Launch Control provided context processors
159 'dashboard_server.context_processors.login_url',
160 )
136161
=== modified file 'dashboard_server/manage.py'
--- dashboard_server/manage.py 2010-09-22 19:31:02 +0000
+++ dashboard_server/manage.py 2010-10-15 20:12:47 +0000
@@ -37,5 +37,6 @@
37 sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)37 sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
38 sys.exit(1)38 sys.exit(1)
3939
40
40if __name__ == "__main__":41if __name__ == "__main__":
41 execute_manager(settings)42 execute_manager(settings)
4243
=== modified file 'dashboard_server/settings.py'
--- dashboard_server/settings.py 2010-10-04 11:51:54 +0000
+++ dashboard_server/settings.py 2010-10-15 20:12:47 +0000
@@ -35,3 +35,7 @@
35 from local_settings import *35 from local_settings import *
36except ImportError:36except ImportError:
37 from development_settings import *37 from development_settings import *
38
39# python-openid is too noisy, so we silence it.
40from openid import oidutil
41oidutil.log = lambda msg, level=0: None
3842
=== modified file 'dashboard_server/templates/base.html'
--- dashboard_server/templates/base.html 2010-10-14 09:58:54 +0000
+++ dashboard_server/templates/base.html 2010-10-15 20:12:47 +0000
@@ -16,15 +16,14 @@
16 <div id="account_info">16 <div id="account_info">
17 {% if user.is_authenticated %}17 {% if user.is_authenticated %}
18 {% blocktrans %}Signed in as {{user}}{% endblocktrans %},18 {% blocktrans %}Signed in as {{user}}{% endblocktrans %},
19 <a class="change_password" href="{% url django.contrib.auth.views.password_change %}">{% trans "change password" %}</a>19 <a class="sign_out" href="{% url django.contrib.auth.views.logout %}">{% trans "sign out" %}</a>
20 or <a class="sign_out" href="{% url django.contrib.auth.views.logout %}">{% trans "sign out" %}</a>
21 {% if user.is_superuser %}20 {% if user.is_superuser %}
22 {% trans "or visit " %}<a class="admin_site" href="{% url admin:index %}"21 {% trans "or visit " %}<a class="admin_site" href="{% url admin:index %}"
23 >{% trans "admin interface" %}</a>22 >{% trans "admin interface" %}</a>
24 {% endif %}23 {% endif %}
25 {% else %}24 {% else %}
26 {% trans "You are not signed in" %}25 {% trans "You are not signed in" %}
27 <a class="sign_in" href="{% url django.contrib.auth.views.login %}">{% trans "Sign In" %}</a>26 <a class="sign_in" href="{{ login_url }}">{% trans "Sign In" %}</a>
28 {% endif %}27 {% endif %}
29 </div>28 </div>
30 <div id="product_logo">29 <div id="product_logo">
3130
=== modified file 'dashboard_server/templates/dashboard_app/bundle_stream_list.html'
--- dashboard_server/templates/dashboard_app/bundle_stream_list.html 2010-10-04 16:05:12 +0000
+++ dashboard_server/templates/dashboard_app/bundle_stream_list.html 2010-10-15 20:12:47 +0000
@@ -31,7 +31,7 @@
31 {% endif %}31 {% endif %}
32</ul>32</ul>
33{% if not user.is_authenticated %}33{% if not user.is_authenticated %}
34<p>You must <a href="{% url django.contrib.auth.views.login %}"34<p>You must <a href="{{ login_url }}"
35 >{% trans "sign in" %}</a> to get more access</p>35 >{% trans "sign in" %}</a> to get more access</p>
36{% endif %}36{% endif %}
37{% endblock %}37{% endblock %}
3838
=== modified file 'dashboard_server/urls.py'
--- dashboard_server/urls.py 2010-10-02 12:23:25 +0000
+++ dashboard_server/urls.py 2010-10-15 20:12:47 +0000
@@ -19,6 +19,7 @@
19from django.conf import settings19from django.conf import settings
20from django.conf.urls.defaults import *20from django.conf.urls.defaults import *
21from django.contrib import admin21from django.contrib import admin
22from django.contrib.auth.views import logout
22from django.contrib import databrowse23from django.contrib import databrowse
23from django.views.generic.simple import direct_to_template24from django.views.generic.simple import direct_to_template
2425
@@ -67,8 +68,9 @@
67 url(r'xml-rpc/', dashboard_xml_rpc_handler,68 url(r'xml-rpc/', dashboard_xml_rpc_handler,
68 name='xml-rpc'),69 name='xml-rpc'),
69 url(r'^dashboard/', include('dashboard_app.urls')),70 url(r'^dashboard/', include('dashboard_app.urls')),
70 url(r'accounts/', include('django.contrib.auth.urls')),71 url(r'^logout/$', logout),
71 (r'^admin/', include(admin.site.urls)),72 (r'^admin/', include(admin.site.urls)),
73 (r'^openid/', include('django_openid_auth.urls')),
72 )74 )
7375
74if settings.SERVE_ASSETS_FROM_DJANGO:76if settings.SERVE_ASSETS_FROM_DJANGO:

Subscribers

People subscribed via source and target branches