mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-04-27 03:00:12 -04:00
Add app 'type' field to app create form
This will allow for differing handling of permissions based on application type. * Web app: Web-hosted application. Can keep a client_secret * Installed app: e.g., android app. Client secret is not so secret See also: https://developers.google.com/accounts/docs/OAuth2InstalledApp * Script: For simple scripts and bots. Client secret is assumed secret.
This commit is contained in:
@@ -88,6 +88,7 @@ from r2.lib.merge import ConflictException
|
||||
import csv
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
from urlparse import urlparse
|
||||
import hashlib
|
||||
import re
|
||||
import urllib
|
||||
@@ -3540,19 +3541,36 @@ class ApiController(RedditController, OAuth2ResourceController):
|
||||
docs=dict(name="a name for the app")),
|
||||
about_url=VSanitizedUrl('about_url'),
|
||||
icon_url=VSanitizedUrl('icon_url'),
|
||||
redirect_uri=VSanitizedUrl('redirect_uri'))
|
||||
redirect_uri=VRedirectUri('redirect_uri'),
|
||||
app_type=VOneOf('app_type', ('web', 'installed', 'script')))
|
||||
@api_doc(api_section.apps)
|
||||
def POST_updateapp(self, form, jquery, name, about_url, icon_url, redirect_uri):
|
||||
def POST_updateapp(self, form, jquery, name, about_url, icon_url,
|
||||
redirect_uri, app_type):
|
||||
if (form.has_errors('name', errors.NO_TEXT) |
|
||||
form.has_errors('redirect_uri', errors.BAD_URL, errors.NO_URL)):
|
||||
form.has_errors('redirect_uri', errors.BAD_URL) |
|
||||
form.has_errors('redirect_uri', errors.NO_URL) |
|
||||
form.has_errors('app_type', errors.INVALID_OPTION)):
|
||||
return
|
||||
|
||||
# Web apps should be redirecting to web
|
||||
if app_type == 'web':
|
||||
parsed = urlparse(redirect_uri)
|
||||
if parsed.scheme not in ('http', 'https'):
|
||||
c.errors.add(errors.INVALID_SCHEME, field='redirect_uri',
|
||||
msg_params={"schemes": "http, https"})
|
||||
form.has_errors('redirect_uri', errors.INVALID_SCHEME)
|
||||
return
|
||||
|
||||
description = request.POST.get('description', '')
|
||||
|
||||
client_id = request.POST.get('client_id')
|
||||
if client_id:
|
||||
# client_id was specified, updating existing OAuth2Client
|
||||
client = OAuth2Client.get_token(client_id)
|
||||
if app_type != client.app_type:
|
||||
# App type cannot be changed after creation
|
||||
abort(400, "invalid request")
|
||||
return
|
||||
if not client:
|
||||
form.set_html('.status', _('invalid client id'))
|
||||
return
|
||||
@@ -3577,7 +3595,8 @@ class ApiController(RedditController, OAuth2ResourceController):
|
||||
client = OAuth2Client._new(name=name,
|
||||
description=description,
|
||||
about_url=about_url or '',
|
||||
redirect_uri=redirect_uri)
|
||||
redirect_uri=redirect_uri,
|
||||
app_type=app_type)
|
||||
client._commit()
|
||||
client.add_developer(c.user)
|
||||
form.set_html('.status', _('application created'))
|
||||
|
||||
@@ -33,6 +33,7 @@ error_list = dict((
|
||||
('VERIFIED_USER_REQUIRED', _("you need to set a valid email address to do that.")),
|
||||
('NO_URL', _('a url is required')),
|
||||
('BAD_URL', _('you should check that url')),
|
||||
('INVALID_SCHEME', _('URI scheme must be one of: %(schemes)s')),
|
||||
('BAD_CAPTCHA', _('care to try these again?')),
|
||||
('BAD_USERNAME', _('invalid user name')),
|
||||
('USERNAME_TAKEN', _('that username is already taken')),
|
||||
|
||||
@@ -249,9 +249,9 @@ def extract_title(data):
|
||||
|
||||
return title.encode('utf-8').strip()
|
||||
|
||||
valid_schemes = ('http', 'https', 'ftp', 'mailto')
|
||||
VALID_SCHEMES = ('http', 'https', 'ftp', 'mailto')
|
||||
valid_dns = re.compile('\A[-a-zA-Z0-9]+\Z')
|
||||
def sanitize_url(url, require_scheme = False):
|
||||
def sanitize_url(url, require_scheme=False, valid_schemes=VALID_SCHEMES):
|
||||
"""Validates that the url is of the form
|
||||
|
||||
scheme://domain/path/to/content#anchor?cruft
|
||||
@@ -262,7 +262,7 @@ def sanitize_url(url, require_scheme = False):
|
||||
otherwise validates"""
|
||||
|
||||
if not url:
|
||||
return
|
||||
return None
|
||||
|
||||
url = url.strip()
|
||||
if url.lower() == 'self':
|
||||
@@ -275,30 +275,34 @@ def sanitize_url(url, require_scheme = False):
|
||||
url = 'http://' + url
|
||||
u = urlparse(url)
|
||||
except ValueError:
|
||||
return
|
||||
return None
|
||||
|
||||
if u.scheme and u.scheme in valid_schemes:
|
||||
# if there is a scheme and no hostname, it is a bad url.
|
||||
if not u.hostname:
|
||||
return
|
||||
if u.username is not None or u.password is not None:
|
||||
return
|
||||
if not u.scheme:
|
||||
return None
|
||||
if valid_schemes is not None and u.scheme not in valid_schemes:
|
||||
return None
|
||||
|
||||
try:
|
||||
idna_hostname = u.hostname.encode('idna')
|
||||
except TypeError as e:
|
||||
g.log.warning("Bad hostname given [%r]: %s", u.hostname, e)
|
||||
raise
|
||||
except UnicodeError:
|
||||
return
|
||||
# if there is a scheme and no hostname, it is a bad url.
|
||||
if not u.hostname:
|
||||
return None
|
||||
if u.username is not None or u.password is not None:
|
||||
return None
|
||||
|
||||
for label in idna_hostname.split('.'):
|
||||
if not re.match(valid_dns, label):
|
||||
return
|
||||
try:
|
||||
idna_hostname = u.hostname.encode('idna')
|
||||
except TypeError as e:
|
||||
g.log.warning("Bad hostname given [%r]: %s", u.hostname, e)
|
||||
raise
|
||||
except UnicodeError:
|
||||
return None
|
||||
|
||||
if idna_hostname != u.hostname:
|
||||
url = urlunparse((u[0], idna_hostname, u[2], u[3], u[4], u[5]))
|
||||
return url
|
||||
for label in idna_hostname.split('.'):
|
||||
if not re.match(valid_dns, label):
|
||||
return None
|
||||
|
||||
if idna_hostname != u.hostname:
|
||||
url = urlunparse((u[0], idna_hostname, u[2], u[3], u[4], u[5]))
|
||||
return url
|
||||
|
||||
def trunc_string(text, length):
|
||||
return text[0:length]+'...' if len(text)>length else text
|
||||
|
||||
@@ -1299,15 +1299,19 @@ class VSanitizedUrl(Validator):
|
||||
|
||||
|
||||
class VUrl(VRequired):
|
||||
def __init__(self, item, allow_self=True, *a, **kw):
|
||||
def __init__(self, item, allow_self=True, require_scheme=False,
|
||||
valid_schemes=utils.VALID_SCHEMES, *a, **kw):
|
||||
self.allow_self = allow_self
|
||||
self.require_scheme = require_scheme
|
||||
self.valid_schemes = valid_schemes
|
||||
VRequired.__init__(self, item, errors.NO_URL, *a, **kw)
|
||||
|
||||
def run(self, url):
|
||||
if not url:
|
||||
return self.error(errors.NO_URL)
|
||||
|
||||
url = utils.sanitize_url(url)
|
||||
url = utils.sanitize_url(url, require_scheme=self.require_scheme,
|
||||
valid_schemes=self.valid_schemes)
|
||||
if not url:
|
||||
return self.error(errors.BAD_URL)
|
||||
|
||||
@@ -1323,6 +1327,19 @@ class VUrl(VRequired):
|
||||
return {self.param: "a valid URL"}
|
||||
|
||||
|
||||
class VRedirectUri(VUrl):
|
||||
def __init__(self, item, valid_schemes=None, *a, **kw):
|
||||
VUrl.__init__(self, item, allow_self=False, require_scheme=True,
|
||||
valid_schemes=valid_schemes, *a, **kw)
|
||||
|
||||
def param_docs(self):
|
||||
doc = "a valid URI"
|
||||
if self.valid_schemes:
|
||||
doc += " with one of the following schemes: "
|
||||
doc += ", ".join(self.valid_schemes)
|
||||
return {self.param: doc}
|
||||
|
||||
|
||||
class VShamedDomain(Validator):
|
||||
def run(self, url):
|
||||
if not url:
|
||||
|
||||
@@ -278,6 +278,7 @@ class OAuth2Client(Token):
|
||||
icon_url="",
|
||||
secret="",
|
||||
redirect_uri="",
|
||||
app_type="web",
|
||||
)
|
||||
_use_db = True
|
||||
_connection_pool = "main"
|
||||
|
||||
@@ -7553,7 +7553,7 @@ body:not(.gold) .allminus-link {
|
||||
.edit-app-icon-button { display: block; text-align: center; width: 72px; }
|
||||
.edit-app-form, .edit-app-form form { display: inline-block; }
|
||||
.edit-app-form th, .edit-app-icon th { width: 12ex; }
|
||||
.edit-app-form input, .edit-app-form textarea { margin: 0px; width: 50ex; }
|
||||
.edit-app-form input.text { margin: 0px; width: 50ex; }
|
||||
.edit-app-form input[name="name"] { width: 20ex !important; }
|
||||
.edit-app-form input[type="file"] { width: auto !important; }
|
||||
.edit-app-form input[type="submit"] {
|
||||
|
||||
@@ -56,6 +56,18 @@
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="app_type_selector(selection='web')">
|
||||
${utils.radio_type('app_type', "web", _("web app"),
|
||||
_("A web based application"),
|
||||
selection == "web")}
|
||||
${utils.radio_type('app_type', "installed", _("installed app"),
|
||||
_("An app intended for installation, such as on a mobile phone"),
|
||||
selection == "installed")}
|
||||
${utils.radio_type('app_type', "script", _("script"),
|
||||
_("Script for personal use. Will only have access to the developers accounts"),
|
||||
selection == "script")}
|
||||
</%def>
|
||||
|
||||
<%def name="editable_developer(app, dev)">
|
||||
<li id="app-dev-${app._id}-${dev._id}">
|
||||
${dev.name} 
|
||||
@@ -83,6 +95,15 @@
|
||||
${app.name}
|
||||
%endif
|
||||
</h2>
|
||||
<h3>
|
||||
%if app.app_type == 'web':
|
||||
${_("web app")}
|
||||
%elif app.app_type == 'installed':
|
||||
${_("installed app")}
|
||||
%elif app.app_type == 'script':
|
||||
${_("personal use script")}
|
||||
%endif
|
||||
</h3>
|
||||
<h3>${app._id}</h3>
|
||||
</div>
|
||||
<div class="app-description">${app.description}</div>
|
||||
@@ -105,6 +126,7 @@
|
||||
onsubmit="${"return post_form(this, 'updateapp', function(x) {return '%s'})" % _("updating...")}">
|
||||
<input type="hidden" name="uh" value="${c.modhash}" />
|
||||
<input type="hidden" name="client_id" value="${app._id}" />
|
||||
<input type="hidden" name="app_type" value="${app.app_type}" />
|
||||
<table class="preftable">
|
||||
<tr>
|
||||
<th>${_("secret")}</th>
|
||||
@@ -113,7 +135,7 @@
|
||||
<tr>
|
||||
<th>${_("name")}</th>
|
||||
<td class="prefright">
|
||||
<input name="name" value="${app.name}">
|
||||
<input class="text" name="name" value="${app.name}">
|
||||
${error_field("NO_TEXT", "name")}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -126,17 +148,18 @@
|
||||
<tr>
|
||||
<th>${_("about url")}</th>
|
||||
<td class="prefright">
|
||||
<input name="about_url" value="${app.about_url}">
|
||||
<input class="text" name="about_url" value="${app.about_url}">
|
||||
${error_field("BAD_URL", "about_url")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>${_("redirect uri")}</th>
|
||||
<td class="prefright">
|
||||
<input name="redirect_uri"
|
||||
value="${app.redirect_uri}">
|
||||
<input class="text" name="redirect_uri"
|
||||
value="${app.redirect_uri if app.redirect_uri else ''}">
|
||||
${error_field("NO_URL", "redirect_uri")}
|
||||
${error_field("BAD_URL", "redirect_uri")}
|
||||
${error_field("INVALID_SCHEME", "redirect_uri")}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -160,7 +183,7 @@
|
||||
onsubmit="${"return post_form(this, 'adddeveloper', function(x) {return '%s'})" % _("adding...")}">
|
||||
<input type="hidden" name="uh" value="${c.modhash}" />
|
||||
<input type="hidden" name="client_id" value="${app._id}" />
|
||||
${_('add developer')}: <input name="name">
|
||||
${_('add developer')}: <input class="text" name="name">
|
||||
<br>
|
||||
${error_field('TOO_MANY_DEVELOPERS', '')}
|
||||
${error_field('OAUTH2_INVALID_CLIENT', 'client_id')}
|
||||
@@ -302,10 +325,11 @@
|
||||
<tr>
|
||||
<th>${_("name")}</th>
|
||||
<td class="prefright">
|
||||
<input name="name">
|
||||
<input class="text" name="name">
|
||||
${error_field("NO_TEXT", "name")}
|
||||
</td>
|
||||
</tr>
|
||||
${app_type_selector()}
|
||||
<tr>
|
||||
<th>${_("description")}</th>
|
||||
<td class="prefright">
|
||||
@@ -315,16 +339,17 @@
|
||||
<tr>
|
||||
<th>${_("about url")}</th>
|
||||
<td class="prefright">
|
||||
<input name="about_url">
|
||||
<input class="text" name="about_url">
|
||||
${error_field("BAD_URL", "about_url")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>${_("redirect uri")}</th>
|
||||
<td class="prefright">
|
||||
<input name="redirect_uri">
|
||||
<input class="text" name="redirect_uri">
|
||||
${error_field("NO_URL", "redirect_uri")}
|
||||
${error_field("BAD_URL", "redirect_uri")}
|
||||
${error_field("INVALID_SCHEME", "redirect_uri")}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Reference in New Issue
Block a user