Merge branch 'oauth'

* oauth:
  Focusing login name field on load.
  Whoops, errant print statement.
  Styling login and authorize oauth templates.
  Adding login view and integrating oauth middleware and authentication backend. Wow that was easy.
  Initial oauth provider commit.
This commit is contained in:
Samuel Clay 2014-01-21 16:52:14 -08:00
commit d262c15d91
18 changed files with 348 additions and 27 deletions

View file

@ -3,12 +3,11 @@ import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
from apps.reader.models import MUserStory
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
from apps.reader.models import MUserStory
userstories = MUserStory.objects.all()
print "%s userstories" % userstories.count()

View file

@ -3,11 +3,11 @@ import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
from apps.reader.models import MUserStory
class Migration(DataMigration):
def forwards(self, orm):
from apps.reader.models import MUserStory
userstories = MUserStory.objects.all()
print "%s userstories" % userstories.count()

View file

@ -4,10 +4,10 @@ from apps.reader import views
urlpatterns = patterns('',
url(r'^$', views.index),
url(r'^login_as', views.login_as, name='login_as'),
url(r'^logout', views.logout, name='logout'),
url(r'^login', views.login, name='login'),
url(r'^logout', views.logout, name='welcome-logout'),
url(r'^login', views.login, name='welcome-login'),
url(r'^autologin/(?P<username>\w+)/(?P<secret>\w+)/?', views.autologin, name='autologin'),
url(r'^signup', views.signup, name='signup'),
url(r'^signup', views.signup, name='welcome-signup'),
url(r'^feeds/?$', views.load_feeds, name='load-feeds'),
url(r'^feed/(?P<feed_id>\d+)', views.load_single_feed, name='load-single-feed'),
url(r'^page/(?P<feed_id>\d+)', views.load_feed_page, name='load-feed-page'),

View file

@ -24,6 +24,7 @@ from django.contrib.sites.models import Site
from django.utils import feedgenerator
from mongoengine.queryset import OperationError
from mongoengine.queryset import NotUniqueError
from oauth2_provider.decorators import protected_resource
from apps.recommendations.models import RecommendedFeed
from apps.analyzer.models import MClassifierTitle, MClassifierAuthor, MClassifierFeed, MClassifierTag
from apps.analyzer.models import apply_classifier_titles, apply_classifier_feeds
@ -744,7 +745,7 @@ def load_feed_page(request, feed_id):
logging.user(request, "~FYLoading original page, from the db")
return HttpResponse(data, mimetype="text/html; charset=utf-8")
@json.json_view
def load_starred_stories(request):
user = get_user(request)

2
config/requirements.txt Normal file → Executable file
View file

@ -39,3 +39,5 @@ requests==1.1.0
seacucumber==1.5
South==0.7.6
stripe==1.7.10
django-oauth-toolkit==0.5.0
django-cors-headers==0.12

View file

@ -1 +1 @@
../../django/django/contrib/admin/media/
/Users/sclay/projects/django/django/contrib/admin/static/admin

View file

@ -58,14 +58,19 @@
.NB-static-form input,
.NB-static-form select {
margin: 6px 0 2px;
width: 200px;
font-size: 14px;
padding: 2px;
border: 1px solid #606060;
-moz-box-shadow:2px 2px 0 #A0B998;
-webkit-box-shadow:2px 2px 0 #A0B998;
box-shadow:2px 2px 0 #A0B998;
margin: 6px 4px;
border: 1px solid rgba(0, 0, 0, .2);
border-radius: 1px;
-moz-box-shadow: inset 0 2px 2px rgba(50, 50, 50, 0.15);
box-shadow: inset 0 2px 2px rgba(50, 50, 50, 0.15);
width: 200px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.NB-static-form input.error,

View file

@ -4069,9 +4069,9 @@ form.opml_import_form input {
.NB-splash-info .NB-splash-title {
position: absolute;
bottom: -1px;
height: 55px;
width: 282px;
height: 54px;
right: 166px;
width: 312px;
z-index: 2;
}
@ -4079,8 +4079,8 @@ form.opml_import_form input {
top: 0px;
bottom: inherit;
right: 24px;
width: 312px;
height: 55px;
width: 282px;
height: 54px;
}
.NB-body-main .NB-splash-info.NB-splash-top .NB-splash-title {
display: none;
@ -8607,6 +8607,9 @@ form.opml_import_form input {
color: #303030;
line-height: 18px;
}
.NB-static h3 {
line-height: 1.4em;
}
.NB-static .NB-splash-info {
opacity: .9;
@ -10201,6 +10204,56 @@ form.opml_import_form input {
margin-left: 200px;
}
/* =============== */
/* = OAuth Forms = */
/* =============== */
.NB-static-oauth h3 {
margin-top: 0;
text-align: center;
}
.NB-static-oauth .NB-static-form {
width: 500px;
margin: 24px auto;
}
.NB-static-login .NB-static-form {
width: 360px;
}
.NB-static-oauth .NB-static-form-label label {
width: 120px;
text-transform: uppercase;
font-size: 18px;
padding: 8px 0 0 0;
color: rgba(0, 0, 0, .6);
}
.NB-static-oauth .NB-static-form-input input {
margin-left: 0;
margin-right: 0;
height: 34px;
font-size: 22px;
}
.NB-static-oauth input[type=submit].NB-static-form-submit {
margin: 24px 0 0 0;
font-size: 26px;
font-size: 18px;
padding: 8px 12px;
}
.NB-static-login input[type=submit].NB-static-form-submit {
margin: 12px 0 0 120px;
}
.NB-static-oauth .NB-error {
font-size: 12px;
text-align: center;
color: #6A1000;
padding: 4px 0 0;
line-height: 14px;
font-weight: bold;
clear: both;
}
/* ======================== */
/* = Feed Options Popover = */
/* ======================== */

View file

@ -8,7 +8,7 @@ import os
CURRENT_DIR = os.path.dirname(__file__)
NEWSBLUR_DIR = CURRENT_DIR
TEMPLATE_DIRS = (os.path.join(CURRENT_DIR, 'templates'),
os.path.join(CURRENT_DIR, 'vendor/zebra/templates'),)
os.path.join(CURRENT_DIR, 'vendor/zebra/templates'))
MEDIA_ROOT = os.path.join(CURRENT_DIR, 'media')
STATIC_ROOT = os.path.join(CURRENT_DIR, 'static')
UTILS_ROOT = os.path.join(CURRENT_DIR, 'utils')
@ -64,12 +64,13 @@ LANGUAGE_CODE = 'en-us'
SITE_ID = 1
USE_I18N = False
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/reader/login'
LOGIN_URL = '/account/login'
MEDIA_URL = '/media/'
STATIC_URL = '/media/'
STATIC_ROOT = '/media/'
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/media/admin/'
CIPHER_USERNAMES = False
DEBUG_ASSETS = DEBUG
HOMEPAGE_USERNAME = 'popular'
@ -100,6 +101,7 @@ MIDDLEWARE_CLASSES = (
'django.middleware.gzip.GZipMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'apps.profile.middleware.TimingMiddleware',
'apps.profile.middleware.LastSeenMiddleware',
@ -108,10 +110,27 @@ MIDDLEWARE_CLASSES = (
'subdomains.middleware.SubdomainMiddleware',
'apps.profile.middleware.SimpsonsMiddleware',
'apps.profile.middleware.ServerHostnameMiddleware',
'corsheaders.middleware.CorsMiddleware',
'oauth2_provider.middleware.OAuth2TokenMiddleware',
# 'debug_toolbar.middleware.DebugToolbarMiddleware',
)
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
AUTHENTICATION_BACKENDS = (
'oauth2_provider.backends.OAuth2Backend',
'django.contrib.auth.backends.ModelBackend',
)
CORS_ORIGIN_ALLOW_ALL = True
OAUTH2_PROVIDER = {
'SCOPES': {
'read': 'Read: saved stories, shared stories, tags, training, and feeds',
'write': 'Write: saving stories, sharing stories, adding tags, adding training, feed management',
},
'CLIENT_ID_GENERATOR_CLASS': 'oauth2_provider.generators.ClientIdGenerator',
}
# ===========
# = Logging =
@ -264,6 +283,8 @@ INSTALLED_APPS = (
'vendor.paypal.standard.ipn',
'vendor.zebra',
'vendor.haystack',
'oauth2_provider',
'corsheaders',
)
# ==========
@ -521,9 +542,8 @@ DEBUG_TOOLBAR_CONFIG = {
if DEBUG:
TEMPLATE_LOADERS = (
('django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
),
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)
else:
TEMPLATE_LOADERS = (

View file

@ -0,0 +1,45 @@
{% extends 'base.html' %}
{% load typogrify_tags utils_tags zebra_tags %}
{% block bodyclass %}NB-static NB-static-oauth NB-static-login{% endblock %}
{% block extra_head_js %}
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
$("input[name=username]").focus();
});
</script>
{% include_stylesheets "common" %}
{% endblock %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="NB-static-title">
Login
</div>
<div class="NB-static-form-wrapper" style="overflow:hidden">
<form method="post" class="NB-static-form" action="{% url 'django.contrib.auth.views.login' %}">
{% if form.errors %}
<p class="NB-error error">Your username and password didn't match.<br />Please try again.</p>
{% else %}{% if next %}
<p class="NB-error error">Please login to continue.</p>
{% endif %}{% endif %}
{% csrf_token %}
<div class="NB-static-form-label">{{ form.username.label_tag }}</div>
<div class="NB-static-form-input">{{ form.username }}</div>
<div class="NB-static-form-label">{{ form.password.label_tag }}</div>
<div class="NB-static-form-input">{{ form.password }}</div>
<input type="submit" value="login" class="NB-modal-submit-button NB-modal-submit-green NB-static-form-submit" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</div>
{% endblock %}

View file

@ -0,0 +1,19 @@
{% extends "oauth2_provider/base.html" %}
{% load i18n %}
{% load url from future %}
{% block content %}
<div class="block-center">
<h3 class="block-center-heading">{% trans "Are you sure to delete the application" %} {{ application.name }}?</h3>
<form method="post" action="{% url 'oauth2_provider:delete' application.pk %}">
{% csrf_token %}
<div class="control-group">
<div class="controls">
<a class="btn btn-large" href="{% url "oauth2_provider:list" %}">{% trans "Cancel" %}</a>
<input type="submit" class="btn btn-large btn-danger" name="allow" value="{% trans "Delete" %}"/>
</div>
</div>
</form>
</div>
{% endblock content %}

View file

@ -0,0 +1,42 @@
{% extends "oauth2_provider/base.html" %}
{% load i18n %}
{% load url from future %}
{% block content %}
<div class="block-center">
<h3 class="block-center-heading">{{ application.name }}</h3>
<ul class="unstyled">
<li>
<p><b>{% trans "Client id" %}</b></p>
<input class="input-block-level" type="text" value="{{ application.client_id }}" readonly>
</li>
<li>
<p><b>{% trans "Client secret" %}</b></p>
<input class="input-block-level" type="text" value="{{ application.client_secret }}" readonly>
</li>
<li>
<p><b>{% trans "Client type" %}</b></p>
<p>{{ application.client_type }}</p>
</li>
<li>
<p><b>{% trans "Authorization Grant Type" %}</b></p>
<p>{{ application.authorization_grant_type }}</p>
</li>
<li>
<p><b>{% trans "Redirect Uris" %}</b></p>
<textarea class="input-block-level" readonly>{{ application.redirect_uris }}</textarea>
</li>
</ul>
<div class="btn-toolbar">
<a class="btn" href="{% url "oauth2_provider:list" %}">{% trans "Go Back" %}</a>
<a class="btn btn-primary" href="{% url "oauth2_provider:update" application.id %}">{% trans "Edit" %}</a>
<a class="btn btn-danger" href="{% url "oauth2_provider:delete" application.id %}">{% trans "Delete" %}</a>
</div>
</div>
{% endblock content %}

View file

@ -0,0 +1,43 @@
{% extends "oauth2_provider/base.html" %}
{% load i18n %}
{% load url from future %}
{% block content %}
<div class="block-center">
<form class="form-horizontal" method="post" action="{% block app-form-action-url %}{% url 'oauth2_provider:update' application.id %}{% endblock app-form-action-url %}">
<h3 class="block-center-heading">
{% block app-form-title %}
{% trans "Edit application" %} {{ application.name }}
{% endblock app-form-title %}
</h3>
{% csrf_token %}
{% for field in form %}
<div class="control-group {% if field.errors %}error{% endif %}">
<label class="control-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
<div class="controls">
{{ field }}
{% for error in field.errors %}
<span class="help-inline">{{ error }}</span>
{% endfor %}
</div>
</div>
{% endfor %}
<div class="control-group {% if form.non_field_errors %}error{% endif %}">
{% for error in form.non_field_errors %}
<span class="help-inline">{{ error }}</span>
{% endfor %}
</div>
<div class="control-group">
<div class="controls">
<a class="btn" href="{% block app-form-back-url %}{% url "oauth2_provider:detail" application.id %}{% endblock app-form-back-url %}">
{% trans "Go Back" %}
</a>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</form>
</div>
{% endblock %}

View file

@ -0,0 +1,20 @@
{% extends "oauth2_provider/base.html" %}
{% load i18n %}
{% load url from future %}
{% block content %}
<div class="block-center">
<h3 class="block-center-heading">{% trans "Your applications" %}</h3>
{% if applications %}
<ul>
{% for application in applications %}
<li><a href="{{ application.get_absolute_url }}">{{ application.name }}</a></li>
{% endfor %}
</ul>
<a class="btn btn-success" href="{% url "oauth2_provider:register" %}">New Application</a>
{% else %}
<p>{% trans "No applications defined" %}. <a href="{% url 'oauth2_provider:register' %}">{% trans "Click here" %}</a> {% trans "if you want to register a new one" %}</p>
{% endif %}
</div>
{% endblock content %}

View file

@ -0,0 +1,10 @@
{% extends "oauth2_provider/application_form.html" %}
{% load i18n %}
{% load url from future %}
{% block app-form-title %}{% trans "Register a new application" %}{% endblock app-form-title %}
{% block app-form-action-url %}{% url 'oauth2_provider:register' %}{% endblock app-form-action-url %}
{% block app-form-back-url %}{% url "oauth2_provider:list" %}"{% endblock app-form-back-url %}

View file

@ -0,0 +1,47 @@
{% extends "base.html" %}
{% block bodyclass %}NB-static NB-static-oauth{% endblock %}
{% load i18n %}
{% block content %}
<div class="NB-static-title">
Authorize {{ application.name }}
</div>
<div class="NB-static-form-wrapper block-center">
{% if not error %}
<form id="authorizationForm" method="post" class="NB-static-form">
<h3 class="block-center-heading">{{ application.name }} would like to access your NewsBlur account</h3>
{% csrf_token %}
{% for field in form %}
{% if field.is_hidden %}
{{ field }}
{% endif %}
{% endfor %}
<p>{{ application.name }} is requesting these permissions:</p>
<ul>
{% for scope in scopes_descriptions %}
<li>{{ scope }}</li>
{% endfor %}
</ul>
{{ form.errors }}
{{ form.non_field_errors }}
<div class="control-group">
<div class="controls">
<input type="submit" class="NB-static-form-submit NB-modal-submit-button NB-modal-submit-grey" value="Deny" style="float: right"/>
<input type="submit" class="NB-static-form-submit NB-modal-submit-button NB-modal-submit-green" name="allow" value="Authorize" style="float: left" />
</div>
</div>
</form>
{% else %}
<h2>Error: {{ error.error }}</h2>
<p>{{ error.description }}</p>
{% endif %}
</div>
{% endblock %}

View file

@ -70,7 +70,9 @@
<div class="NB-module-header-signup">Sign up</div>
</div>
<div class="NB-login">
<form method="post" action="{% url "login" %}">
<form method="post" action="{% url "welcome-login" %}">
{% csrf_token %}
<div>
{{ login_form.username.label_tag }}
{{ login_form.username }}
@ -97,7 +99,9 @@
</div>
<div class="NB-signup">
<form method="post" action="{% url "signup" %}">
<form method="post" action="{% url "welcome-signup" %}">
{% csrf_token %}
<div>
{{ signup_form.username.label_tag }}
{{ signup_form.username }}

11
urls.py
View file

@ -3,6 +3,9 @@ from django.conf import settings
from apps.reader import views as reader_views
from apps.social import views as social_views
from apps.static import views as static_views
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^$', reader_views.index, name='index'),
@ -32,6 +35,7 @@ urlpatterns = patterns('',
(r'^push/', include('apps.push.urls')),
(r'^categories/', include('apps.categories.urls')),
(r'^_haproxychk', static_views.haproxy_check),
url(r'^admin/', include(admin.site.urls)),
url(r'^about/?', static_views.about, name='about'),
url(r'^faq/?', static_views.faq, name='faq'),
url(r'^api/?', static_views.api, name='api'),
@ -46,6 +50,13 @@ urlpatterns = patterns('',
url(r'^android/?', static_views.android, name='android-static'),
url(r'^firefox/?', static_views.firefox, name='firefox'),
url(r'zebra/', include('zebra.urls', namespace="zebra", app_name='zebra')),
url(r'^account/login/?$',
'django.contrib.auth.views.login',
{'template_name': 'accounts/login.html'}, name='login'),
url(r'^account/logout/?$',
'django.contrib.auth.views.logout',
{'next_page': '/'}, name='logout'),
url(r'^account/', include('oauth2_provider.urls', namespace='oauth2_provider')),
)
if settings.DEBUG: