forked from CGM_Public/pretix_original
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae485a77d6 | ||
|
|
29e17d2284 | ||
|
|
6b95aca3f7 | ||
|
|
f227319f29 | ||
|
|
e63b85ccd6 | ||
|
|
bf3398e5f6 | ||
|
|
765d98464e | ||
|
|
66bdf9ab79 | ||
|
|
da830ef0a4 |
@@ -2,6 +2,7 @@
|
|||||||
cd /pretix/src
|
cd /pretix/src
|
||||||
export DJANGO_SETTINGS_MODULE=production_settings
|
export DJANGO_SETTINGS_MODULE=production_settings
|
||||||
export DATA_DIR=/data/
|
export DATA_DIR=/data/
|
||||||
|
export HOME=/pretix
|
||||||
NUM_WORKERS=10
|
NUM_WORKERS=10
|
||||||
|
|
||||||
if [ ! -d /data/logs ]; then
|
if [ ! -d /data/logs ]; then
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ named ``/etc/systemd/system/pretix.service`` with the following content::
|
|||||||
-v /etc/pretix:/etc/pretix \
|
-v /etc/pretix:/etc/pretix \
|
||||||
-v /var/run/redis:/var/run/redis \
|
-v /var/run/redis:/var/run/redis \
|
||||||
-v /var/run/mysqld:/var/run/mysqld \
|
-v /var/run/mysqld:/var/run/mysqld \
|
||||||
pretix/standalone all
|
pretix/standalone:stable all
|
||||||
ExecStop=/usr/bin/docker stop %n
|
ExecStop=/usr/bin/docker stop %n
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ to get a better plain text representation of your text. Note however, that for
|
|||||||
security reasons you can only use the following HTML elements::
|
security reasons you can only use the following HTML elements::
|
||||||
|
|
||||||
a, abbr, acronym, b, br, code, div, em, h1, h2,
|
a, abbr, acronym, b, br, code, div, em, h1, h2,
|
||||||
h3, h4, h5, h6, hr, i, li, ol, p, span, strong,
|
h3, h4, h5, h6, hr, i, li, ol, p, pre, span, strong,
|
||||||
table, tbody, td, thead, tr, ul
|
table, tbody, td, thead, tr, ul
|
||||||
|
|
||||||
Additionally, only the following attributes are allowed on them::
|
Additionally, only the following attributes are allowed on them::
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "1.10.0"
|
__version__ = "1.10.1"
|
||||||
|
|||||||
@@ -17,7 +17,18 @@ class CheckinList(LoggedModel):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_with_numbers(qs, event):
|
def annotate_with_numbers(qs, event):
|
||||||
from . import Order, OrderPosition
|
"""
|
||||||
|
Modifies a queryset of checkin lists by annotating it with the number of order positions and
|
||||||
|
checkins associated with it.
|
||||||
|
"""
|
||||||
|
# Import here to prevent circular import
|
||||||
|
from . import Order, OrderPosition, Item
|
||||||
|
|
||||||
|
# This is the mother of all subqueries. Sorry. I try to explain it, at least?
|
||||||
|
# First, we prepare a subquery that for every check-in that belongs to a paid-order
|
||||||
|
# position and to the list in question. Then, we check that it also belongs to the
|
||||||
|
# correct subevent (just to be sure) and aggregate over lists (so, over everything,
|
||||||
|
# since we filtered by lists).
|
||||||
cqs = Checkin.objects.filter(
|
cqs = Checkin.objects.filter(
|
||||||
position__order__event=event,
|
position__order__event=event,
|
||||||
position__order__status=Order.STATUS_PAID,
|
position__order__status=Order.STATUS_PAID,
|
||||||
@@ -30,6 +41,11 @@ class CheckinList(LoggedModel):
|
|||||||
).order_by().values('list').annotate(
|
).order_by().values('list').annotate(
|
||||||
c=Count('*')
|
c=Count('*')
|
||||||
).values('c')
|
).values('c')
|
||||||
|
|
||||||
|
# Now for the hard part: getting all order positions that contribute to this list. This
|
||||||
|
# requires us to use TWO subqueries. The first one, pqs_all, will only be used for check-in
|
||||||
|
# lists that contain all the products of the event. This is the simpler one, it basically
|
||||||
|
# looks like the check-in counter above.
|
||||||
pqs_all = OrderPosition.objects.filter(
|
pqs_all = OrderPosition.objects.filter(
|
||||||
order__event=event,
|
order__event=event,
|
||||||
order__status=Order.STATUS_PAID,
|
order__status=Order.STATUS_PAID,
|
||||||
@@ -41,10 +57,16 @@ class CheckinList(LoggedModel):
|
|||||||
).order_by().values('order__event').annotate(
|
).order_by().values('order__event').annotate(
|
||||||
c=Count('*')
|
c=Count('*')
|
||||||
).values('c')
|
).values('c')
|
||||||
|
|
||||||
|
# Now we need a subquery for the case of checkin lists that are limited to certain
|
||||||
|
# products. We cannot use OuterRef("limit_products") since that would do a cross-product
|
||||||
|
# with the products table and we'd get duplicate rows in the output with different annotations
|
||||||
|
# on them, which isn't useful at all. Therefore, we need to add a second layer of subqueries
|
||||||
|
# to retrieve all of those items and then check if the item_id is IN this subquery result.
|
||||||
pqs_limited = OrderPosition.objects.filter(
|
pqs_limited = OrderPosition.objects.filter(
|
||||||
order__event=event,
|
order__event=event,
|
||||||
order__status=Order.STATUS_PAID,
|
order__status=Order.STATUS_PAID,
|
||||||
item__in=OuterRef('limit_products')
|
item_id__in=Subquery(Item.objects.filter(checkinlist__pk=OuterRef(OuterRef('pk'))).values('pk'))
|
||||||
).filter(
|
).filter(
|
||||||
# This assumes that in an event with subevents, *all* positions have subevents
|
# This assumes that in an event with subevents, *all* positions have subevents
|
||||||
# and *all* checkin lists have a subevent assigned
|
# and *all* checkin lists have a subevent assigned
|
||||||
@@ -54,6 +76,9 @@ class CheckinList(LoggedModel):
|
|||||||
c=Count('*')
|
c=Count('*')
|
||||||
).values('c')
|
).values('c')
|
||||||
|
|
||||||
|
# Finally, we put all of this together. We force empty subquery aggregates to 0 by using Coalesce()
|
||||||
|
# and decide which subquery to use for this row. In the end, we compute an integer percentage in case
|
||||||
|
# we want to display a progress bar.
|
||||||
return qs.annotate(
|
return qs.annotate(
|
||||||
checkin_count=Coalesce(Subquery(cqs, output_field=models.IntegerField()), 0),
|
checkin_count=Coalesce(Subquery(cqs, output_field=models.IntegerField()), 0),
|
||||||
position_count=Coalesce(Case(
|
position_count=Coalesce(Case(
|
||||||
|
|||||||
@@ -769,6 +769,9 @@ class OrderPosition(AbstractPosition):
|
|||||||
'order_code': order.code
|
'order_code': order.code
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Delete afterwards. Deleting in between might cause deletion of things related to add-ons
|
||||||
|
# due to the deletion cascade.
|
||||||
|
for cartpos in cp:
|
||||||
cartpos.delete()
|
cartpos.delete()
|
||||||
return ops
|
return ops
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ ALLOWED_TAGS = [
|
|||||||
'h4',
|
'h4',
|
||||||
'h5',
|
'h5',
|
||||||
'h6',
|
'h6',
|
||||||
|
'pre',
|
||||||
# Update doc/user/markdown.rst if you change this!
|
# Update doc/user/markdown.rst if you change this!
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,13 @@ class FilterForm(forms.Form):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_order_by(self):
|
||||||
|
o = self.cleaned_data.get('ordering')
|
||||||
|
if o.startswith('-'):
|
||||||
|
return '-' + self.orders[o[1:]]
|
||||||
|
else:
|
||||||
|
return self.orders[o]
|
||||||
|
|
||||||
|
|
||||||
class OrderFilterForm(FilterForm):
|
class OrderFilterForm(FilterForm):
|
||||||
query = forms.CharField(
|
query = forms.CharField(
|
||||||
@@ -139,7 +146,7 @@ class OrderFilterForm(FilterForm):
|
|||||||
qs = qs.filter(status=s)
|
qs = qs.filter(status=s)
|
||||||
|
|
||||||
if fdata.get('ordering'):
|
if fdata.get('ordering'):
|
||||||
qs = qs.order_by(self.orders[fdata.get('ordering')])
|
qs = qs.order_by(self.get_order_by())
|
||||||
|
|
||||||
if fdata.get('provider'):
|
if fdata.get('provider'):
|
||||||
qs = qs.filter(payment_provider=fdata.get('provider'))
|
qs = qs.filter(payment_provider=fdata.get('provider'))
|
||||||
@@ -276,7 +283,7 @@ class SubEventFilterForm(FilterForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if fdata.get('ordering'):
|
if fdata.get('ordering'):
|
||||||
qs = qs.order_by(self.orders[fdata.get('ordering')])
|
qs = qs.order_by(self.get_order_by())
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
@@ -309,7 +316,7 @@ class OrganizerFilterForm(FilterForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if fdata.get('ordering'):
|
if fdata.get('ordering'):
|
||||||
qs = qs.order_by(dict(self.fields['ordering'].choices)[fdata.get('ordering')])
|
qs = qs.order_by(self.get_order_by())
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
@@ -390,7 +397,7 @@ class EventFilterForm(FilterForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if fdata.get('ordering'):
|
if fdata.get('ordering'):
|
||||||
qs = qs.order_by(self.orders[fdata.get('ordering')])
|
qs = qs.order_by(self.get_order_by())
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|||||||
@@ -192,14 +192,6 @@ def shop_state_widget(sender, **kwargs):
|
|||||||
|
|
||||||
@receiver(signal=event_dashboard_widgets)
|
@receiver(signal=event_dashboard_widgets)
|
||||||
def checkin_widget(sender, subevent=None, **kwargs):
|
def checkin_widget(sender, subevent=None, **kwargs):
|
||||||
size_qs = OrderPosition.objects.filter(order__event=sender, order__status='p')
|
|
||||||
checked_qs = OrderPosition.objects.filter(order__event=sender, order__status='p', checkins__isnull=False)
|
|
||||||
|
|
||||||
# if this setting is False, we check only items for admission
|
|
||||||
if not sender.settings.ticket_download_nonadm:
|
|
||||||
size_qs = size_qs.filter(item__admission=True)
|
|
||||||
checked_qs = checked_qs.filter(item__admission=True)
|
|
||||||
|
|
||||||
widgets = []
|
widgets = []
|
||||||
qs = sender.checkin_lists.filter(subevent=subevent)
|
qs = sender.checkin_lists.filter(subevent=subevent)
|
||||||
qs = CheckinList.annotate_with_numbers(qs, sender)
|
qs = CheckinList.annotate_with_numbers(qs, sender)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//Copyright 2014-2015 Google Inc. All rights reserved.
|
//Copyright 2014-2015 Google Inc. All rights reserved.
|
||||||
|
// Extended by Raphael Michel 2017 for compatibility with Firefox 57
|
||||||
//Use of this source code is governed by a BSD-style
|
//Use of this source code is governed by a BSD-style
|
||||||
//license that can be found in the LICENSE file or at
|
//license that can be found in the LICENSE file or at
|
||||||
//https://developers.google.com/open-source/licenses/bsd
|
//https://developers.google.com/open-source/licenses/bsd
|
||||||
@@ -10,63 +10,64 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
/**
|
var build_u2f_object = function () {
|
||||||
|
/**
|
||||||
* Namespace for the U2F api.
|
* Namespace for the U2F api.
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
var u2f = u2f || {};
|
var u2f = u2f || {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FIDO U2F Javascript API Version
|
* FIDO U2F Javascript API Version
|
||||||
* @number
|
* @number
|
||||||
*/
|
*/
|
||||||
var js_api_version;
|
var js_api_version;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The U2F extension id
|
* The U2F extension id
|
||||||
* @const {string}
|
* @const {string}
|
||||||
*/
|
*/
|
||||||
// The Chrome packaged app extension ID.
|
// The Chrome packaged app extension ID.
|
||||||
// Uncomment this if you want to deploy a server instance that uses
|
// Uncomment this if you want to deploy a server instance that uses
|
||||||
// the package Chrome app and does not require installing the U2F Chrome extension.
|
// the package Chrome app and does not require installing the U2F Chrome extension.
|
||||||
u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd';
|
u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd';
|
||||||
// The U2F Chrome extension ID.
|
// The U2F Chrome extension ID.
|
||||||
// Uncomment this if you want to deploy a server instance that uses
|
// Uncomment this if you want to deploy a server instance that uses
|
||||||
// the U2F Chrome extension to authenticate.
|
// the U2F Chrome extension to authenticate.
|
||||||
// u2f.EXTENSION_ID = 'pfboblefjcgdjicmnffhdgionmgcdmne';
|
// u2f.EXTENSION_ID = 'pfboblefjcgdjicmnffhdgionmgcdmne';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message types for messsages to/from the extension
|
* Message types for messsages to/from the extension
|
||||||
* @const
|
* @const
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
*/
|
*/
|
||||||
u2f.MessageTypes = {
|
u2f.MessageTypes = {
|
||||||
'U2F_REGISTER_REQUEST': 'u2f_register_request',
|
'U2F_REGISTER_REQUEST': 'u2f_register_request',
|
||||||
'U2F_REGISTER_RESPONSE': 'u2f_register_response',
|
'U2F_REGISTER_RESPONSE': 'u2f_register_response',
|
||||||
'U2F_SIGN_REQUEST': 'u2f_sign_request',
|
'U2F_SIGN_REQUEST': 'u2f_sign_request',
|
||||||
'U2F_SIGN_RESPONSE': 'u2f_sign_response',
|
'U2F_SIGN_RESPONSE': 'u2f_sign_response',
|
||||||
'U2F_GET_API_VERSION_REQUEST': 'u2f_get_api_version_request',
|
'U2F_GET_API_VERSION_REQUEST': 'u2f_get_api_version_request',
|
||||||
'U2F_GET_API_VERSION_RESPONSE': 'u2f_get_api_version_response'
|
'ndowsU2F_GET_API_VERSION_RESPONSE': 'u2f_get_api_version_response'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response status codes
|
* Response status codes
|
||||||
* @const
|
* @const
|
||||||
* @enum {number}
|
* @enum {number}
|
||||||
*/
|
*/
|
||||||
u2f.ErrorCodes = {
|
u2f.ErrorCodes = {
|
||||||
'OK': 0,
|
'OK': 0,
|
||||||
'OTHER_ERROR': 1,
|
'OTHER_ERROR': 1,
|
||||||
'BAD_REQUEST': 2,
|
'BAD_REQUEST': 2,
|
||||||
'CONFIGURATION_UNSUPPORTED': 3,
|
'CONFIGURATION_UNSUPPORTED': 3,
|
||||||
'DEVICE_INELIGIBLE': 4,
|
'DEVICE_INELIGIBLE': 4,
|
||||||
'TIMEOUT': 5
|
'TIMEOUT': 5
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A message for registration requests
|
* A message for registration requests
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* type: u2f.MessageTypes,
|
* type: u2f.MessageTypes,
|
||||||
@@ -75,10 +76,10 @@ u2f.ErrorCodes = {
|
|||||||
* requestId: ?number
|
* requestId: ?number
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
u2f.U2fRequest;
|
u2f.U2fRequest;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A message for registration responses
|
* A message for registration responses
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* type: u2f.MessageTypes,
|
* type: u2f.MessageTypes,
|
||||||
@@ -86,32 +87,32 @@ u2f.U2fRequest;
|
|||||||
* requestId: ?number
|
* requestId: ?number
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
u2f.U2fResponse;
|
u2f.U2fResponse;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An error object for responses
|
* An error object for responses
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* errorCode: u2f.ErrorCodes,
|
* errorCode: u2f.ErrorCodes,
|
||||||
* errorMessage: ?string
|
* errorMessage: ?string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
u2f.Error;
|
u2f.Error;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data object for a single sign request.
|
* Data object for a single sign request.
|
||||||
* @typedef {enum {BLUETOOTH_RADIO, BLUETOOTH_LOW_ENERGY, USB, NFC}}
|
* @typedef {enum {BLUETOOTH_RADIO, BLUETOOTH_LOW_ENERGY, USB, NFC}}
|
||||||
*/
|
*/
|
||||||
u2f.Transport;
|
u2f.Transport;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data object for a single sign request.
|
* Data object for a single sign request.
|
||||||
* @typedef {Array<u2f.Transport>}
|
* @typedef {Array<u2f.Transport>}
|
||||||
*/
|
*/
|
||||||
u2f.Transports;
|
u2f.Transports;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data object for a single sign request.
|
* Data object for a single sign request.
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* version: string,
|
* version: string,
|
||||||
@@ -120,10 +121,10 @@ u2f.Transports;
|
|||||||
* appId: string
|
* appId: string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
u2f.SignRequest;
|
u2f.SignRequest;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data object for a sign response.
|
* Data object for a sign response.
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* keyHandle: string,
|
* keyHandle: string,
|
||||||
@@ -131,20 +132,20 @@ u2f.SignRequest;
|
|||||||
* clientData: string
|
* clientData: string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
u2f.SignResponse;
|
u2f.SignResponse;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data object for a registration request.
|
* Data object for a registration request.
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* version: string,
|
* version: string,
|
||||||
* challenge: string
|
* challenge: string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
u2f.RegisterRequest;
|
u2f.RegisterRequest;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data object for a registration response.
|
* Data object for a registration response.
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* version: string,
|
* version: string,
|
||||||
@@ -153,10 +154,10 @@ u2f.RegisterRequest;
|
|||||||
* appId: string
|
* appId: string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
u2f.RegisterResponse;
|
u2f.RegisterResponse;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data object for a registered key.
|
* Data object for a registered key.
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* version: string,
|
* version: string,
|
||||||
@@ -165,26 +166,26 @@ u2f.RegisterResponse;
|
|||||||
* appId: ?string
|
* appId: ?string
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
u2f.RegisteredKey;
|
u2f.RegisteredKey;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data object for a get API register response.
|
* Data object for a get API register response.
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* js_api_version: number
|
* js_api_version: number
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
u2f.GetJsApiVersionResponse;
|
u2f.GetJsApiVersionResponse;
|
||||||
|
|
||||||
|
|
||||||
//Low level MessagePort API support
|
//Low level MessagePort API support
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up a MessagePort to the U2F extension using the
|
* Sets up a MessagePort to the U2F extension using the
|
||||||
* available mechanisms.
|
* available mechanisms.
|
||||||
* @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
|
* @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
|
||||||
*/
|
*/
|
||||||
u2f.getMessagePort = function(callback) {
|
u2f.getMessagePort = function (callback) {
|
||||||
if (typeof chrome != 'undefined' && chrome.runtime) {
|
if (typeof chrome != 'undefined' && chrome.runtime) {
|
||||||
// The actual message here does not matter, but we need to get a reply
|
// The actual message here does not matter, but we need to get a reply
|
||||||
// for the callback to run. Thus, send an empty signature request
|
// for the callback to run. Thus, send an empty signature request
|
||||||
@@ -193,7 +194,7 @@ u2f.getMessagePort = function(callback) {
|
|||||||
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
|
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
|
||||||
signRequests: []
|
signRequests: []
|
||||||
};
|
};
|
||||||
chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() {
|
chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function () {
|
||||||
if (!chrome.runtime.lastError) {
|
if (!chrome.runtime.lastError) {
|
||||||
// We are on a whitelisted origin and can talk directly
|
// We are on a whitelisted origin and can talk directly
|
||||||
// with the extension.
|
// with the extension.
|
||||||
@@ -213,80 +214,80 @@ u2f.getMessagePort = function(callback) {
|
|||||||
// when this origin doesn't have access to any extensions.
|
// when this origin doesn't have access to any extensions.
|
||||||
u2f.getIframePort_(callback);
|
u2f.getIframePort_(callback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detect chrome running on android based on the browser's useragent.
|
* Detect chrome running on android based on the browser's useragent.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.isAndroidChrome_ = function() {
|
u2f.isAndroidChrome_ = function () {
|
||||||
var userAgent = navigator.userAgent;
|
var userAgent = navigator.userAgent;
|
||||||
return userAgent.indexOf('Chrome') != -1 &&
|
return userAgent.indexOf('Chrome') != -1 &&
|
||||||
userAgent.indexOf('Android') != -1;
|
userAgent.indexOf('Android') != -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detect chrome running on iOS based on the browser's platform.
|
* Detect chrome running on iOS based on the browser's platform.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.isIosChrome_ = function() {
|
u2f.isIosChrome_ = function () {
|
||||||
return $.inArray(navigator.platform, ["iPhone", "iPad", "iPod"]) > -1;
|
return $.inArray(navigator.platform, ["iPhone", "iPad", "iPod"]) > -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects directly to the extension via chrome.runtime.connect.
|
* Connects directly to the extension via chrome.runtime.connect.
|
||||||
* @param {function(u2f.WrappedChromeRuntimePort_)} callback
|
* @param {function(u2f.WrappedChromeRuntimePort_)} callback
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.getChromeRuntimePort_ = function(callback) {
|
u2f.getChromeRuntimePort_ = function (callback) {
|
||||||
var port = chrome.runtime.connect(u2f.EXTENSION_ID,
|
var port = chrome.runtime.connect(u2f.EXTENSION_ID,
|
||||||
{'includeTlsChannelId': true});
|
{'includeTlsChannelId': true});
|
||||||
setTimeout(function() {
|
setTimeout(function () {
|
||||||
callback(new u2f.WrappedChromeRuntimePort_(port));
|
callback(new u2f.WrappedChromeRuntimePort_(port));
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a 'port' abstraction to the Authenticator app.
|
* Return a 'port' abstraction to the Authenticator app.
|
||||||
* @param {function(u2f.WrappedAuthenticatorPort_)} callback
|
* @param {function(u2f.WrappedAuthenticatorPort_)} callback
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.getAuthenticatorPort_ = function(callback) {
|
u2f.getAuthenticatorPort_ = function (callback) {
|
||||||
setTimeout(function() {
|
setTimeout(function () {
|
||||||
callback(new u2f.WrappedAuthenticatorPort_());
|
callback(new u2f.WrappedAuthenticatorPort_());
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a 'port' abstraction to the iOS client app.
|
* Return a 'port' abstraction to the iOS client app.
|
||||||
* @param {function(u2f.WrappedIosPort_)} callback
|
* @param {function(u2f.WrappedIosPort_)} callback
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.getIosPort_ = function(callback) {
|
u2f.getIosPort_ = function (callback) {
|
||||||
setTimeout(function() {
|
setTimeout(function () {
|
||||||
callback(new u2f.WrappedIosPort_());
|
callback(new u2f.WrappedIosPort_());
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper for chrome.runtime.Port that is compatible with MessagePort.
|
* A wrapper for chrome.runtime.Port that is compatible with MessagePort.
|
||||||
* @param {Port} port
|
* @param {Port} port
|
||||||
* @constructor
|
* @constructor
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.WrappedChromeRuntimePort_ = function(port) {
|
u2f.WrappedChromeRuntimePort_ = function (port) {
|
||||||
this.port_ = port;
|
this.port_ = port;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format and return a sign request compliant with the JS API version supported by the extension.
|
* Format and return a sign request compliant with the JS API version supported by the extension.
|
||||||
* @param {Array<u2f.SignRequest>} signRequests
|
* @param {Array<u2f.SignRequest>} signRequests
|
||||||
* @param {number} timeoutSeconds
|
* @param {number} timeoutSeconds
|
||||||
* @param {number} reqId
|
* @param {number} reqId
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
u2f.formatSignRequest_ =
|
u2f.formatSignRequest_ =
|
||||||
function(appId, challenge, registeredKeys, timeoutSeconds, reqId) {
|
function (appId, challenge, registeredKeys, timeoutSeconds, reqId) {
|
||||||
if (js_api_version === undefined || js_api_version < 1.1) {
|
if (js_api_version === undefined || js_api_version < 1.1) {
|
||||||
// Adapt request to the 1.0 JS API
|
// Adapt request to the 1.0 JS API
|
||||||
var signRequests = [];
|
var signRequests = [];
|
||||||
@@ -314,9 +315,9 @@ u2f.formatSignRequest_ =
|
|||||||
timeoutSeconds: timeoutSeconds,
|
timeoutSeconds: timeoutSeconds,
|
||||||
requestId: reqId
|
requestId: reqId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format and return a register request compliant with the JS API version supported by the extension..
|
* Format and return a register request compliant with the JS API version supported by the extension..
|
||||||
* @param {Array<u2f.SignRequest>} signRequests
|
* @param {Array<u2f.SignRequest>} signRequests
|
||||||
* @param {Array<u2f.RegisterRequest>} signRequests
|
* @param {Array<u2f.RegisterRequest>} signRequests
|
||||||
@@ -324,8 +325,8 @@ u2f.formatSignRequest_ =
|
|||||||
* @param {number} reqId
|
* @param {number} reqId
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
u2f.formatRegisterRequest_ =
|
u2f.formatRegisterRequest_ =
|
||||||
function(appId, registeredKeys, registerRequests, timeoutSeconds, reqId) {
|
function (appId, registeredKeys, registerRequests, timeoutSeconds, reqId) {
|
||||||
if (js_api_version === undefined || js_api_version < 1.1) {
|
if (js_api_version === undefined || js_api_version < 1.1) {
|
||||||
// Adapt request to the 1.0 JS API
|
// Adapt request to the 1.0 JS API
|
||||||
for (var i = 0; i < registerRequests.length; i++) {
|
for (var i = 0; i < registerRequests.length; i++) {
|
||||||
@@ -357,74 +358,74 @@ u2f.formatRegisterRequest_ =
|
|||||||
timeoutSeconds: timeoutSeconds,
|
timeoutSeconds: timeoutSeconds,
|
||||||
requestId: reqId
|
requestId: reqId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Posts a message on the underlying channel.
|
* Posts a message on the underlying channel.
|
||||||
* @param {Object} message
|
* @param {Object} message
|
||||||
*/
|
*/
|
||||||
u2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) {
|
u2f.WrappedChromeRuntimePort_.prototype.postMessage = function (message) {
|
||||||
this.port_.postMessage(message);
|
this.port_.postMessage(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulates the HTML 5 addEventListener interface. Works only for the
|
* Emulates the HTML 5 addEventListener interface. Works only for the
|
||||||
* onmessage event, which is hooked up to the chrome.runtime.Port.onMessage.
|
* onmessage event, which is hooked up to the chrome.runtime.Port.onMessage.
|
||||||
* @param {string} eventName
|
* @param {string} eventName
|
||||||
* @param {function({data: Object})} handler
|
* @param {function({data: Object})} handler
|
||||||
*/
|
*/
|
||||||
u2f.WrappedChromeRuntimePort_.prototype.addEventListener =
|
u2f.WrappedChromeRuntimePort_.prototype.addEventListener =
|
||||||
function(eventName, handler) {
|
function (eventName, handler) {
|
||||||
var name = eventName.toLowerCase();
|
var name = eventName.toLowerCase();
|
||||||
if (name == 'message' || name == 'onmessage') {
|
if (name == 'message' || name == 'onmessage') {
|
||||||
this.port_.onMessage.addListener(function(message) {
|
this.port_.onMessage.addListener(function (message) {
|
||||||
// Emulate a minimal MessageEvent object
|
// Emulate a minimal MessageEvent object
|
||||||
handler({'data': message});
|
handler({'data': message});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error('WrappedChromeRuntimePort only supports onMessage');
|
console.error('WrappedChromeRuntimePort only supports onMessage');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap the Authenticator app with a MessagePort interface.
|
* Wrap the Authenticator app with a MessagePort interface.
|
||||||
* @constructor
|
* @constructor
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.WrappedAuthenticatorPort_ = function() {
|
u2f.WrappedAuthenticatorPort_ = function () {
|
||||||
this.requestId_ = -1;
|
this.requestId_ = -1;
|
||||||
this.requestObject_ = null;
|
this.requestObject_ = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launch the Authenticator intent.
|
* Launch the Authenticator intent.
|
||||||
* @param {Object} message
|
* @param {Object} message
|
||||||
*/
|
*/
|
||||||
u2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) {
|
u2f.WrappedAuthenticatorPort_.prototype.postMessage = function (message) {
|
||||||
var intentUrl =
|
var intentUrl =
|
||||||
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +
|
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +
|
||||||
';S.request=' + encodeURIComponent(JSON.stringify(message)) +
|
';S.request=' + encodeURIComponent(JSON.stringify(message)) +
|
||||||
';end';
|
';end';
|
||||||
document.location = intentUrl;
|
document.location = intentUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells what type of port this is.
|
* Tells what type of port this is.
|
||||||
* @return {String} port type
|
* @return {String} port type
|
||||||
*/
|
*/
|
||||||
u2f.WrappedAuthenticatorPort_.prototype.getPortType = function() {
|
u2f.WrappedAuthenticatorPort_.prototype.getPortType = function () {
|
||||||
return "WrappedAuthenticatorPort_";
|
return "WrappedAuthenticatorPort_";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulates the HTML 5 addEventListener interface.
|
* Emulates the HTML 5 addEventListener interface.
|
||||||
* @param {string} eventName
|
* @param {string} eventName
|
||||||
* @param {function({data: Object})} handler
|
* @param {function({data: Object})} handler
|
||||||
*/
|
*/
|
||||||
u2f.WrappedAuthenticatorPort_.prototype.addEventListener = function(eventName, handler) {
|
u2f.WrappedAuthenticatorPort_.prototype.addEventListener = function (eventName, handler) {
|
||||||
var name = eventName.toLowerCase();
|
var name = eventName.toLowerCase();
|
||||||
if (name == 'message') {
|
if (name == 'message') {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -435,15 +436,15 @@ u2f.WrappedAuthenticatorPort_.prototype.addEventListener = function(eventName, h
|
|||||||
} else {
|
} else {
|
||||||
console.error('WrappedAuthenticatorPort only supports message');
|
console.error('WrappedAuthenticatorPort only supports message');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback invoked when a response is received from the Authenticator.
|
* Callback invoked when a response is received from the Authenticator.
|
||||||
* @param function({data: Object}) callback
|
* @param function({data: Object}) callback
|
||||||
* @param {Object} message message Object
|
* @param {Object} message message Object
|
||||||
*/
|
*/
|
||||||
u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ =
|
u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ =
|
||||||
function(callback, message) {
|
function (callback, message) {
|
||||||
var messageObject = JSON.parse(message.data);
|
var messageObject = JSON.parse(message.data);
|
||||||
var intentUrl = messageObject['intentURL'];
|
var intentUrl = messageObject['intentURL'];
|
||||||
|
|
||||||
@@ -455,59 +456,60 @@ u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ =
|
|||||||
}
|
}
|
||||||
|
|
||||||
callback({'data': responseObject});
|
callback({'data': responseObject});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base URL for intents to Authenticator.
|
* Base URL for intents to Authenticator.
|
||||||
* @const
|
* @const
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ =
|
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ =
|
||||||
'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE';
|
'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap the iOS client app with a MessagePort interface.
|
* Wrap the iOS client app with a MessagePort interface.
|
||||||
* @constructor
|
* @constructor
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.WrappedIosPort_ = function() {};
|
u2f.WrappedIosPort_ = function () {
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launch the iOS client app request
|
* Launch the iOS client app request
|
||||||
* @param {Object} message
|
* @param {Object} message
|
||||||
*/
|
*/
|
||||||
u2f.WrappedIosPort_.prototype.postMessage = function(message) {
|
u2f.WrappedIosPort_.prototype.postMessage = function (message) {
|
||||||
var str = JSON.stringify(message);
|
var str = JSON.stringify(message);
|
||||||
var url = "u2f://auth?" + encodeURI(str);
|
var url = "u2f://auth?" + encodeURI(str);
|
||||||
location.replace(url);
|
location.replace(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells what type of port this is.
|
* Tells what type of port this is.
|
||||||
* @return {String} port type
|
* @return {String} port type
|
||||||
*/
|
*/
|
||||||
u2f.WrappedIosPort_.prototype.getPortType = function() {
|
u2f.WrappedIosPort_.prototype.getPortType = function () {
|
||||||
return "WrappedIosPort_";
|
return "WrappedIosPort_";
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulates the HTML 5 addEventListener interface.
|
* Emulates the HTML 5 addEventListener interface.
|
||||||
* @param {string} eventName
|
* @param {string} eventName
|
||||||
* @param {function({data: Object})} handler
|
* @param {function({data: Object})} handler
|
||||||
*/
|
*/
|
||||||
u2f.WrappedIosPort_.prototype.addEventListener = function(eventName, handler) {
|
u2f.WrappedIosPort_.prototype.addEventListener = function (eventName, handler) {
|
||||||
var name = eventName.toLowerCase();
|
var name = eventName.toLowerCase();
|
||||||
if (name !== 'message') {
|
if (name !== 'message') {
|
||||||
console.error('WrappedIosPort only supports message');
|
console.error('WrappedIosPort only supports message');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up an embedded trampoline iframe, sourced from the extension.
|
* Sets up an embedded trampoline iframe, sourced from the extension.
|
||||||
* @param {function(MessagePort)} callback
|
* @param {function(MessagePort)} callback
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.getIframePort_ = function(callback) {
|
u2f.getIframePort_ = function (callback) {
|
||||||
// Create the iframe
|
// Create the iframe
|
||||||
var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID;
|
var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID;
|
||||||
var iframe = document.createElement('iframe');
|
var iframe = document.createElement('iframe');
|
||||||
@@ -516,7 +518,7 @@ u2f.getIframePort_ = function(callback) {
|
|||||||
document.body.appendChild(iframe);
|
document.body.appendChild(iframe);
|
||||||
|
|
||||||
var channel = new MessageChannel();
|
var channel = new MessageChannel();
|
||||||
var ready = function(message) {
|
var ready = function (message) {
|
||||||
if (message.data == 'ready') {
|
if (message.data == 'ready') {
|
||||||
channel.port1.removeEventListener('message', ready);
|
channel.port1.removeEventListener('message', ready);
|
||||||
callback(channel.port1);
|
callback(channel.port1);
|
||||||
@@ -527,61 +529,61 @@ u2f.getIframePort_ = function(callback) {
|
|||||||
channel.port1.addEventListener('message', ready);
|
channel.port1.addEventListener('message', ready);
|
||||||
channel.port1.start();
|
channel.port1.start();
|
||||||
|
|
||||||
iframe.addEventListener('load', function() {
|
iframe.addEventListener('load', function () {
|
||||||
// Deliver the port to the iframe and initialize
|
// Deliver the port to the iframe and initialize
|
||||||
iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]);
|
iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//High-level JS API
|
//High-level JS API
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default extension response timeout in seconds.
|
* Default extension response timeout in seconds.
|
||||||
* @const
|
* @const
|
||||||
*/
|
*/
|
||||||
u2f.EXTENSION_TIMEOUT_SEC = 30;
|
u2f.EXTENSION_TIMEOUT_SEC = 30;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A singleton instance for a MessagePort to the extension.
|
* A singleton instance for a MessagePort to the extension.
|
||||||
* @type {MessagePort|u2f.WrappedChromeRuntimePort_}
|
* @type {MessagePort|u2f.WrappedChromeRuntimePort_}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.port_ = null;
|
u2f.port_ = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callbacks waiting for a port
|
* Callbacks waiting for a port
|
||||||
* @type {Array<function((MessagePort|u2f.WrappedChromeRuntimePort_))>}
|
* @type {Array<function((MessagePort|u2f.WrappedChromeRuntimePort_))>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.waitingForPort_ = [];
|
u2f.waitingForPort_ = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A counter for requestIds.
|
* A counter for requestIds.
|
||||||
* @type {number}
|
* @type {number}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.reqCounter_ = 0;
|
u2f.reqCounter_ = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map from requestIds to client callbacks
|
* A map from requestIds to client callbacks
|
||||||
* @type {Object.<number,(function((u2f.Error|u2f.RegisterResponse))
|
* @type {Object.<number,(function((u2f.Error|u2f.RegisterResponse))
|
||||||
* |function((u2f.Error|u2f.SignResponse)))>}
|
* |function((u2f.Error|u2f.SignResponse)))>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.callbackMap_ = {};
|
u2f.callbackMap_ = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates or retrieves the MessagePort singleton to use.
|
* Creates or retrieves the MessagePort singleton to use.
|
||||||
* @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
|
* @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.getPortSingleton_ = function(callback) {
|
u2f.getPortSingleton_ = function (callback) {
|
||||||
if (u2f.port_) {
|
if (u2f.port_) {
|
||||||
callback(u2f.port_);
|
callback(u2f.port_);
|
||||||
} else {
|
} else {
|
||||||
if (u2f.waitingForPort_.length == 0) {
|
if (u2f.waitingForPort_.length == 0) {
|
||||||
u2f.getMessagePort(function(port) {
|
u2f.getMessagePort(function (port) {
|
||||||
u2f.port_ = port;
|
u2f.port_ = port;
|
||||||
u2f.port_.addEventListener('message',
|
u2f.port_.addEventListener('message',
|
||||||
/** @type {function(Event)} */ (u2f.responseHandler_));
|
/** @type {function(Event)} */ (u2f.responseHandler_));
|
||||||
@@ -593,14 +595,14 @@ u2f.getPortSingleton_ = function(callback) {
|
|||||||
}
|
}
|
||||||
u2f.waitingForPort_.push(callback);
|
u2f.waitingForPort_.push(callback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles response messages from the extension.
|
* Handles response messages from the extension.
|
||||||
* @param {MessageEvent.<u2f.Response>} message
|
* @param {MessageEvent.<u2f.Response>} message
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
u2f.responseHandler_ = function(message) {
|
u2f.responseHandler_ = function (message) {
|
||||||
var response = message.data;
|
var response = message.data;
|
||||||
var reqId = response['requestId'];
|
var reqId = response['requestId'];
|
||||||
if (!reqId || !u2f.callbackMap_[reqId]) {
|
if (!reqId || !u2f.callbackMap_[reqId]) {
|
||||||
@@ -610,9 +612,9 @@ u2f.responseHandler_ = function(message) {
|
|||||||
var cb = u2f.callbackMap_[reqId];
|
var cb = u2f.callbackMap_[reqId];
|
||||||
delete u2f.callbackMap_[reqId];
|
delete u2f.callbackMap_[reqId];
|
||||||
cb(response['responseData']);
|
cb(response['responseData']);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches an array of sign requests to available U2F tokens.
|
* Dispatches an array of sign requests to available U2F tokens.
|
||||||
* If the JS API version supported by the extension is unknown, it first sends a
|
* If the JS API version supported by the extension is unknown, it first sends a
|
||||||
* message to the extension to find out the supported API version and then it sends
|
* message to the extension to find out the supported API version and then it sends
|
||||||
@@ -623,7 +625,7 @@ u2f.responseHandler_ = function(message) {
|
|||||||
* @param {function((u2f.Error|u2f.SignResponse))} callback
|
* @param {function((u2f.Error|u2f.SignResponse))} callback
|
||||||
* @param {number=} opt_timeoutSeconds
|
* @param {number=} opt_timeoutSeconds
|
||||||
*/
|
*/
|
||||||
u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
|
u2f.sign = function (appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
|
||||||
if (js_api_version === undefined) {
|
if (js_api_version === undefined) {
|
||||||
// Send a message to get the extension to JS API version, then send the actual sign request.
|
// Send a message to get the extension to JS API version, then send the actual sign request.
|
||||||
u2f.getApiVersion(
|
u2f.getApiVersion(
|
||||||
@@ -636,9 +638,9 @@ u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSecon
|
|||||||
// We know the JS API version. Send the actual sign request in the supported API version.
|
// We know the JS API version. Send the actual sign request in the supported API version.
|
||||||
u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
|
u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches an array of sign requests to available U2F tokens.
|
* Dispatches an array of sign requests to available U2F tokens.
|
||||||
* @param {string=} appId
|
* @param {string=} appId
|
||||||
* @param {string=} challenge
|
* @param {string=} challenge
|
||||||
@@ -646,8 +648,8 @@ u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSecon
|
|||||||
* @param {function((u2f.Error|u2f.SignResponse))} callback
|
* @param {function((u2f.Error|u2f.SignResponse))} callback
|
||||||
* @param {number=} opt_timeoutSeconds
|
* @param {number=} opt_timeoutSeconds
|
||||||
*/
|
*/
|
||||||
u2f.sendSignRequest = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
|
u2f.sendSignRequest = function (appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
|
||||||
u2f.getPortSingleton_(function(port) {
|
u2f.getPortSingleton_(function (port) {
|
||||||
var reqId = ++u2f.reqCounter_;
|
var reqId = ++u2f.reqCounter_;
|
||||||
u2f.callbackMap_[reqId] = callback;
|
u2f.callbackMap_[reqId] = callback;
|
||||||
var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
|
var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
|
||||||
@@ -655,9 +657,9 @@ u2f.sendSignRequest = function(appId, challenge, registeredKeys, callback, opt_t
|
|||||||
var req = u2f.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId);
|
var req = u2f.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId);
|
||||||
port.postMessage(req);
|
port.postMessage(req);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches register requests to available U2F tokens. An array of sign
|
* Dispatches register requests to available U2F tokens. An array of sign
|
||||||
* requests identifies already registered tokens.
|
* requests identifies already registered tokens.
|
||||||
* If the JS API version supported by the extension is unknown, it first sends a
|
* If the JS API version supported by the extension is unknown, it first sends a
|
||||||
@@ -669,12 +671,12 @@ u2f.sendSignRequest = function(appId, challenge, registeredKeys, callback, opt_t
|
|||||||
* @param {function((u2f.Error|u2f.RegisterResponse))} callback
|
* @param {function((u2f.Error|u2f.RegisterResponse))} callback
|
||||||
* @param {number=} opt_timeoutSeconds
|
* @param {number=} opt_timeoutSeconds
|
||||||
*/
|
*/
|
||||||
u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
|
u2f.register = function (appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
|
||||||
if (js_api_version === undefined) {
|
if (js_api_version === undefined) {
|
||||||
// Send a message to get the extension to JS API version, then send the actual register request.
|
// Send a message to get the extension to JS API version, then send the actual register request.
|
||||||
u2f.getApiVersion(
|
u2f.getApiVersion(
|
||||||
function (response) {
|
function (response) {
|
||||||
js_api_version = response['js_api_version'] === undefined ? 0: response['js_api_version'];
|
js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version'];
|
||||||
console.log("Extension JS API Version: ", js_api_version);
|
console.log("Extension JS API Version: ", js_api_version);
|
||||||
u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
|
u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
|
||||||
callback, opt_timeoutSeconds);
|
callback, opt_timeoutSeconds);
|
||||||
@@ -684,9 +686,9 @@ u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_t
|
|||||||
u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
|
u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
|
||||||
callback, opt_timeoutSeconds);
|
callback, opt_timeoutSeconds);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches register requests to available U2F tokens. An array of sign
|
* Dispatches register requests to available U2F tokens. An array of sign
|
||||||
* requests identifies already registered tokens.
|
* requests identifies already registered tokens.
|
||||||
* @param {string=} appId
|
* @param {string=} appId
|
||||||
@@ -695,8 +697,8 @@ u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_t
|
|||||||
* @param {function((u2f.Error|u2f.RegisterResponse))} callback
|
* @param {function((u2f.Error|u2f.RegisterResponse))} callback
|
||||||
* @param {number=} opt_timeoutSeconds
|
* @param {number=} opt_timeoutSeconds
|
||||||
*/
|
*/
|
||||||
u2f.sendRegisterRequest = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
|
u2f.sendRegisterRequest = function (appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
|
||||||
u2f.getPortSingleton_(function(port) {
|
u2f.getPortSingleton_(function (port) {
|
||||||
var reqId = ++u2f.reqCounter_;
|
var reqId = ++u2f.reqCounter_;
|
||||||
u2f.callbackMap_[reqId] = callback;
|
u2f.callbackMap_[reqId] = callback;
|
||||||
var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
|
var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
|
||||||
@@ -705,10 +707,10 @@ u2f.sendRegisterRequest = function(appId, registerRequests, registeredKeys, call
|
|||||||
appId, registeredKeys, registerRequests, timeoutSeconds, reqId);
|
appId, registeredKeys, registerRequests, timeoutSeconds, reqId);
|
||||||
port.postMessage(req);
|
port.postMessage(req);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches a message to the extension to find out the supported
|
* Dispatches a message to the extension to find out the supported
|
||||||
* JS API version.
|
* JS API version.
|
||||||
* If the user is on a mobile phone and is thus using Google Authenticator instead
|
* If the user is on a mobile phone and is thus using Google Authenticator instead
|
||||||
@@ -716,8 +718,8 @@ u2f.sendRegisterRequest = function(appId, registerRequests, registeredKeys, call
|
|||||||
* @param {function((u2f.Error|u2f.GetJsApiVersionResponse))} callback
|
* @param {function((u2f.Error|u2f.GetJsApiVersionResponse))} callback
|
||||||
* @param {number=} opt_timeoutSeconds
|
* @param {number=} opt_timeoutSeconds
|
||||||
*/
|
*/
|
||||||
u2f.getApiVersion = function(callback, opt_timeoutSeconds) {
|
u2f.getApiVersion = function (callback, opt_timeoutSeconds) {
|
||||||
u2f.getPortSingleton_(function(port) {
|
u2f.getPortSingleton_(function (port) {
|
||||||
// If we are using Android Google Authenticator or iOS client app,
|
// If we are using Android Google Authenticator or iOS client app,
|
||||||
// do not fire an intent to ask which JS API version to use.
|
// do not fire an intent to ask which JS API version to use.
|
||||||
if (port.getPortType) {
|
if (port.getPortType) {
|
||||||
@@ -732,7 +734,7 @@ u2f.getApiVersion = function(callback, opt_timeoutSeconds) {
|
|||||||
apiVersion = 0;
|
apiVersion = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
callback({ 'js_api_version': apiVersion });
|
callback({'js_api_version': apiVersion});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var reqId = ++u2f.reqCounter_;
|
var reqId = ++u2f.reqCounter_;
|
||||||
@@ -745,4 +747,14 @@ u2f.getApiVersion = function(callback, opt_timeoutSeconds) {
|
|||||||
};
|
};
|
||||||
port.postMessage(req);
|
port.postMessage(req);
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return u2f;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
var u2f = build_u2f_object();
|
||||||
|
} catch (TypeError) {
|
||||||
|
// Firefox 57 sets the u2f object itself, we are not allowed to override it,
|
||||||
|
// so fail silently.
|
||||||
|
}
|
||||||
@@ -16,8 +16,8 @@ libsass
|
|||||||
django-otp==0.3.*
|
django-otp==0.3.*
|
||||||
python-u2flib-server==4.*
|
python-u2flib-server==4.*
|
||||||
django-formtools==2.0
|
django-formtools==2.0
|
||||||
celery==4.0.2
|
celery==4.1.*
|
||||||
kombu==4.0.2
|
kombu==4.1.*
|
||||||
django-statici18n==1.3.*
|
django-statici18n==1.3.*
|
||||||
inlinestyler==0.2.*
|
inlinestyler==0.2.*
|
||||||
BeautifulSoup4
|
BeautifulSoup4
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ setup(
|
|||||||
'django-otp==0.3.*',
|
'django-otp==0.3.*',
|
||||||
'python-u2flib-server==4.*',
|
'python-u2flib-server==4.*',
|
||||||
'django-formtools==2.0',
|
'django-formtools==2.0',
|
||||||
'celery==4.0.2',
|
'celery==4.1.*',
|
||||||
'kombu==4.0.2',
|
'kombu==4.1.*',
|
||||||
'django-statici18n==1.3.*',
|
'django-statici18n==1.3.*',
|
||||||
'inlinestyler==0.2.*',
|
'inlinestyler==0.2.*',
|
||||||
'BeautifulSoup4',
|
'BeautifulSoup4',
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ from django.test import TestCase
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
from pretix.base.models import (
|
from pretix.base.models import (
|
||||||
CachedFile, CartPosition, Event, Item, ItemCategory, ItemVariation, Order,
|
CachedFile, CartPosition, CheckinList, Event, Item, ItemCategory,
|
||||||
OrderPosition, Organizer, Question, Quota, User, Voucher, WaitingListEntry,
|
ItemVariation, Order, OrderPosition, Organizer, Question, Quota, User,
|
||||||
|
Voucher, WaitingListEntry,
|
||||||
)
|
)
|
||||||
from pretix.base.models.event import SubEvent
|
from pretix.base.models.event import SubEvent
|
||||||
from pretix.base.models.items import SubEventItem, SubEventItemVariation
|
from pretix.base.models.items import SubEventItem, SubEventItemVariation
|
||||||
@@ -1066,3 +1067,66 @@ class CachedFileTestCase(TestCase):
|
|||||||
assert f.read().strip() == "file_content"
|
assert f.read().strip() == "file_content"
|
||||||
cf.delete()
|
cf.delete()
|
||||||
assert not default_storage.exists(cf.file.name)
|
assert not default_storage.exists(cf.file.name)
|
||||||
|
|
||||||
|
|
||||||
|
class CheckinListTestCase(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.organizer = Organizer.objects.create(name='Dummy', slug='dummy')
|
||||||
|
cls.event = Event.objects.create(
|
||||||
|
organizer=cls.organizer, name='Dummy', slug='dummy',
|
||||||
|
date_from=now(), date_to=now() - timedelta(hours=1),
|
||||||
|
)
|
||||||
|
cls.item1 = cls.event.items.create(name="Ticket", default_price=12)
|
||||||
|
cls.item2 = cls.event.items.create(name="Shirt", default_price=6)
|
||||||
|
cls.cl_all = cls.event.checkin_lists.create(
|
||||||
|
name='All', all_products=True
|
||||||
|
)
|
||||||
|
cls.cl_both = cls.event.checkin_lists.create(
|
||||||
|
name='Both', all_products=False
|
||||||
|
)
|
||||||
|
cls.cl_both.limit_products.add(cls.item1)
|
||||||
|
cls.cl_both.limit_products.add(cls.item2)
|
||||||
|
cls.cl_tickets = cls.event.checkin_lists.create(
|
||||||
|
name='Tickets', all_products=False
|
||||||
|
)
|
||||||
|
cls.cl_tickets.limit_products.add(cls.item1)
|
||||||
|
o = Order.objects.create(
|
||||||
|
code='FOO', event=cls.event, email='dummy@dummy.test',
|
||||||
|
status=Order.STATUS_PAID,
|
||||||
|
datetime=now(), expires=now() + timedelta(days=10),
|
||||||
|
total=Decimal("30"), payment_provider='banktransfer', locale='en'
|
||||||
|
)
|
||||||
|
OrderPosition.objects.create(
|
||||||
|
order=o,
|
||||||
|
item=cls.item1,
|
||||||
|
variation=None,
|
||||||
|
price=Decimal("12"),
|
||||||
|
)
|
||||||
|
op2 = OrderPosition.objects.create(
|
||||||
|
order=o,
|
||||||
|
item=cls.item1,
|
||||||
|
variation=None,
|
||||||
|
price=Decimal("12"),
|
||||||
|
)
|
||||||
|
op3 = OrderPosition.objects.create(
|
||||||
|
order=o,
|
||||||
|
item=cls.item2,
|
||||||
|
variation=None,
|
||||||
|
price=Decimal("6"),
|
||||||
|
)
|
||||||
|
op2.checkins.create(list=cls.cl_tickets)
|
||||||
|
op3.checkins.create(list=cls.cl_both)
|
||||||
|
|
||||||
|
def test_annotated(self):
|
||||||
|
lists = list(CheckinList.annotate_with_numbers(self.event.checkin_lists.order_by('name'), self.event))
|
||||||
|
assert lists == [self.cl_all, self.cl_both, self.cl_tickets]
|
||||||
|
assert lists[0].checkin_count == 0
|
||||||
|
assert lists[0].position_count == 3
|
||||||
|
assert lists[0].percent == 0
|
||||||
|
assert lists[1].checkin_count == 1
|
||||||
|
assert lists[1].position_count == 3
|
||||||
|
assert lists[1].percent == 33
|
||||||
|
assert lists[2].checkin_count == 1
|
||||||
|
assert lists[2].position_count == 2
|
||||||
|
assert lists[2].percent == 50
|
||||||
|
|||||||
@@ -73,6 +73,33 @@ class CheckoutTestCase(TestCase):
|
|||||||
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
self.assertRedirects(response, '/%s/%s/' % (self.orga.slug, self.event.slug),
|
||||||
target_status_code=200)
|
target_status_code=200)
|
||||||
|
|
||||||
|
def test_addon_questions(self):
|
||||||
|
q1 = Question.objects.create(
|
||||||
|
event=self.event, question='Age', type=Question.TYPE_NUMBER,
|
||||||
|
required=True
|
||||||
|
)
|
||||||
|
q1.items.add(self.ticket)
|
||||||
|
q1.items.add(self.workshop1)
|
||||||
|
ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1,
|
||||||
|
price_included=True)
|
||||||
|
cp1 = CartPosition.objects.create(
|
||||||
|
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||||
|
price=23, expires=now() + timedelta(minutes=10)
|
||||||
|
)
|
||||||
|
cp1.answers.create(question=q1, answer='12')
|
||||||
|
cp2 = CartPosition.objects.create(
|
||||||
|
event=self.event, cart_id=self.session_key, item=self.workshop1, addon_to=cp1,
|
||||||
|
price=0, expires=now() + timedelta(minutes=10)
|
||||||
|
)
|
||||||
|
cp2.answers.create(question=q1, answer='12')
|
||||||
|
|
||||||
|
self._set_session('payment', 'banktransfer')
|
||||||
|
response = self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
|
doc = BeautifulSoup(response.rendered_content, "lxml")
|
||||||
|
self.assertEqual(len(doc.select(".thank-you")), 1)
|
||||||
|
self.assertEqual(OrderPosition.objects.filter(item=self.ticket).first().answers.first().answer, '12')
|
||||||
|
self.assertEqual(OrderPosition.objects.filter(item=self.workshop1).first().answers.first().answer, '12')
|
||||||
|
|
||||||
def test_questions(self):
|
def test_questions(self):
|
||||||
q1 = Question.objects.create(
|
q1 = Question.objects.create(
|
||||||
event=self.event, question='Age', type=Question.TYPE_NUMBER,
|
event=self.event, question='Age', type=Question.TYPE_NUMBER,
|
||||||
|
|||||||
Reference in New Issue
Block a user