General housekeeping such as adding package.json for Node server, bash script for launching a celery worker and updating the readme to assist with launching the DEMOS2 app. Updated some models to include UI helper functions. Main work done is around event preparation - with a Celery worker running and the Node server, trustees are now emailed a link to prepare events. The event detail page has also had a bit of an overhaul to include additional information and to make it easier to use

This commit is contained in:
vince0656 2018-07-02 10:06:05 +01:00
parent de9eaa7881
commit f82a380fa4
21 changed files with 337 additions and 696 deletions

4
.gitignore vendored
View File

@ -1,9 +1,11 @@
*.pyc
*.sqlite
*.sqlite3
package-lock.json
node_modules/
__pycache__
migrations/
build
/venv*/
/.idea/
/.idea/

529
Node/package-lock.json generated
View File

@ -1,529 +0,0 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"accepts": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz",
"integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=",
"requires": {
"mime-types": "2.1.17",
"negotiator": "0.6.1"
}
},
"append-field": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-0.1.0.tgz",
"integrity": "sha1-bdxY+gg8e8VF08WZWygwzCNm1Eo="
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"body-parser": {
"version": "1.18.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
"integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
"requires": {
"bytes": "3.0.0",
"content-type": "1.0.4",
"debug": "2.6.9",
"depd": "1.1.1",
"http-errors": "1.6.2",
"iconv-lite": "0.4.19",
"on-finished": "2.3.0",
"qs": "6.5.1",
"raw-body": "2.3.2",
"type-is": "1.6.15"
}
},
"busboy": {
"version": "0.2.14",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
"requires": {
"dicer": "0.2.5",
"readable-stream": "1.1.14"
}
},
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
},
"concat-stream": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
"integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
"requires": {
"inherits": "2.0.3",
"readable-stream": "2.3.3",
"typedarray": "0.0.6"
},
"dependencies": {
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"readable-stream": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"safe-buffer": "5.1.1",
"string_decoder": "1.0.3",
"util-deprecate": "1.0.2"
}
},
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"requires": {
"safe-buffer": "5.1.1"
}
}
}
},
"content-disposition": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"cookie-parser": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz",
"integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=",
"requires": {
"cookie": "0.3.1",
"cookie-signature": "1.0.6"
}
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"cors": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz",
"integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=",
"requires": {
"object-assign": "4.1.1",
"vary": "1.1.2"
},
"dependencies": {
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
}
}
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
"integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"dicer": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
"requires": {
"readable-stream": "1.1.14",
"streamsearch": "0.1.2"
}
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz",
"integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.16.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz",
"integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=",
"requires": {
"accepts": "1.3.4",
"array-flatten": "1.1.1",
"body-parser": "1.18.2",
"content-disposition": "0.5.2",
"content-type": "1.0.4",
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "1.1.1",
"encodeurl": "1.0.1",
"escape-html": "1.0.3",
"etag": "1.8.1",
"finalhandler": "1.1.0",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "1.1.2",
"on-finished": "2.3.0",
"parseurl": "1.3.2",
"path-to-regexp": "0.1.7",
"proxy-addr": "2.0.2",
"qs": "6.5.1",
"range-parser": "1.2.0",
"safe-buffer": "5.1.1",
"send": "0.16.1",
"serve-static": "1.13.1",
"setprototypeof": "1.1.0",
"statuses": "1.3.1",
"type-is": "1.6.15",
"utils-merge": "1.0.1",
"vary": "1.1.2"
}
},
"finalhandler": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
"integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
"requires": {
"debug": "2.6.9",
"encodeurl": "1.0.1",
"escape-html": "1.0.3",
"on-finished": "2.3.0",
"parseurl": "1.3.2",
"statuses": "1.3.1",
"unpipe": "1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"http-errors": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
"integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
"requires": {
"depd": "1.1.1",
"inherits": "2.0.3",
"setprototypeof": "1.0.3",
"statuses": "1.3.1"
},
"dependencies": {
"setprototypeof": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
"integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
}
}
},
"iconv-lite": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz",
"integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A="
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"milagro-crypto-js": {
"version": "file:milagro-crypto-js"
},
"mime": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
},
"mime-db": {
"version": "1.30.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
"integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE="
},
"mime-types": {
"version": "2.1.17",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
"integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
"requires": {
"mime-db": "1.30.0"
}
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"multer": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/multer/-/multer-1.3.0.tgz",
"integrity": "sha1-CSsmcPaEb6SRSWXvyM+Uwg/sbNI=",
"requires": {
"append-field": "0.1.0",
"busboy": "0.2.14",
"concat-stream": "1.6.0",
"mkdirp": "0.5.1",
"object-assign": "3.0.0",
"on-finished": "2.3.0",
"type-is": "1.6.15",
"xtend": "4.0.1"
}
},
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
},
"object-assign": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
"integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseurl": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
"integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"process-nextick-args": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
},
"proxy-addr": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz",
"integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=",
"requires": {
"forwarded": "0.1.2",
"ipaddr.js": "1.5.2"
}
},
"qs": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
},
"range-parser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
},
"raw-body": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
"integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
"requires": {
"bytes": "3.0.0",
"http-errors": "1.6.2",
"iconv-lite": "0.4.19",
"unpipe": "1.0.0"
}
},
"readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "0.0.1",
"string_decoder": "0.10.31"
}
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"send": {
"version": "0.16.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz",
"integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==",
"requires": {
"debug": "2.6.9",
"depd": "1.1.1",
"destroy": "1.0.4",
"encodeurl": "1.0.1",
"escape-html": "1.0.3",
"etag": "1.8.1",
"fresh": "0.5.2",
"http-errors": "1.6.2",
"mime": "1.4.1",
"ms": "2.0.0",
"on-finished": "2.3.0",
"range-parser": "1.2.0",
"statuses": "1.3.1"
}
},
"serve-static": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz",
"integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==",
"requires": {
"encodeurl": "1.0.1",
"escape-html": "1.0.3",
"parseurl": "1.3.2",
"send": "0.16.1"
}
},
"setprototypeof": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
},
"statuses": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
"integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
},
"streamsearch": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
},
"type-is": {
"version": "1.6.15",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz",
"integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=",
"requires": {
"media-typer": "0.3.0",
"mime-types": "2.1.17"
}
},
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
}
}
}

23
Node/package.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "Node DEMOS2 Cryptography Server",
"version": "1.0.0",
"description": "Exposes Cryptographic Functionality from Various Endpoints",
"main": "index.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/vincentmdealmeida/DEMOS2"
},
"keywords": [],
"author": "Bingsheng Zang, Thomas Smith",
"license": "ISC",
"dependencies": {
"express": "^4.16.3",
"milagro-crypto-js": "git+https://github.com/milagro-crypto/milagro-crypto-js.git"
}
}

View File

@ -1,10 +1,10 @@
# DEMOS2
Prototype Django based e-voting application, to demonstrate DEMOS2's client-side encryption e-voting.
Prototype Django-based e-voting application, to demonstrate DEMOS2's client-side encryption e-voting.
The previous repository for DEMOS2 by Carey Williams can be found at: https://github.com/CareyJWilliams/DEMOS2
### Dependencies
### Main Application Dependencies
Python: Version 2.7 (Anything higher than this will not currently work)
Python packages: Specified in 'requirements.txt' - PyCharm will detect these dependencies and offer installation
@ -17,9 +17,23 @@ Finally, with all the above dependencies in place, you can simply issue the foll
python manage.py migrate
'aullauthdemo/settings.py' specifies the Google reCAPTCHA site key and private key which will need changing when deployed
onto a new domain.
onto a new domain. There is also a DOMAIN setting within the file that needs updating during deployment as things
like email functionality depend on this setting for correct URL generation during event preparation etc.
### Running the server and creating a new user account
Emails from the application are currently sent from the following email account which can be updated within the settings:
demos2.no.reply@gmail.com
### NodeJS Dependencies
The Node.js crypto server depends on the milagro-crypto-js and express modules. A package.json file can be found in the
Node/ directory with these dependency requirements and therefore from this folder you can run:
npm install
Once the dependencies have been installed, you can then run the node server as per the below instructions.
### Step 1: Running the Python app and creating a new user account
You can run the server with the following command:
@ -33,8 +47,16 @@ You can then click on 'Join' to create a new user account. Currently, a server e
email account saying something like 'Too Many Attempts'. Rest assured that the account will have been created. Navigate
back to the home page and you should be able to log in. This will hopefully be fixed in a future version.
### Other
### Step 2: Running Celery
This was included in the previous readme and may be required:
Celery is used to run tasks asynchronously and the DEMOS2 application can't run without this application. A bash script
called 'start_celery_worker.sh' is provided to make starting a worker as easy as possible:
The Node.js encryption server depends on the milagro-crypto-js library. Download the source and follow the instructions for installation: https://github.com/milagro-crypto/milagro-crypto-js. To install, place the package's files (`package.json` level) into the directory Node/milagro-crypto-js (a new folder) then run `npm install` in the Node folder. This should install dependencies including the local package you just added.
./start_celery_worker.sh
### Step 3: Running the NodeJS Server
The NodeJS server exposes a lot of cryptographic operations that the application depends on throughout. To run the
server, issue the following command line request from the Node/ folder:
node index.js

View File

@ -4,18 +4,21 @@ import subprocess
import json
import urllib2
#change this file name etc., temporary change to get it working for the meantime
'''
All functions in this file have been re-implemenented by Thomas Smith
File then updated by Vincent de Almeida. Changes include:
-Update filename to 'crypto_rpc' to reflect the RPC nature of the methods
'''
def param():
jsondict = json.load(urllib2.urlopen('http://localhost:8080/param'))
url = 'http://localhost:8080/param' # RPC URL
jsondict = json.load(urllib2.urlopen(url))
return json.dumps(jsondict)
def combpk(amount, pks):
url = 'http://localhost:8080/cmpkstring'
url = 'http://localhost:8080/cmpkstring' # RPC URL
querystring = '?number='+str(amount)
for pk in pks:
querystring += '&PK='+pk
@ -26,7 +29,7 @@ def combpk(amount, pks):
return json.dumps(jsondict)
def addec(amount, ciphers):
url = 'http://localhost:8080/addec'
url = 'http://localhost:8080/addec' # RPC URL
querystring = '?number='+str(amount)
c1s = ciphers['c1s']
c2s = ciphers['c2s']
@ -40,7 +43,7 @@ def addec(amount, ciphers):
return json.dumps(jsondict)
def tally(amount, param, decs, cipher):
url = 'http://localhost:8080/tally'
url = 'http://localhost:8080/tally' # RPC URL
querystring = '?number='+str(amount)
querystring += '&param='+urllib2.quote(str(param))

View File

@ -1,7 +1,8 @@
from __future__ import unicode_literals
from datetime import datetime
import json
from django.core.mail import send_mail
from django.db import models
from django.utils import timezone
@ -9,9 +10,17 @@ from allauthdemo.auth.models import DemoUser
class EmailUser(models.Model):
email = models.CharField(max_length=80, unique=True)
def send_email(self, subject, message, from_email=None):
"""
Sends an email to this User.
"""
send_mail(subject, message, from_email, [self.email])
def __unicode__(self):
return self.email
class Event(models.Model):
users_organisers = models.ManyToManyField(DemoUser, blank=True, related_name="organisers")
users_trustees = models.ManyToManyField(EmailUser, blank=True, related_name="trustees")
@ -26,11 +35,32 @@ class Event(models.Model):
c_email = models.CharField(max_length=512, blank=True)
trustees = models.CharField(max_length=4096)
def EID_hr(self):
EID_json = json.loads(self.EID)
return EID_json['hr']
def EID_crypto(self):
EID_json = json.loads(self.EID)
EID_crypto_str = EID_json['crypto']
return json.loads(EID_crypto_str)
def duration(self):
duration_str = self.start_time.strftime("%d-%m-%y %H:%M")
duration_str = duration_str + " - " + self.end_time.strftime("%d-%m-%y %H:%M %Z")
duration_str = self.start_time_formatted()
duration_str = duration_str + " - " + self.end_time_formatted_utc()
return duration_str
def start_time_formatted(self):
return self.start_time.strftime("%d-%m-%y %H:%M")
def start_time_formatted_utc(self):
return self.start_time.strftime("%d-%m-%y %H:%M %Z")
def end_time_formatted(self):
return self.end_time.strftime("%d-%m-%y %H:%M")
def end_time_formatted_utc(self):
return self.end_time.strftime("%d-%m-%y %H:%M %Z")
def status(self):
status_str = ""
@ -113,14 +143,4 @@ class Organiser(models.Model):
email = models.CharField(max_length=100, blank=False, null=False)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
'''
class Organiser(models.Model):
user = models.ForeignKey(DemoUser, on_delete=models.CASCADE)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
class Trustee(models.Model):
user = models.ForeignKey(DemoUser, on_delete=models.CASCADE)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
'''
#class EventOrganisers():
#event = models.ForeignKey(Event, on_delete=models.CASCADE)

View File

@ -1,14 +1,29 @@
from __future__ import absolute_import
import csv
from os import urandom
import base64
from io import StringIO
import json
from os import urandom
from celery import task
from django.core.exceptions import ValidationError
from django.core.validators import EmailValidator
from django.core.mail import send_mail
from allauthdemo.polls.models import Ballot, Event, EmailUser, AccessKey
from .cpp_calls import param, combpk, addec, tally
from django.conf import settings
from allauthdemo.polls.models import AccessKey
from .crypto_rpc import param, combpk, addec, tally
'''
Goal: This py file defines celery tasks that can be initiated
The following tasks were re-implemented by Thomas Smith: generate_event_param, tally_results, generate_combpk, generate_enc
This file was also updated by Vincent de Almeida
'''
# Will store the result of the initial cal to param() from .cpp_calls
group_param = None
def is_valid_email(email):
try:
@ -23,36 +38,59 @@ def create_ballots(poll):
for voter in poll.event.voters.all():
ballot = poll.ballots.create(voter=voter, poll=poll)
@task()
def create_voters(csvfile, event):
print("Creating voters for event " + event.title)
reader = csv.reader(csvfile, delimiter=',')
string = ""
for row in reader:
email = string.join(row)
print(email)
#testvoter = EmailUser.objects.get_or_create(email='notarealemail@live.com')[0]
#event.voters.add(testvoter)
if (is_valid_email(email)):
voter = EmailUser.objects.get_or_create(email=email)[0]
event.voters.add(voter)
key = base64.urlsafe_b64encode(urandom(16)).decode('utf-8')
AccessKey.objects.create(user=voter, event=event, key=key)
send_mail(
'Your Voting Key',
'Key: ' + key,
'from@example.com',
[string.join(row)],
fail_silently=False,
)
'''
Starting here: functions re-implemented by Thomas Smith
Will generate a key for accessing either the event preparation page or the voting page
'''
def gen_access_key():
return base64.urlsafe_b64encode(urandom(16)).decode('utf-8')
'''
Emails an event preparation URL containing an access key for all of the trustees for an event
'''
@task()
def generate_event_param(event):
event.EID = param()
def email_trustees_prep(trustees, event):
email_subject = "Key Generation and Preparation for Event '" + event.title + "'"
# Plain text email - this could be replaced for a HTML-based email in the future
email_body = "Please visit the following URL to prepare the event and generate your trustee secret key:\n\n"
url_base = "http://" + settings.DOMAIN + "/event/" + str(event.pk) + "/prepare/?key="
email_body = email_body + url_base
for trustee in trustees:
# Generate a key and create an AccessKey object
key = gen_access_key()
AccessKey.objects.create(user=trustee, event=event, key=key)
trustee.send_email(email_subject, email_body + key)
'''
Emails the access keys for all of the voters for an event
'''
@task()
def email_voters_a_key(voters, event):
email_subject = "Voting Access for Event '" + event.title + "'"
email_body = 'Key: '
for voter in voters:
# Generate a key and create an AccessKey object
key = gen_access_key()
AccessKey.objects.create(user=voter, event=event, key=key)
voter.send_email(email_subject, email_body + key)
'''
Updates the EID of an event to contain 2 event IDs: a human readable one (hr) and a crypto one (GP from param())
'''
@task()
def update_EID(event):
global group_param
if group_param is None:
group_param = param()
EID = {}
EID['hr'] = event.EID
EID['crypto'] = group_param
event.EID = json.dumps(EID)
event.save()
@task()
@ -98,20 +136,4 @@ def generate_enc(poll):
poll.enc = addec(amount, ciphers)
poll.save()
'''
End of re-implemented code
'''
@task()
def add(x, y):
return x + y
@task()
def mul(x, y):
return x * y
@task()
def xsum(numbers):
return sum(numbers)

View File

@ -1,13 +1,9 @@
import re
from datetime import datetime
from django.utils.dateparse import parse_datetime
from allauthdemo.polls.models import Event
from allauthdemo.polls.models import Poll
from allauthdemo.polls.models import PollOption
from allauthdemo.polls.models import EmailUser
from allauthdemo.polls.models import Event, Poll, PollOption, EmailUser
from allauthdemo.auth.models import DemoUser
'''
@ -382,6 +378,7 @@ class EventModelAdaptor:
errors_summary = errors_summary + str(i + 1) + " "
self.invalid_form_fields['polls_data'] = {'val': polls_json}
self.invalid_form_fields['poll_count'] = {'val': poll_count}
if not valid and len(errors_summary) > 34:
errors_summary = errors_summary + "and can be corrected by editing them."
@ -536,7 +533,7 @@ class EventModelAdaptor:
organisers_list = self.form_data.pop('organiser-email-input')
for organiser in organisers_list:
if organiser != '' and DemoUser.objects.filter(email=organiser).count() == 1:
if organiser != '' and DemoUser.objects.filter(email=organiser).exists():
self.organisers.append(DemoUser.objects.filter(email=organiser).get())
# Extract the list of trustees
@ -544,7 +541,7 @@ class EventModelAdaptor:
for trustee in trustees_list:
if trustee != '':
if EmailUser.objects.filter(email=trustee).count() == 1:
if EmailUser.objects.filter(email=trustee).exists():
self.trustees.append(EmailUser.objects.filter(email=trustee).get())
else:
self.trustees.append(EmailUser(email=trustee))
@ -555,7 +552,7 @@ class EventModelAdaptor:
for voter_email in voters_email_list:
if voter_email != '':
if EmailUser.objects.filter(email=voter_email).count() == 1:
if EmailUser.objects.filter(email=voter_email).exists():
self.voters.append(EmailUser.objects.filter(email=voter_email).get())
else:
self.voters.append(EmailUser(email=voter_email))
@ -634,14 +631,15 @@ class EventModelAdaptor:
# Add the list of trustees to the event, making sure they're instantiated
for trustee in self.trustees:
if EmailUser.objects.filter(email=trustee.email).count() == 0:
if not EmailUser.objects.filter(email=trustee.email).exists():
trustee.save()
self.event.users_trustees = self.trustees
# Add the list of voters to the event, making sure they're instantiated
# Additionally, generating the AccessKey for voters
for voter in self.voters:
if EmailUser.objects.filter(email=voter.email).count() == 0:
if not EmailUser.objects.filter(email=voter.email).exists():
voter.save()
self.event.voters = self.voters
@ -655,10 +653,10 @@ class EventModelAdaptor:
self.event.save()
# Finally perform a data clean up
self.__clear_data()
# Finally return a reference to the event
return self.event
def __clear_data(self):
def clear_data(self):
self.form_data = None
self.form_data_validation = None
self.invalid_form_fields = {}

View File

@ -17,8 +17,7 @@ from .forms import EventForm, PollForm, OptionFormset, QuestionFormset, Organise
from .models import Event, Poll, PollOption, EmailUser, Ballot, TrusteeKey, Decryption
from allauthdemo.auth.models import DemoUser
from .tasks import create_voters, create_ballots, generate_event_param, generate_combpk, generate_enc, tally_results
from .cpp_calls import param, addec, combpk, tally
from .tasks import email_trustees_prep, update_EID, generate_combpk, generate_enc, tally_results
from .utils.EventModelAdaptor import EventModelAdaptor
@ -38,6 +37,7 @@ class EventDetailView(generic.DetailView):
def get_context_data(self, **kwargs):
context = super(EventDetailView, self).get_context_data(**kwargs)
context['is_organiser'] = ((not self.request.user.is_anonymous()) and (self.object.users_organisers.filter(email=self.request.user.email).exists()))
#context['now'] = timezone.now()
return context
@ -153,28 +153,42 @@ def view_poll(request, event_id, poll_num):
})
def event_trustee_setup(request, event_id):
# Obtain the event and the event preparation access key that's been supplied
event = get_object_or_404(Event, pk=event_id)
access_key = request.GET.get('key', None)
if (access_key):
# If the a_key is present, check it's valid and related to a trustee EmailUser instance for this event
if access_key:
email_key = event.keys.filter(key=access_key)
if (email_key.exists() and event.users_trustees.filter(email=email_key[0].user.email).exists()):
if (TrusteeKey.objects.filter(event=event, user=email_key[0].user).exists()):
if email_key.exists() and event.users_trustees.filter(email=email_key[0].user.email).exists():
if TrusteeKey.objects.filter(event=event, user=email_key[0].user).exists():
messages.add_message(request, messages.WARNING, 'You have already submitted your key for this event')
return HttpResponseRedirect(reverse("user_home"))
if (request.method == "POST"):
if request.method == "POST":
form = EventSetupForm(request.POST)
if (form.is_valid()):
# If form data is valid, create a TrusteeKey object with the supplied public key
if form.is_valid():
public_key = request.POST["public_key"]
key = TrusteeKey.objects.get_or_create(event=event, user=email_key[0].user)[0]
key.key = public_key
key.save()
if (event.trustee_keys.count() == event.users_trustees.count()): # ready for combpk
# When all trustees have supplied their public key, we can combine them to create a master key
# The event will now be ready to receive votes on the various polls that have been defined -
# voters therefore need to be informed
if event.trustee_keys.count() == event.users_trustees.count():
generate_combpk.delay(event)
messages.add_message(request, messages.SUCCESS, 'You have successfully submitted your public key for this event')
# TODO: Create Celery task that generates voting URLs for voters as well as creates the ballots
success_msg = 'You have successfully submitted your public key for this event'
messages.add_message(request, messages.SUCCESS, success_msg)
# This re-direct may not be appropriate for trustees that don't have logins
return HttpResponseRedirect(reverse("user_home"))
else:
form = EventSetupForm()
return render(request, "polls/event_setup.html", {"event": event, "form": form })
return render(request, "polls/event_setup.html", {"event": event, "form": form})
#if no key or is invalid?
messages.add_message(request, messages.WARNING, 'You do not have permission to access: ' + request.path)
@ -247,11 +261,11 @@ def manage_questions(request, event_id):
poll.save()
formset = OptionFormset(request.POST, prefix="formset_organiser", instance=poll)
if formset.is_valid():
for form in formset:
formset.save()
create_ballots.delay(poll)
messages.add_message(request, messages.SUCCESS, 'Poll created successfully')
return HttpResponseRedirect(reverse('polls:view-poll', kwargs={'event_id': poll.event_id, 'poll_num': event.polls.count() }))
formset.save()
#create_ballots.delay(poll)
messages.add_message(request, messages.SUCCESS, 'Poll created successfully')
return HttpResponseRedirect(reverse('polls:event-polls', args=[poll.event_id]))
return render(request, "polls/create_poll.html", {"event": event, "question_form": form, "option_formset": formset})
elif request.method == "GET":
@ -296,17 +310,28 @@ def create_event(request):
'''Process form data based on above results'''
if result['success']:
if form_data_valid:
# Create the new event using the form data
adaptor.extractData()
adaptor.updateModel()
new_event = adaptor.updateModel()
# Update the EID to include the GP in its EID
update_EID.delay(new_event)
# Send an email to all trustees for event preparation
trustees = new_event.users_trustees.all()
email_trustees_prep.delay(trustees, new_event)
adaptor.clear_data()
return HttpResponseRedirect(reverse('polls:index'))
else:
invalid_fields = adaptor.getInvalidFormFields()
adaptor.clear_data()
return render_invalid(request, events, demo_users, invalid_fields)
else:
invalid_fields = adaptor.getInvalidFormFields()
invalid_fields['recaptcha'] = {'error': 'The reCAPTCHA server validation failed, please try again.'}
adaptor.clear_data()
return render_invalid(request, events, demo_users, invalid_fields)
elif request.method == "GET":

View File

@ -26,6 +26,9 @@ TEMPLATE_DEBUG = True
ALLOWED_HOSTS = ['web.server.com']
# Domain the application is deployed on (Needs changing for production)
# This must not include the protocol nor any trailing slashes as application code should just add this in
DOMAIN = "127.0.0.1:8000"
# Application definition
@ -163,8 +166,8 @@ MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
#EMAIL_PORT = 1025
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'username@gmail.com'
EMAIL_HOST_PASSWORD = 'password'
EMAIL_HOST_USER = 'demos2.no.reply@gmail.com'
EMAIL_HOST_PASSWORD = 'Demos2LancsUni'
EMAIL_PORT = 587
EMAIL_USE_TLS = True

View File

@ -150,7 +150,7 @@
//new function
demosEncrypt.generateKeys = function() {
parameter = $("#event-param").val();
var tempParams = JSON.parse(parameter);
var tempParams = JSON.parse(JSON.parse(parameter).crypto);
//the full objects need to be initalised as per the library, then copy the values we need into it
//I follow Bingsheng's code as to what objects are used in the parameter object
var ctx = new CTX("BN254CX"); //new context we can use

View File

@ -26,11 +26,13 @@
}
{% endfor %}
];
{% if invalid_fields.poll_count %}pollCount = {{ invalid_fields.poll_count.val }};{% endif %}
</script>
<!-- The following UI was ported from the Election Authority UI in DEMOS1 by Vincent de Almeida -->
<!-- The DEMOS1 repository can be found at: https://github.com/mlevogiannis/demos-voting -->
<!-- TODO: look into i18n translations as this was a feature implemented in DEMOS1 -->
<!-- TODO: look into i18n translations as this was a feature implemented in DEMOS1 to enable automatic translations -->
<div class="container">
<div class="page-header">
<h2>Create New Event with Polls</h2>
@ -419,7 +421,7 @@
</div>
</div>
<!-- { Hidden field: Number of Polls } -->
<input type="number" id="poll-count-input" name="poll-count-input" class="hidden">
<input type="number" id="poll-count-input" name="poll-count-input" class="hidden" {% if invalid_fields.poll_count %}value="{{ invalid_fields.poll_count.val }}"{% endif %}>
<!-- Organisers -->
<div class="form-group">
<label for="organisers-input" class="col-sm-3 col-md-2 control-label">Organisers:</label> <!-- This text can be a template variable -->

View File

@ -5,35 +5,50 @@
{% block content %}
<div class="container">
<a href="{% url 'polls:index' %}"><< Back to Events List</a>
<br/>
<h1>Event: {{object.title}}</h1>
<p>By {{object.users_organisers.all}}</p>
<ul class="nav nav-tabs">
<li class="{% block event_nav_details %}{% endblock %}">
<a href="{% url 'polls:view-event' event.id %}">Details</a>
</li>
<li class="{% block event_nav_polls %}{% endblock %}">
<a href="{% url 'polls:event-polls' event.id %}">Polls</a>
</li>
<li class="{% block event_nav_organisers %}{% endblock %}">
<a href="{% url 'polls:event-organisers' event.id %}">Organisers</a>
</li>
{% if event.prepared == False and is_trustee %}
<li class="">
<a href="{% url 'polls:prepare-event' event.id %}">Prepare Event</a>
</li>
{% endif %}
<a href="{% url 'polls:index' %}"><< Back to Events List</a>
{% if is_organiser %}
<li class="">
<a href="{% url 'polls:edit-event' event.id %}"><span class="fa fa-pencil"></span> Edit Event</a>
</li>
<li class="{% block event_nav_launch %}{% endblock %}">
<a href="{% url 'polls:launch-event' event.id %}">Launch Event</a>
</li>
<div>
<!-- Heading -->
<div class="col-xs-7 col-sm-9 col-md-10">
<h2>Event: {{object.title}}</h2>
</div>
<!-- Edit Button -->
<div class="col-xs-5 col-sm-3 col-md-2 marginTopEditButton">
<a href="{% url 'polls:edit-event' event.id %}" class="btn btn-primary" style="float: right;">
<span class="fa fa-pencil"></span> Edit
</a>
</div>
</div>
{% else %}
<br/>
<h2>Event: {{object.title}}</h2>
{% endif %}
</ul>
{% block event_content %}{% endblock %}
<div class="overviewPadding {% if is_organiser %}marginTopOverview{% endif %}">
<hr/>
<span>By: {{object.users_organisers.all}}</span>
<br/>
<span>Ready / Prepared: {{ object.prepared }}</span>
<br/>
<span>Event Status: {{ object.status }}</span>
</div>
<br/>
<ul class="nav nav-tabs">
<li class="{% block event_nav_details %}{% endblock %}">
<a href="{% url 'polls:view-event' event.id %}"><strong>Summary</strong></a>
</li>
<li class="{% block event_nav_polls %}{% endblock %}">
<a href="{% url 'polls:event-polls' event.id %}"><strong>Polls ({{ object.polls.count }})</strong></a>
</li>
<li class="{% block event_nav_organisers %}{% endblock %}">
<a href="{% url 'polls:event-organisers' event.id %}"><strong>Entities</strong></a>
</li>
{% if is_organiser %}
<li class="{% block event_nav_launch %}{% endblock %}">
<a href="{% url 'polls:launch-event' event.id %}"><strong>Advanced</strong></a>
</li>
{% endif %}
</ul>
{% block event_content %}{% endblock %}
</div>
{% endblock %}

View File

@ -4,10 +4,17 @@
{% block event_nav_details %}active{% endblock %}
{% block event_content %}
<h2>Event Details</h2>
<span>Ready/Prepared: {{ event.prepared }}</span>
<br />
<span>Start Time: {{ event.start_time }}</span>
<br />
<span>End Time: {{ event.end_time }}</span>
<br/>
<span><strong>Start Time:</strong> {{ event.start_time_formatted_utc }}</span>
<br />
<span><strong>End Time:</strong> {{ event.end_time_formatted_utc }}</span>
<br/>
<br/>
<span><strong>Number of Polls:</strong> {{ event.polls.count }}</span>
<br/>
<span><strong>Number of Organisers:</strong> {{ event.users_organisers.count }}</span>
<br/>
<span><strong>Number of Trustees:</strong> {{ event.users_trustees.count }}</span>
<br/>
<span><strong>Number of Voters:</strong> {{ event.voters.count }}</span>
{% endblock %}

View File

@ -4,11 +4,16 @@
{% block event_nav_launch %}active{% endblock %}
{% block event_content %}
<h2>Event Details</h2>
<span>Trustee keys: {{ event.trustee_keys.count }} / {{ event.users_trustees.count }}</span>
<br/>
<span><strong>Trustee Keys Received:</strong> {{ event.trustee_keys.count }} / {{ event.users_trustees.count }}</span>
<br />
<span>EID (param): {{ event.EID }} </span>
<br/>
<span><strong>Human-readable Event ID:</strong> {{ event.EID_hr }} </span>
<br />
<span>Public key: {{ event.public_key }} </span>
<br/>
<span><strong>Group Param ID:</strong> {{ event.EID_crypto }} </span>
<br />
<br/>
<span><strong>Public key:</strong> {{ event.public_key }} </span>
{% endblock %}

View File

@ -4,7 +4,8 @@
{% block event_nav_organisers %}active{% endblock %}
{% block event_content %}
<h2>Event Organisers</h2>
<h2>Event Organisers</h2>
<hr/>
{% if object.users_organisers.all %}
<ul class="list-group">
{% for user in object.users_organisers.all %}
@ -14,7 +15,9 @@
{% else %}
<p>No organisers for this Event.</p>
{% endif %}
<h2>Event Trustees</h2>
<hr/>
<h2>Event Trustees</h2>
<hr/>
{% if object.users_trustees.all %}
<ul class="list-group">
{% for user in object.users_trustees.all %}
@ -24,7 +27,9 @@
{% else %}
<p>No trustees for this Event.</p>
{% endif %}
<h2>Voters</h2>
<hr/>
<h2>Voters</h2>
<hr/>
{% if object.voters.all %}
<ul class="list-group">
{% for voter in object.voters.all %}

View File

@ -4,10 +4,8 @@
{% block event_nav_polls %}active{% endblock %}
{% block event_content %}
<h2>Event Polls</h2>
{% if object.polls.all %}
{% for poll in object.polls.all %}
<hr/>
<h3>Poll: {{ poll.question_text }} (<a href="{% url 'polls:view-poll' event_id=event.id poll_num=forloop.counter %}">Edit</a>)</h3>
<br/>
<h4>Poll Options:</h4>
@ -17,12 +15,12 @@
{% endfor %}
</ul>
<p>Minimum Number of Option Selections: {{ poll.min_num_selections }}. Maximum Number of Option Selections: {{ poll.max_num_selections }}.</p>
<hr/>
{% endfor %}
{% else %}
<p>No polls are available for this Event.</p>
{% endif %}
{% if is_organiser %}
<hr/>
<a href="{% url 'polls:create-poll' event.id %}" class="btn btn-default" role="button">Add Poll</a>
{% endif %}
{% endblock %}

View File

@ -7,26 +7,29 @@
{% block content %}
<div class="container">
<h1>Event: {{event.title}}</h1>
<h2>Trustee Setup</h2>
<h2>Trustee Event Setup for Event '{{ event.title }}'</h2>
<hr/>
<div class="panel panel-default">
<div class="panel-heading">Public Key</div>
<div class="panel-heading"><strong>Step 1: Generate Your Secret Key</strong></div>
<div class="panel panel-body">
{% load crispy_forms_tags %}
<form method="post" action="" class="">
{% crispy form %}
<input id="public-submit" class="btn btn-default" type="submit" value="Submit" disabled="true"/>
</form>
<input id="secret-key" class="textinput textInput form-control" type="text"/>
<input id="event-param" type="text" value="{{event.EID}}" hidden/>
<div class="alert alert-warning" role="alert" style="margin-top: 0.75em;">
<strong>Warning:</strong> This key can <strong>NOT</strong> be recalculated if forgotten or lost! Ensure you back this up.
</div>
<button id="keygen-btn" onclick="demosEncrypt.generateKeys()" class="btn btn-success">Generate</button>
<a id="download-btn" role="button" href="#" class="btn btn-primary" disabled>Download</a>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">Secret Key</div>
<div class="panel-heading"><strong>Step 2: Submit Your Public Key</strong></div>
<div class="panel panel-body">
<input id="secret-key" class="textinput textInput form-control" type="text"></input>
<input id="event-param" type="text" value="{{event.EID}}" hidden></input>
<p>Make a backup of this secret key before submitting your public key. This can NOT be recalculated if forgotten!</p>
<button id="keygen-btn" onclick="demosEncrypt.generateKeys()" class="btn btn-default">Generate</button>
<a id="download-btn" role="button" href="#" class="btn btn-default" disabled>Download</a>
{% load crispy_forms_tags %}
<form method="post" action="" class="">
{% crispy form %}
<p>Ensure your secret key is backed up before submitting.</p>
<input id="public-submit" class="btn btn-danger" type="submit" value="Submit" disabled/>
</form>
</div>
</div>
</div>

1
start_celery_worker.sh Executable file
View File

@ -0,0 +1 @@
celery -A allauthdemo worker -l info

View File

@ -154,7 +154,7 @@ input[type="file"] {
display: none;
}
/* Events List page */
/* Events List page / Events Detail */
.statusBtn {
width: 74px;
@ -166,4 +166,16 @@ input[type="file"] {
.marginTopCreateButton {
margin-top: 1.75em;
}
.marginTopEditButton {
margin-top: 0.85em;
}
.marginTopOverview {
margin-top: 5em;
}
.overviewPadding {
padding-left: 16px;
}

View File

@ -705,7 +705,11 @@ function processFileChange(event) {
}
}
document.getElementById('files').addEventListener('change', processFileChange, false);
var filesHandle = document.getElementById('files');
if(filesHandle) {
filesHandle.addEventListener('change', processFileChange, false);
}
// reCAPTCHA