Compare commits

..

812 Commits

Author SHA1 Message Date
Ariel Rin
2f320bc256 Version Bump 2.9.0a1 2021-05-09 06:45:17 +00:00
Ariel Rin
45b8d42b8e Merge branch 'javascript-modernization' into 'v2.9.x'
Cleanup and modernize JS

See merge request allianceauth/allianceauth!1305
2021-05-09 06:43:58 +00:00
Peter Pfeufer
bd2d19f867 Cleanup and modernize JS 2021-05-09 06:43:58 +00:00
Ariel Rin
0be404baca Merge branch 'py36remove' into 'v2.9.x'
Remove Python36 Testing and Support

See merge request allianceauth/allianceauth!1301
2021-05-09 06:42:56 +00:00
Ariel Rin
e6cee9ac83 Remove Python36 Testing and Support 2021-05-09 06:42:56 +00:00
Ariel Rin
d173a59441 Merge branch 'add-robots-txt' into 'v2.9.x'
Adding robots.txt to fix random 404 message

See merge request allianceauth/allianceauth!1302
2021-04-27 14:11:14 +00:00
Peter Pfeufer
8f59f2549a robots.txt added
This fixes the randomly appearing 404 error in Auth  (issue #1232)
2021-04-15 20:59:25 +02:00
Ariel Rin
630400fee4 Merge branch 'django-3-2-update' into 'v2.9.x'
Django 3.2 compatibility update

See merge request allianceauth/allianceauth!1300
2021-04-08 05:07:01 +00:00
Ariel Rin
1ec6929e91 Merge branch 'python39' into 'v2.9.x'
Add  Python3.9 Testing

See merge request allianceauth/allianceauth!1284
2021-04-08 04:49:15 +00:00
Ariel Rin
8c6bdd8ae2 Add Python3.9 Testing 2021-04-08 04:49:15 +00:00
Peter Pfeufer
e04138bced migrations 2021-04-07 18:40:39 +02:00
Peter Pfeufer
db5ad85811 add new default for PKs 2021-04-07 18:16:33 +02:00
Peter Pfeufer
5b44fd376d revert version cap on Django, so we get 3.2 again 2021-04-07 18:11:01 +02:00
Ariel Rin
0036e8b280 Version Bump v2.8.3 2021-04-07 15:18:08 +00:00
Ariel Rin
ea2b5bfecf Merge branch 'srp-killmail-link-fix-issue-1282' into 'master'
Proper error message on non kill mail zkb links

See merge request allianceauth/allianceauth!1297
2021-04-07 15:12:56 +00:00
Peter Pfeufer
aa7495fa60 Proper error message on non kill mail zkb links 2021-04-07 15:12:56 +00:00
Ariel Rin
162ec1bd86 Merge branch 'docs-permissioncleanup' into 'master'
Permissions Cleanup Docs

See merge request allianceauth/allianceauth!1293
2021-04-07 13:23:28 +00:00
Ariel Rin
2668884008 Merge branch 'srp-copypaste' into 'master'
SRP Character Name Copy-Paste

See merge request allianceauth/allianceauth!1295
2021-04-07 13:22:24 +00:00
Ariel Rin
abdc3f3485 SRP Character Name Copy-Paste 2021-04-07 13:22:23 +00:00
Ariel Rin
911f21ee7c Merge branch 'add-support-discord-link' into 'master'
support discord link added to admin notifications on dashboard

See merge request allianceauth/allianceauth!1298
2021-04-07 13:21:26 +00:00
Ariel Rin
2387c40254 Limit Django to 3.1.x 2021-04-07 13:12:15 +00:00
Peter Pfeufer
76e18a79b3 support discord link added to admin notifications on dashboard 2021-04-06 10:44:12 +02:00
Ariel Rin
9725c9c947 Update .gitlab-ci.yml file 2021-03-27 08:38:40 +00:00
Ariel Rin
247ed7cc64 Merge branch 'nootification_log_spam' into 'master'
Use default by default in Notification system

See merge request allianceauth/allianceauth!1294
2021-02-25 11:04:18 +00:00
Aaron Kable
2fd2af793e Use default by defaut in notification system 2021-02-21 18:14:42 +08:00
Ariel Rin
3fc36b9ce1 Permissions Cleanup Tool 2021-02-18 13:33:23 +10:00
Ariel Rin
c12fd2d7bc Merge branch 'improve_developer_docs' into 'master'
Improve developer docs

See merge request allianceauth/allianceauth!1292
2021-02-18 01:52:15 +00:00
ErikKalkoken
7fe1ba2fb2 Improve docs for WSL setup 2021-02-17 09:50:01 +01:00
ErikKalkoken
ab7eb3e5df Fix sphinx issues in docs 2021-02-17 09:35:18 +01:00
Ariel Rin
3452c3acd1 Version Bump v2.8.2 2021-02-05 12:19:25 +00:00
Ariel Rin
3c305fbf37 Merge branch 'wheel' into 'master'
Build wheel

See merge request allianceauth/allianceauth!1290
2021-01-26 12:59:56 +00:00
Rebecca Claire Murphy
a5e0721ec1 Build wheel 2021-01-26 12:59:56 +00:00
Ariel Rin
164a0d5376 Merge branch 'gitlab-announcements-fix' into 'master'
Only show open announcements

See merge request allianceauth/allianceauth!1291
2021-01-26 12:55:11 +00:00
Ariel Rin
2bcf6ec39a Merge branch 'master' into 'master'
Squash mumble migrations

See merge request allianceauth/allianceauth!1289
2021-01-26 12:30:35 +00:00
Peter Pfeufer
40824156bf Only show open announcements 2021-01-25 21:13:43 +01:00
Rebecca Claire Murphy
cec4495034 Merge branch 'master' of gitlab.com:allianceauth/allianceauth 2021-01-15 19:43:39 -05:00
Ariel Rin
74eb8621d9 Merge branch 'notifications_refresh' into 'master'
Notifications refresh v2 and session caching

See merge request allianceauth/allianceauth!1218
2021-01-16 00:09:49 +00:00
Erik Kalkoken
4394d25961 Notifications refresh v2 and session caching 2021-01-16 00:09:49 +00:00
Rebecca Claire Murphy
8113327d31 Squash mumble migrations 2021-01-08 12:28:12 -05:00
Ariel Rin
aeeb35bc60 Merge branch 'ceo_id' into 'master'
Add CEO ID to Corp Eve Model

See merge request allianceauth/allianceauth!1287
2021-01-08 10:37:33 +00:00
colcrunch
630aa3f320 Merge branch 'doc-fixes' into 'master'
It's TeamSpeak not Discord here

See merge request allianceauth/allianceauth!1288
2021-01-07 19:16:22 +00:00
Peter Pfeufer
3487a945c2 It's TeamSpeak not Discord here 2021-01-06 10:42:01 +01:00
Ariel Rin
1936ae44a3 Merge branch 'opengroupauditlogs' into 'master'
Add Open Group Audit Logs

Closes #1262

See merge request allianceauth/allianceauth!1279
2021-01-06 02:38:48 +00:00
Ariel Rin
3d7a84e786 Revert "Add Audit Logs to Open Groups and Autoleaves"
This reverts commit f64d81292e8b6c0531d610799098c3e3cf105c87.
2021-01-06 02:38:47 +00:00
Ariel Rin
a4d6730cb0 Merge branch 'docs' into 'master'
Document Permissions and other Docs Improvments

Closes #1253

See merge request allianceauth/allianceauth!1283
2021-01-06 02:37:29 +00:00
Ariel Rin
b724227a29 Merge branch 'dependencies' into 'master'
Bump Django-Redis-Cache to 3.x branch

Closes #1264

See merge request allianceauth/allianceauth!1280
2021-01-06 02:36:52 +00:00
AaronKable
d72964fd7c Add CEO Id to Corp Eve Model 2021-01-05 22:14:52 +08:00
Ariel Rin
d4a41cfb60 Merge branch 'transifex' into 'master'
Update from Transifex

See merge request allianceauth/allianceauth!1285
2020-12-28 07:11:50 +00:00
Ariel Rin
05859453df Update from Transifex 2020-12-28 07:11:50 +00:00
Ariel Rin
240d910c9b Document Permissions and other Docs Improvments 2020-12-26 16:10:42 +10:00
Ariel Rin
0c31bce7d0 Bump Django-Redis-Cache up to 3.x branch 2020-12-23 17:59:36 +10:00
Ariel Rin
f4c4ae36ed Merge branch 'fix_celeryonce_dependency' into 'master'
Prevent autoretry_for bug in celery_once

See merge request allianceauth/allianceauth!1278
2020-12-19 02:08:36 +00:00
Ariel Rin
b9931b2ceb Merge branch 'improve_groups_state_binding' into 'master'
Enable group state restrictions to work with internal groups

See merge request allianceauth/allianceauth!1276
2020-12-19 02:05:05 +00:00
Erik Kalkoken
4fd6f06c0b Enable group state restrictions to work with internal groups 2020-12-19 02:05:05 +00:00
ErikKalkoken
09573ba7ef fix dependency for celery_once 2020-12-17 22:53:27 +01:00
Ariel Rin
08e9676760 Version Bump v2.8.1 2020-12-07 04:46:55 +00:00
Ariel Rin
15ae737522 Merge branch 'user-agent' into 'master'
Bump minimum django-esi and add User Agent header.

See merge request allianceauth/allianceauth!1275
2020-12-07 04:32:10 +00:00
Ariel Rin
50ec9e563e Merge branch 'celery_result_logger' into 'master'
Remove result from default log message on task success

See merge request allianceauth/allianceauth!1273
2020-12-07 04:30:52 +00:00
colcrunch
cb40649f8b Add note to local.py on ESI_CONTACT_EMAIL setting 2020-12-06 22:54:31 -05:00
colcrunch
bccead0881 Add mention of ESI_USER_CONTACT_EMAIL to install documentation. 2020-12-02 17:42:03 -05:00
colcrunch
35ae710624 Add ESI_USER_CONTACT_EMAIL setting to local.py 2020-12-01 23:25:48 -05:00
colcrunch
75de4518f2 Use app_info_text in EveSwaggerProvider to set the User-Agent header on ESI requests. 2020-12-01 22:40:09 -05:00
colcrunch
bfcdfea6c8 Update minimum django-esi version to ensure support for user-agent header. 2020-12-01 17:17:55 -05:00
Ariel Rin
471553fa88 Add a minimal set of django deps needed to build docs 2020-12-01 07:14:22 +00:00
Ariel Rin
db59f5f69b Add Django to docs requirements 2020-11-30 14:41:01 +00:00
Ariel Rin
1b5413646e Correct Typos in Docs Requirements 2020-11-30 14:37:42 +00:00
Ariel Rin
0337d2517c use limited set of python packages for docs 2020-11-30 14:30:23 +00:00
ErikKalkoken
87e6eb9688 Remove result from default log message on task success 2020-11-28 22:10:41 +01:00
Ariel Rin
7b8c246ef8 Merge branch 'master' into 'master'
[Quickfix] Missing ^ added

See merge request allianceauth/allianceauth!1271
2020-11-19 13:26:53 +00:00
Peter Pfeufer
a268a8980a Missing ^ added 2020-11-18 22:50:27 +01:00
Ariel Rin
3b516c338e Merge branch 'groupmanagement-revamp' into 'master'
Group management improvements

See merge request allianceauth/allianceauth!1270
2020-11-17 11:56:16 +00:00
Peter Pfeufer
9952685805 textfield should not be Null=True 2020-11-16 18:04:57 +01:00
Peter Pfeufer
2f59c8df22 And of course I missed something ... 2020-10-26 01:10:41 +01:00
Peter Pfeufer
6cd0a42791 reverted code formatting for Aaron :-) 2020-10-26 01:06:58 +01:00
Peter Pfeufer
4c42153bfd stop member count header from line breaking 2020-10-25 13:53:17 +01:00
Peter Pfeufer
603bd9c37c make description a real description
textfield instead of charfield. a bit more user friendly in the backend
2020-10-25 12:40:10 +01:00
Peter Pfeufer
87c0c3ac73 tables formatted properly
align center is horrible to read ....
2020-10-25 11:55:03 +01:00
Peter Pfeufer
8a91e7f6ac navactive status fixed in left menu and top menu 2020-10-25 11:22:10 +01:00
Peter Pfeufer
281dbdbb01 proper URL structure established 2020-10-25 11:21:35 +01:00
Ariel Rin
8980d8d32f Version bump to 2.8.0 Stable 2020-10-13 05:33:54 +00:00
Ariel Rin
9d6cf9a62e Merge branch 'transifex' into 'master'
Add French and Japanese Translations + QoL Translation Fixes

See merge request allianceauth/allianceauth!1263
2020-10-13 04:17:37 +00:00
Ariel Rin
0fabb2b368 Add French and Japanese Translations + QoL Translation Fixes 2020-10-13 04:17:37 +00:00
Ariel Rin
9801ca0314 Merge branch 'emailoverrideredirect' into 'master'
Fix Redirect to dashboard if not verifying email

See merge request allianceauth/allianceauth!1268
2020-10-13 04:08:15 +00:00
Ariel Rin
fd86b26b39 Redirect to dashboard if not verifying email 2020-10-13 13:25:00 +10:00
Ariel Rin
b0dbef1587 Merge branch 'master' into 'master'
Expand mumble pwhash field to allow for passlib 1.7.3+

See merge request allianceauth/allianceauth!1267
2020-10-11 10:34:44 +00:00
Ariel Rin
a6e60bc23b expand mumble certhash to allow for hmac-sha-256 2020-10-11 20:11:56 +10:00
Ariel Rin
137e8a876d Merge branch 'add-corp-and-alliance-tags-to-srp' into 'master'
Add alliance and corp ticker to pilot name

See merge request allianceauth/allianceauth!1265
2020-10-11 03:42:56 +00:00
Ariel Rin
fe9538253f Merge branch 'highlight-active-menu-item' into 'master'
Highlight active menu item

See merge request allianceauth/allianceauth!1266
2020-10-11 03:41:17 +00:00
Peter Pfeufer
edda2c248e highlight active menu item 2020-10-02 20:12:21 +02:00
Peter Pfeufer
62275639e3 Added alliance and corp ticker to pilot name
Takes care of https://gitlab.com/allianceauth/allianceauth/-/issues/1228
2020-10-01 20:14:54 +02:00
Ariel Rin
d0c68b82f4 Merge branch 'docs' into 'master'
Docs Sphinx Upgrades

See merge request allianceauth/allianceauth!1264
2020-10-01 09:17:36 +00:00
Ariel Rin
78b5953bdf Merge branch 'master' of https://gitlab.com/allianceauth/allianceauth into docs 2020-10-01 18:59:14 +10:00
Ariel Rin
25e565b099 Docs Requirement upgrades 2020-10-01 18:54:04 +10:00
Ariel Rin
1fe0f78ad7 Exclude django-redis-cache 2.1.3 2020-10-01 01:59:48 +00:00
Ariel Rin
fc8b68156f Merge branch 'patch-4' into 'master'
Fixing FA icon

See merge request allianceauth/allianceauth!1262
2020-09-29 05:10:30 +00:00
Peter Pfeufer
b47cd197ce Fixing FA icon 2020-09-28 21:07:08 +00:00
Ariel Rin
3943426c4c Version Bump 2.8.0a2 2020-09-21 06:25:16 +00:00
Ariel Rin
08a9bd42a3 Merge branch 'django3' into 'master'
Django 3.1.1 bring up

See merge request allianceauth/allianceauth!1256
2020-09-21 06:16:44 +00:00
Ariel Rin
b2ff339efe Merge branch 'gitlabci2' into 'master'
Update Gitlab Deploy Python Version, Debian Distro to Stable

See merge request allianceauth/allianceauth!1260
2020-09-21 06:15:40 +00:00
Ariel Rin
c604131e04 Update Deploy Runner 2020-09-21 12:12:02 +10:00
Ariel Rin
f17607f126 Merge branch 'transifex' into 'master'
Update From Transifex

See merge request allianceauth/allianceauth!1258
2020-09-21 01:45:16 +00:00
Ariel Rin
94e455a57b Update From Transifex 2020-09-21 01:45:16 +00:00
Ariel Rin
3c9149db4a Merge branch 'improve_authtuilts_add_permission' into 'master'
Improve auth utils for permissions

See merge request allianceauth/allianceauth!1255
2020-09-21 01:09:03 +00:00
Ariel Rin
96cc615c07 Merge branch 'docs_mumbleavatars' into 'master'
Docs: Mumble Avatars Feature

See merge request allianceauth/allianceauth!1250
2020-09-21 00:01:35 +00:00
Ariel Rin
cd1f4a1c2b Docs: Mumble Avatars Feature 2020-09-21 00:01:35 +00:00
Ariel Rin
e0f99a42db Merge branch 'exiom-srp-update' into 'master'
SRP Module - Added Datatables & Sorting, Standardized Date/Time for Overall AA Consistency

See merge request allianceauth/allianceauth!1254
2020-09-20 23:55:22 +00:00
Exiom
3506e417d4 SRP Module - Added Datatables & Sorting, Standardized Date/Time for Overall AA Consistency 2020-09-20 23:55:22 +00:00
AaronKable
36197e2212 swap to reverse_lazy 2020-09-18 23:32:36 +08:00
AaronKable
fc5f42d01e remove whitespace in setup.py 2020-09-18 22:16:49 +08:00
AaronKable
e26d3767e0 update models as NullBooleanField is deprecated. 2020-09-18 22:16:24 +08:00
AaronKable
046b37c76a update auth and group model admins for django 3.1 2020-09-18 22:04:59 +08:00
AaronKable
925ff3e116 remove django req's from tox, they are managed in setup.py 2020-09-18 22:04:05 +08:00
AaronKable
e5ede4f7b6 Cant Reference what is already deleted 2020-09-18 22:03:16 +08:00
AaronKable
ded9301527 initial django3 bringup 2020-09-18 11:45:37 +08:00
ErikKalkoken
bd1ed6ff73 Add user as return value to add permission methods 2020-09-15 12:35:58 +02:00
Ariel Rin
feb65980d4 Merge branch 'fix_group_count_badge' into 'master'
Fix group count badge showing at zero

Closes #1258

See merge request allianceauth/allianceauth!1253
2020-09-12 02:05:22 +00:00
Ariel Rin
e81d75a782 Merge branch 'docs_settings_fix' into 'master'
Remove erroneous indents from settings in service module docs

See merge request allianceauth/allianceauth!1252
2020-09-12 02:04:37 +00:00
ErikKalkoken
ff305d13ae Fix group count badge showing at zero 2020-09-11 23:54:56 +02:00
colcrunch
8bcbc1a779 Remove erroneous indents from settings in service module docs. (Checked other docs, and there do not appear to be any more errors of this type) 2020-09-11 12:51:17 -04:00
Ariel Rin
3874aa6fee Version Bump 2.8.0a1 2020-09-11 11:52:20 +00:00
Ariel Rin
103e9f3a11 Merge branch 'discourse_beta' into 'master'
Discourse API with external package

See merge request allianceauth/allianceauth!1251
2020-09-11 11:33:19 +00:00
Ariel Rin
d02c25f421 Merge branch 'feature_menu_item_badges' into 'master'
Add menu item badge feature and update group icons

See merge request allianceauth/allianceauth!1240
2020-09-11 11:33:14 +00:00
Erik Kalkoken
228af38a4a Add menu item badge feature and update group icons 2020-09-11 11:33:14 +00:00
AaronKable
051a48885c discourse API with external package 2020-09-11 17:19:54 +08:00
Ariel Rin
6bcdc6052f Merge branch 'local-delivery' into 'master'
JS/CSS/Font Refactoring for use with AA-GDPR Package

Closes #1217

See merge request allianceauth/allianceauth!1247
2020-09-11 04:13:01 +00:00
Ariel Rin
af3527e64f Revert "load bootswatch less locally #1217"
This reverts commit 3a9a7267ea8734ba0a5cf1d0fea632eb2276d45c.
2020-09-11 04:13:01 +00:00
Ariel Rin
17ef3dd07a Merge branch 'srp_fix' into 'master'
Use request.scheme to get the http/https for the site

See merge request allianceauth/allianceauth!1249
2020-09-11 03:05:06 +00:00
AaronKable
1f165ecd2a use request.scheme to get the http/https for the site 2020-09-07 19:10:58 +08:00
Ariel Rin
70d1d450a9 Add Korean and Russian as features 2020-09-03 12:24:23 +00:00
Ariel Rin
b667892698 Version Bump 2.7.5 2020-09-01 02:09:05 +00:00
Ariel Rin
dc11add0e9 Merge branch 'discordapp.com-deprecation' into 'master'
discord.com replaces discordapp.com

See merge request allianceauth/allianceauth!1248
2020-09-01 01:53:25 +00:00
Ariel Rin
cb429a0b88 discord.com replaces discordapp.com 2020-09-01 11:20:57 +10:00
Ariel Rin
b51039cfc0 Merge branch 'docs' into 'master'
Add Optimizing Mumble to Services docs

See merge request allianceauth/allianceauth!1243
2020-09-01 01:11:49 +00:00
Ariel Rin
eadd959d95 Merge branch 'fix_celery_once_backend' into 'master'
Fix celery once not working properly

See merge request allianceauth/allianceauth!1246
2020-08-25 06:13:59 +00:00
Erik Kalkoken
1856e03d88 Fix celery once not working properly 2020-08-25 06:13:59 +00:00
Ariel Rin
7dcfa622a3 Merge branch 'issue_1234_exiom' into 'master'
Month Ordering Fix for Group Management Audit Logs

See merge request allianceauth/allianceauth!1245
2020-08-25 06:07:45 +00:00
Exiom
64251b9b3c Fix Date/Time Month Ordering #1234 2020-08-21 03:57:24 +00:00
Exiom
6b073dd5fc Fix Date/Time Month Ordering #1234 2020-08-21 03:33:35 +00:00
Ariel Rin
0911fabfb2 Merge branch 'issue_1234' into 'master'
Correct Month Ordering in Group Management Audit Logs

Closes #1234

See merge request allianceauth/allianceauth!1244
2020-08-20 07:11:47 +00:00
Ariel Rin
050d3f5e63 use month numerical 2020-08-20 16:55:18 +10:00
Ariel Rin
bbe3f78ad1 Grammar and Spelling Corrections 2020-08-20 15:19:50 +10:00
Ariel Rin
8204c18895 Add Optimzing Mumble 2020-08-20 15:09:07 +10:00
Ariel Rin
b91c788897 Merge branch 'bulk-affiliations' into 'master'
Reduce run time for eve online model updates

See merge request allianceauth/allianceauth!1227
2020-08-20 03:14:40 +00:00
Erik Kalkoken
1d20a3029f Only update characters if they have changed corp or alliance by bulk calling affiliations before calling character tasks. 2020-08-20 03:14:40 +00:00
Ariel Rin
9cfebc9ae3 Version Bump to 2.7.4 2020-08-17 06:34:55 +00:00
Ariel Rin
ddabb4539b Merge branch 'fix_admin_status_tags_bug' into 'master'
Bugfix: Loading of dashboard fails with 'NoneType object is not iterable' from status_tags

See merge request allianceauth/allianceauth!1237
2020-08-14 02:26:12 +00:00
Ariel Rin
ada35e221b Merge branch 'transifex' into 'master'
Update from Transifex

See merge request allianceauth/allianceauth!1241
2020-08-14 02:24:07 +00:00
Ariel Rin
6fef9d904e Update from Transifex 2020-08-14 02:24:07 +00:00
Ariel Rin
67cf2b5904 Merge branch 'fontawesomev5' into 'master'
Remove FA v4 shims, bump FA to 5.14

Closes #1248

See merge request allianceauth/allianceauth!1242
2020-08-14 02:21:41 +00:00
Ariel Rin
3bebe792f6 Remove FA v4 shims, bump to fa 5.14 2020-08-14 12:01:08 +10:00
ErikKalkoken
00b4d89181 Fix status tags bug and remove unused context from status_overview tag 2020-07-27 14:53:25 +02:00
Ariel Rin
f729c6b650 Merge branch 'patch-3' into 'master'
Update Mumble documentation on setting a server password

Closes #1252

See merge request allianceauth/allianceauth!1236
2020-07-24 11:18:02 +00:00
colcrunch
df95f8c3f3 Merge branch 'discord_improvements' into 'master'
Fix error 500 on service page for Discord and add feature "group_to_role"

See merge request allianceauth/allianceauth!1235
2020-07-23 20:58:26 +00:00
Erik Kalkoken
fe36e57d72 Fix error 500 on service page for Discord and add feature "group_to_role" 2020-07-23 20:58:26 +00:00
Peter Pfeufer
31197812b6 Update Mumble documentation on setting a server password (#1252) 2020-07-22 12:18:52 +00:00
Ariel Rin
bd3fe01a12 correct task import for manual model population 2020-07-14 12:25:05 +00:00
col_crunch
39f7f32b7d Version bump. 2020-07-13 15:32:54 -04:00
colcrunch
b4522a1277 Merge branch 'enable_django_esi_20' into 'master'
Enable django-esi 2.0 dependency

See merge request allianceauth/allianceauth!1232
2020-07-13 18:54:21 +00:00
colcrunch
bb6a7e8327 Merge branch 'issue_1250' into 'master'
Fix Discord service issues and improve dashboard

Closes #1250

See merge request allianceauth/allianceauth!1229
2020-07-13 18:54:08 +00:00
colcrunch
9bd42a7579 Merge branch 'docu_update_contributions' into 'master'
Add contributing chapter to docs

See merge request allianceauth/allianceauth!1233
2020-07-13 18:52:30 +00:00
Erik Kalkoken
b41430e5a3 Add contributing chapter to docs 2020-07-13 18:52:29 +00:00
ErikKalkoken
595353e838 Enable using django-esi 2.0 2020-07-11 12:35:16 +02:00
ErikKalkoken
f1a21bb856 Add state info to dashboard, improve stat change notifications, improve auth tests 2020-07-03 14:58:45 +02:00
ErikKalkoken
e44c2935f9 Version bump for alpha testing 2020-07-03 13:29:00 +02:00
ErikKalkoken
4d546f948d Fix state role not always updated due to lazy properties 2020-07-02 21:26:40 +02:00
ErikKalkoken
3bab349d7b Fix tests 2020-06-30 00:15:48 +02:00
ErikKalkoken
eef6126ef8 Improve state backend fix, add tests for state backend and service signal changes 2020-06-30 00:01:40 +02:00
ErikKalkoken
5c7478fa39 Fix update_nickname runs on ever save of userprofile, fix nickname not updated after main's name changes 2020-06-28 21:38:25 +02:00
ErikKalkoken
64b72d0b06 Fix service signals for state change 2020-06-28 17:14:16 +02:00
ErikKalkoken
b266a98b25 Add more tests for state change 2020-06-28 14:56:01 +02:00
ErikKalkoken
8a27de5df8 Fix state change does not update groups 2020-06-28 01:45:51 +02:00
ErikKalkoken
f9b5310fce Fix user account not deleted when demoted to guest 2020-06-27 23:32:12 +02:00
ErikKalkoken
fdce173969 Add tests to fix new bugs 2020-06-27 16:18:06 +02:00
ErikKalkoken
7b9ddf90c1 Fix tests for tox 2020-06-25 23:38:20 +02:00
ErikKalkoken
580c8c19de Update request timeout default 2020-06-25 22:32:29 +02:00
ErikKalkoken
55cc77140e Fix bug blocking superuser from adding Discord bot 2020-06-25 22:19:48 +02:00
Ariel Rin
93c89dd7cc Merge branch 'wother-master-patch-74872' into 'master'
Updated discord.md

See merge request allianceauth/allianceauth!1228
2020-06-22 03:19:27 +00:00
Carter Foulger
c970cbbd2d Updated discord.md with additional troubleshooting
steps.
2020-06-19 19:45:22 +00:00
Ariel Rin
9ea55fa51f Version Bump 2.7.2 2020-06-11 03:49:06 +00:00
Ariel Rin
5775a11b4e Merge branch 'replace_context_manager_groups' into 'master'
Improve page load performance by replacing groups context manager

See merge request allianceauth/allianceauth!1219
2020-06-11 03:41:31 +00:00
Ariel Rin
1a666b6584 Merge branch 'fontawesomev5' into 'master'
Font Awesome V5 Update

Closes #1207

See merge request allianceauth/allianceauth!1224
2020-06-11 03:33:37 +00:00
Ariel Rin
35407a2108 Font Awesome V5 Update 2020-06-11 03:33:37 +00:00
Ariel Rin
71fb19aa22 Merge branch 'version_battle' into 'master'
Make version relevant to an admin

See merge request allianceauth/allianceauth!1220
2020-06-11 03:13:13 +00:00
AaronKable
b7d7f7b8ce latest stable 2020-06-11 10:47:05 +08:00
Ariel Rin
59b983edcc Merge branch 'future' into 'master'
Remove Future dependency

Closes #1242

See merge request allianceauth/allianceauth!1223
2020-06-11 01:01:23 +00:00
Ariel Rin
1734d034e1 Merge branch 'evemodel_integers' into 'master'
Change EveModels to Integer ID fields

See merge request allianceauth/allianceauth!1211
2020-06-09 13:10:10 +00:00
Aaron Kable
7f7500ff0c Change EveModels to Integer ID fields 2020-06-09 13:10:10 +00:00
Ariel Rin
ce77c24e5c Exclude Celery 4.4.4 2020-06-09 11:30:13 +10:00
Ariel Rin
5469a591c0 Remove Future dependency 2020-06-09 11:10:32 +10:00
Ariel Rin
a4befc5e59 Version Bump 2.7.1 2020-06-09 00:25:30 +00:00
Ariel Rin
1ee8065592 Merge branch 'issue_1244' into 'master'
Fix sleep length must be non-negative

Closes #1244

See merge request allianceauth/allianceauth!1222
2020-06-09 00:23:36 +00:00
ErikKalkoken
e4e3bd44fc Fix sleep length must be non-negative 2020-06-08 14:59:22 +02:00
AaronKable
c75de07c2e Only show Pre-Release when available 2020-06-08 20:47:05 +08:00
AaronKable
e928131809 make version relevant to an admin 2020-06-08 19:18:11 +08:00
Ariel Rin
4f802e82a9 Version Bump to 2.7.0 2020-06-07 06:53:52 +00:00
Ariel Rin
0c90bd462e Merge branch 'remove_test_logging' into 'master'
Remove Test Logging from discourse

See merge request allianceauth/allianceauth!1207
2020-06-07 06:52:08 +00:00
ErikKalkoken
bbb70c93d9 Initial 2020-06-06 17:59:23 +02:00
Ariel Rin
f6e6ba775c Merge branch 'revert-3a984e8a' into 'master'
Revert "Merge branch 'notifications_refresh' into 'master'"

See merge request allianceauth/allianceauth!1216
2020-06-04 14:22:11 +00:00
Ariel Rin
06646be907 Merge branch 'fix_celery_4.4.4_issue' into 'master'
Add future to dependencies to fix celery 4.4.4 issue

See merge request allianceauth/allianceauth!1217
2020-06-04 11:58:28 +00:00
ErikKalkoken
1b4c1a4b9e Add future to dependencies 2020-06-04 13:52:34 +02:00
Ariel Rin
ae3f5a0f62 Revert "Merge branch 'notifications_refresh' into 'master'"
This reverts merge request !1215
2020-06-04 11:21:50 +00:00
Ariel Rin
3a984e8a4d Merge branch 'notifications_refresh' into 'master'
Add notifications auto refresh

See merge request allianceauth/allianceauth!1215
2020-06-04 08:25:01 +00:00
Erik Kalkoken
7d711a54bc Add notifications auto refresh 2020-06-04 08:25:01 +00:00
Ariel Rin
d92d629c25 Merge branch 'orm_fix' into 'master'
Fix Group Managment ORM Queries

See merge request allianceauth/allianceauth!1214
2020-05-27 02:23:37 +00:00
Ariel Rin
21e630209a Merge branch 'issue_1238' into 'master'
PEP440 versioning for admin dashboard

See merge request allianceauth/allianceauth!1213
2020-05-27 02:21:01 +00:00
Erik Kalkoken
e3933998ef PEP440 versioning for admin dashboard 2020-05-27 02:21:00 +00:00
Ariel Rin
667afe9051 Merge branch 'master' into 'master'
Update Extension Logger

Closes #1230

See merge request allianceauth/allianceauth!1206
2020-05-27 02:17:43 +00:00
AaronKable
26dc2881eb fix group managment ORM queries 2020-05-27 08:15:22 +08:00
Col Crunch
250cb33285 Raise an error if get_extension_logger recieves a non-string argument. 2020-05-26 13:49:06 -04:00
Col Crunch
db51abec1f Change default logging level for extension logger. 2020-05-26 13:26:19 -04:00
Col Crunch
530716d458 Fix docstring n get_extension_logger 2020-05-26 13:25:48 -04:00
Ariel Rin
f3065d79b3 Merge branch 'improve_evelinks' into 'master'
Add support for type icons to evelinks

See merge request allianceauth/allianceauth!1210
2020-05-25 14:51:42 +00:00
Erik Kalkoken
bca5f0472e Add support for type icons to evelinks 2020-05-25 14:51:41 +00:00
Ariel Rin
8e54c43917 Merge branch 'mumble-certhash' into 'master'
Add certhash field to Mumble user

See merge request allianceauth/allianceauth!1204
2020-05-25 14:46:03 +00:00
Ariel Rin
946df1d7a0 Merge branch 'issue_1234' into 'master'
Fix issue #1234 and add badges to group requests

Closes #1234

See merge request allianceauth/allianceauth!1212
2020-05-25 13:36:29 +00:00
ErikKalkoken
55f00f742c Fix issue #1234 and add badges to group requests 2020-05-25 15:22:08 +02:00
Ariel Rin
97b2cb71b7 Version Bump to 2.6.6a10 2020-05-25 12:55:05 +00:00
Ariel Rin
ba3a5ba53c Merge branch 'discord_delete_user_bug' into 'master'
Fix API exception handling for delete_user

See merge request allianceauth/allianceauth!1209
2020-05-25 01:28:25 +00:00
ErikKalkoken
953c09c999 Fix backoff exception handling for delete_user 2020-05-24 19:13:31 +02:00
Ariel Rin
b4cc325b07 Merge branch 'fix_test_order_issues' into 'master'
Make notification tests less order dependent

See merge request allianceauth/allianceauth!1208
2020-05-23 04:58:07 +00:00
AaronKable
28c1343f3e make tests less order dependant 2020-05-23 12:54:09 +08:00
Col Crunch
c16fd94c4a Update CI config for new version of gitlab. 2020-05-23 00:31:27 -04:00
Ariel Rin
bb2cc20838 Merge branch 'discord_ignore_managed_roles' into 'master'
Enable Discord service to deal with managed roles

See merge request allianceauth/allianceauth!1205
2020-05-23 04:28:01 +00:00
Erik Kalkoken
7b815fd010 Enable Discord service to deal with managed roles 2020-05-23 04:28:01 +00:00
AaronKable
18584974df remove test logging 2020-05-23 12:18:05 +08:00
Ariel Rin
15823b7785 Merge branch 'stop_context_spam' into 'master'
Stop the notification context provider from hitting the database for every menu hook

See merge request allianceauth/allianceauth!1201
2020-05-23 04:14:10 +00:00
Col Crunch
6c275d4cd2 Add comment to local describing how to change logger level. Also update docs to be consistent with this change. 2020-05-23 00:08:37 -04:00
Col Crunch
2d64ee5e2a Base extension logger level on the level of its parent. 2020-05-23 00:08:00 -04:00
AaronKable
3ae5ffa3f6 tests 2020-05-23 12:04:06 +08:00
Ben Cole
57d9ddc2c6 Add certhash field to Mumble user 2020-05-22 14:04:35 +01:00
Ariel Rin
8b84def494 Merge branch 'discord_update_usernames' into 'master'
Add update username feature

See merge request allianceauth/allianceauth!1203
2020-05-19 03:18:01 +00:00
ErikKalkoken
546f01ceb2 Add update username feature 2020-05-19 00:13:19 +02:00
Ariel Rin
be720d0e0f Merge branch 'redundant-sql' into 'master'
Use existing character variable on dashboard.

See merge request allianceauth/allianceauth!1202
2020-05-18 01:02:08 +00:00
Ariel Rin
72bed03244 Merge branch 'discord_service_overhaul' into 'master'
Discord service major overhaul

See merge request allianceauth/allianceauth!1200
2020-05-18 01:01:13 +00:00
Erik Kalkoken
38083ed284 Discord service major overhaul 2020-05-18 01:01:13 +00:00
Ariel Rin
53f1b94475 Correct typo in bzip2-devel dependency 2020-05-14 09:31:47 +00:00
AaronKable
ed4270a0e3 Use existing character variable. 2020-05-14 17:09:00 +08:00
Ariel Rin
f1d5cc8903 Merge branch 'discourse' into 'master'
API Headers for Discourse

See merge request allianceauth/allianceauth!1197
2020-05-12 00:00:43 +00:00
Ariel Rin
80efdec5d9 Version Bump 2.6.5 2020-05-10 03:17:39 +00:00
Ariel Rin
d49687400a Merge branch 'typo_fix' into 'master'
Typo Fix

See merge request allianceauth/allianceauth!1199
2020-05-10 03:13:35 +00:00
AaronKable
e6e03b50da Sorry Ariel. I promise to do better. 2020-05-08 23:48:18 +08:00
AaronKable
543fa3cfa9 extra logging for tests 2020-05-08 10:03:05 +08:00
Ariel Rin
899988c7c2 Merge branch 'i18n-chinese' into 'master'
Significant Korean Translation, Transifex Updates

See merge request allianceauth/allianceauth!1198
2020-05-08 01:12:37 +00:00
Ariel Rin
2f48dd449b Significant Korean Translation, Transifex Updates 2020-05-08 01:12:37 +00:00
AaronKable
f70fbbdfee headers for Discourse 2020-04-27 14:37:01 +08:00
Ariel Rin
2b09ca240d Merge branch 'fix_docu_py_upgrade' into 'master'
Docs only: Python upgrade guide and mumble installation guide

See merge request allianceauth/allianceauth!1193
2020-04-26 04:30:23 +00:00
Ariel Rin
0626ff84ad Merge branch 'group_add' into 'master'
Direct Group Link Copy Button

See merge request allianceauth/allianceauth!1196
2020-04-26 04:25:28 +00:00
Ariel Rin
62ec746ee3 Merge branch 'Jonnyw2k-master-patch-23183' into 'master'
Add OG metadata to base.html

Closes #1231

See merge request allianceauth/allianceauth!1195
2020-04-26 04:25:12 +00:00
ErikKalkoken
d0f12d7d56 Make customization its own chapter, add tuning section 2020-04-23 17:04:16 +02:00
ErikKalkoken
b806a69604 Improve mumble installation guide 2020-04-19 17:43:43 +02:00
AaronKable
a609d6360b add copy button to group manager screen 2020-04-18 10:43:01 +08:00
Jonnyw2k
dafbfc8644 Update allianceauth/authentication/templates/public/base.html 2020-04-18 02:12:12 +00:00
Jonnyw2k
55413eea19 Update allianceauth/authentication/templates/public/base.html 2020-04-18 01:22:07 +00:00
ErikKalkoken
5247c181af Fix django-celery-beat version in py upgrade guide 2020-04-18 01:14:27 +02:00
Ariel Rin
321af5ec87 Version Bump v2.6.4 2020-04-17 06:56:40 +00:00
Ariel Rin
9ccf340b3d Merge branch 'error-redirects' into 'master'
Add 500 and 400, 403, 404 error redirects back to dashboard with basic message

See merge request allianceauth/allianceauth!1152
2020-04-17 06:45:01 +00:00
Aaron Kable
d7dcacb899 Add 500 and 400, 403, 404 error redirects back to dashboard with basic message 2020-04-17 06:45:01 +00:00
Ariel Rin
8addd483c2 Merge branch 'issue_1221' into 'master'
Remove support for Python 3.5

Closes #1224 and #1221

See merge request allianceauth/allianceauth!1182
2020-04-15 11:25:44 +00:00
Ariel Rin
4d27e5ac9b Merge branch 'improve_help_icon' into 'master'
Improve style of help icon

See merge request allianceauth/allianceauth!1192
2020-04-15 11:24:55 +00:00
ErikKalkoken
31290f6e80 Improve style of help icon 2020-04-11 20:23:41 +02:00
Ariel Rin
c31cc4dbee Merge branch 'mumble_displaynames' into 'master'
Mumble Display Names

See merge request allianceauth/allianceauth!1185
2020-04-06 02:19:53 +00:00
Aaron Kable
cc1f94cf61 Mumble Display Names 2020-04-06 02:19:53 +00:00
Ariel Rin
a9132b8d50 Merge branch 'fix_dev_docs' into 'master'
Add config file for readthedocs

See merge request allianceauth/allianceauth!1191
2020-04-03 13:33:30 +00:00
ErikKalkoken
7b4a9891aa Add config file for readthedocs 2020-04-03 15:18:45 +02:00
Ariel Rin
dcaaf38ecc Merge branch 'esi_update' into 'master'
Update swagger files and remove swagger file dependency from srp package

See merge request allianceauth/allianceauth!1187
2020-04-03 12:39:34 +00:00
Ariel Rin
653a8aa850 Merge branch 'improve_docu_link' into 'master'
Move docu link to top menu and open in new window

See merge request allianceauth/allianceauth!1189
2020-04-03 12:04:06 +00:00
Ariel Rin
274af11385 Merge branch 'docu_devs_update' into 'master'
Extend developer docs

See merge request allianceauth/allianceauth!1188
2020-04-03 12:03:34 +00:00
Erik Kalkoken
170b246901 Extend developer docs 2020-04-03 12:03:34 +00:00
Ariel Rin
5250432ce3 Version Bump v2.6.3 2020-04-02 03:51:59 +00:00
Ariel Rin
53d6e973eb Merge branch 'i18n-chinese' into 'master'
Update Translations from Transifex

See merge request allianceauth/allianceauth!1190
2020-04-02 03:32:39 +00:00
Ariel Rin
c9bdd62d53 Update Translations from Transifex 2020-04-02 03:32:39 +00:00
Ariel Rin
7eb98af528 Merge branch 'issue_1225' into 'master'
Fix broken link and remove outdated migrations for services name formatter

Closes #1225

See merge request allianceauth/allianceauth!1183
2020-04-02 03:04:55 +00:00
Ariel Rin
385e3e21b3 Merge branch 'improve_groups_view' into 'master'
Add sorting to group view and add tests to group management

See merge request allianceauth/allianceauth!1172
2020-04-02 03:01:27 +00:00
Erik Kalkoken
127ec63d76 Add sorting to group view and add tests to group management 2020-04-02 03:01:27 +00:00
Ariel Rin
4988b5f260 Merge branch 'common_logger' into 'master'
Extensions Logging

See merge request allianceauth/allianceauth!1180
2020-04-02 02:59:55 +00:00
Ariel Rin
f28a50f92c Merge branch 'fix_translations_3' into 'master'
Add missing translations

See merge request allianceauth/allianceauth!1186
2020-03-26 03:06:11 +00:00
Ariel Rin
e8efe8e609 Merge branch 'i18n-chinese' into 'master'
Add Korean and Russian, Update from Transifex

See merge request allianceauth/allianceauth!1179
2020-03-26 02:50:25 +00:00
Ariel Rin
d7e7457bc5 Add Korean and Russian, Update from Transifex 2020-03-26 02:50:25 +00:00
Ariel Rin
daff927811 Merge branch 'prioritize_celery' into 'master'
Add Celery Priorities

See merge request allianceauth/allianceauth!1181
2020-03-26 02:20:02 +00:00
Aaron Kable
8861ec0a61 Add Celery Priorities 2020-03-26 02:20:02 +00:00
Ariel Rin
bd4321f61a Merge branch 'docs_update' into 'master'
Docs only: Harmonize gunicorn config, add localization feature

See merge request allianceauth/allianceauth!1184
2020-03-26 01:55:03 +00:00
Erik Kalkoken
d831482fe0 Docs only: Harmonize gunicorn config, add localization feature 2020-03-26 01:55:03 +00:00
ErikKalkoken
9ea79ea389 Move docu link to top menu and open in new window 2020-03-26 00:10:30 +01:00
ErikKalkoken
b6fdf840ef Update swagger files and remove swagger fle dependency from srp package 2020-03-25 18:00:23 +01:00
ErikKalkoken
73f262ce4b Add missing translations 2020-03-24 20:21:35 +01:00
ErikKalkoken
f63434adc3 Fix broken link and remove outdated migrations for services name formatter 2020-03-21 14:41:45 +01:00
ErikKalkoken
42948386ec Remove support for Python 3.5 2020-03-21 13:16:42 +01:00
Aaron Kable
1ce0dbde0e stop the notification context profider from hitting the database for each menu hook 2020-03-16 02:09:24 +00:00
Ariel Rin
32e0621b0a Merge branch 'improve_install_docu' into 'master'
Docs: Add python upgrade guide, remove old AA 1.15 upgrade guide, improve install guide

See merge request allianceauth/allianceauth!1177
2020-03-15 12:21:21 +00:00
Erik Kalkoken
78e05b84e9 Docs: Add python upgrade guide, remove old AA 1.15 upgrade guide, improve install guide 2020-03-15 12:21:21 +00:00
Col Crunch
76ebd21163 Add function to services.hooks to provide a concise way for creating loggers for extensions/plugins. Revise basic documentation to use this function. 2020-03-13 15:21:15 -04:00
Col Crunch
38aaf545c6 Add some very basic docs for logging changes 2020-03-13 14:42:09 -04:00
Col Crunch
527d7ef671 Change level of extension_file handler, rename the logger from allianceauth.extensions to extensions and remove propagate from the logger. 2020-03-13 04:42:09 -04:00
Col Crunch
e54b80e061 Add a common logger (and specific log file) for extensions to utilize. 2020-03-13 00:33:35 -04:00
Col Crunch
27f95a8b2c Remove Zone.Indentifier files. 2020-03-12 23:55:34 -04:00
Ariel Rin
a1e8903128 Version Bump v2.6.2 2020-03-09 15:53:51 +00:00
Ariel Rin
b00ac2aef4 Merge branch 'i18n-chinese' into 'master'
Update German and Spanish Locales, Add Chinese Simplified with Transifex

See merge request allianceauth/allianceauth!1174
2020-03-09 15:51:12 +00:00
Ariel Rin
8865d15ed9 Update German and Spanish Locales, Add Chinese Simplified with Transifex 2020-03-09 15:51:12 +00:00
Ariel Rin
fc3d4b7f33 Merge branch 'hotfix_groups' into 'master'
HOTFIX GroupManager for Groups as Group Leads

See merge request allianceauth/allianceauth!1178
2020-03-09 08:17:41 +00:00
Ariel Rin
934cc44540 Merge branch 'fix_dashboard_sorting' into 'master'
Improve groups and character lists on dashboard

See merge request allianceauth/allianceauth!1171
2020-03-09 08:14:26 +00:00
AaronKable
106de3dd4c HOTFIX Group manager for Groups as Group Leads 2020-03-09 15:48:30 +08:00
Ariel Rin
9b55cfcbe3 Merge branch 'issue_1214' into 'master'
Documentation overhaul

Closes #1216 and #1214

See merge request allianceauth/allianceauth!1166
2020-03-05 02:23:58 +00:00
Erik Kalkoken
8137f1023a Documentation overhaul 2020-03-05 02:23:58 +00:00
Ariel Rin
d670e33b6f Merge branch 'fix_translation_strings_2' into 'master'
fix broken translation strings (part 2)

See merge request allianceauth/allianceauth!1176
2020-03-04 23:00:08 +00:00
ErikKalkoken
3d3bb8fc94 fix broken translation strings 2020-03-03 13:53:55 +01:00
Ariel Rin
9c880eae8a Merge branch 'fix_translation_string_bugs' into 'master'
Fix translation string bugs

See merge request allianceauth/allianceauth!1175
2020-03-03 00:52:18 +00:00
ErikKalkoken
54a71630f1 Fix translation string bugs 2020-02-29 15:55:42 +01:00
Ariel Rin
923a8453cc Merge branch 'fix_coverage_for_core' into 'master'
Remove coverage output from core tests

See merge request allianceauth/allianceauth!1173
2020-02-22 15:57:48 +00:00
ErikKalkoken
00447ca819 Remove coverage output from core tests 2020-02-22 16:50:04 +01:00
Ariel Rin
ad4ee9d822 Version Bump v2.6.1 2020-02-22 12:59:41 +00:00
Ariel Rin
40e9dbfda2 Merge branch 'issue_1219' into 'master'
HOTFIX: Fix startup error when autogroups is not installed

Closes #1219

See merge request allianceauth/allianceauth!1169
2020-02-22 12:58:28 +00:00
ErikKalkoken
b9da6911e6 Fix Mumble search issue 2020-02-20 20:07:48 +01:00
ErikKalkoken
81f9211098 Fix update_main_character_model() bug 2020-02-20 17:16:28 +01:00
ErikKalkoken
8290081365 Fix missing import bug in UserAdmin, StateAdmin, add tests for those cases 2020-02-20 15:29:54 +01:00
ErikKalkoken
81af610c11 Add sorting to characters and groups and remove auto groups 2020-02-19 01:29:14 +01:00
ErikKalkoken
cfa2cf58f3 Fix issue #1219 2020-02-18 20:35:34 +01:00
ErikKalkoken
01c17d28f6 Extend tox setup to include core only testing 2020-02-18 19:34:44 +01:00
Ariel Rin
efd2a5e8c5 Version Bump v2.6.0 2020-02-18 08:54:31 +00:00
Ariel Rin
c437b00727 Merge branch 'feature_admin_update' into 'master'
Improve admin site lists for users, groups and service users

See merge request allianceauth/allianceauth!1164
2020-02-18 08:34:19 +00:00
Ariel Rin
5df0d1ddc6 Merge branch 'issue_1199' into 'master'
Change esi client loading to on-demand

Closes #1199

See merge request allianceauth/allianceauth!1165
2020-02-18 08:31:52 +00:00
Ariel Rin
c3521b0d87 Merge branch 'issue_1162' into 'master'
Make AA work on mobiles

Closes #1215 and #1162

See merge request allianceauth/allianceauth!1167
2020-02-18 03:35:44 +00:00
Ariel Rin
148916d35e Merge branch 'services-deprecation' into 'master'
Deprecate Market and Seat service

See merge request allianceauth/allianceauth!1168
2020-02-18 01:23:45 +00:00
Ariel Rin
06c7da944c Merge branch 'corpstats_mains' into 'master'
HOTFIX: Dont show out of corp mains with alt characters in corp as a main when they are not in corp.

See merge request allianceauth/allianceauth!1154
2020-02-18 01:21:54 +00:00
ErikKalkoken
f2ba741499 Add sorting to user for group, state for character, corporation, alliance and group for group user leaders, group group leaders 2020-02-18 01:35:17 +01:00
ErikKalkoken
0f9927686b Add new standard table style, improve UI for group management 2020-02-17 23:45:00 +01:00
ErikKalkoken
59855a71ef Fix layout bug and improve UI for permission tool, add filterDropDown JS 2020-02-17 20:16:30 +01:00
ErikKalkoken
fffb21bb4f Fix spr, permissions_tool, improve group_management 2020-02-17 00:59:47 +01:00
Ariel Rin
30bb6cdfab Deprecate Market and Seat service 2020-02-17 00:04:30 +10:00
ErikKalkoken
8771477884 Fix optimer, timerboard and corpstats 2020-02-15 23:26:22 +01:00
ErikKalkoken
55a5070691 Fix group management, improve dashboard 2020-02-15 22:49:48 +01:00
ErikKalkoken
1182b51e4b Fix services 2020-02-15 20:10:38 +01:00
ErikKalkoken
9976ecc2aa Fix login and dashboard for display on mobiles 2020-02-15 20:10:30 +01:00
ErikKalkoken
3bd8107fcf Move groupmgmt test to subfolder, add admin tests 2020-02-14 01:03:07 +01:00
ErikKalkoken
a48c67de5c Restructure Discord tests into folder and add admin testst 2020-02-13 23:20:22 +01:00
ErikKalkoken
bb0a7c014e Change to on-demand loading for debug and failed starts only 2020-02-13 16:08:07 +01:00
ErikKalkoken
80729b6b06 Performance improve admin tests, test w/o auto groups 2020-02-13 01:14:46 +01:00
ErikKalkoken
ff168d1c9e Add admin tests, some fixes 2020-02-13 00:40:41 +01:00
ErikKalkoken
331100370c Change esi client loading to on-demand in eveonline module 2020-02-12 16:32:11 +01:00
ErikKalkoken
47babf2ed7 Refactor common functions for creating admin user list, peformance tweaks 2020-02-08 00:51:13 +01:00
ErikKalkoken
c1388bf23f Adopt all services user and auth user lists to new format 2020-02-07 23:01:13 +01:00
ErikKalkoken
3f4dfe9b0b Move common service user list features into central admin class 2020-02-07 20:37:06 +01:00
ErikKalkoken
0caac20d77 Adding settings for users lists, showing all auto groups in groups 2020-02-07 17:21:33 +01:00
ErikKalkoken
9d0a65a516 Add tooltips to users, add CSS 2020-02-07 14:38:36 +01:00
ErikKalkoken
ab061ba7a6 Further improvements to admin site 2020-02-07 14:38:36 +01:00
ErikKalkoken
2d24d064d5 Improve admin site 2020-02-07 14:38:36 +01:00
ErikKalkoken
458685026b Add fields to discord and ts3 admin 2020-02-07 14:38:36 +01:00
ErikKalkoken
b0448a4565 Further improvements 2020-02-07 14:38:36 +01:00
ErikKalkoken
f902f59b31 Improve user and group admin lists 2020-02-07 14:38:36 +01:00
ErikKalkoken
2b8bfbe544 Improve user and group admin 2020-02-07 14:38:36 +01:00
Ariel Rin
564a25e578 Version Bump v2.5.1 2020-02-07 10:42:50 +00:00
Ariel Rin
f50b08d301 Merge branch 'fix_esi_duplicated_model_warning' into 'master'
Fix esi duplicate model warnings

See merge request allianceauth/allianceauth!1159
2020-02-07 05:01:30 +00:00
Ariel Rin
26566d9ce0 Merge branch 'issue_1200' into 'master'
Fix activation link shown trunctated in some mail clients

Closes #1200

See merge request allianceauth/allianceauth!1160
2020-02-07 04:58:52 +00:00
Ariel Rin
f42d438be2 Merge branch 'callback_redir_fix' into 'master'
HOTFIX: Callback redirect fix

See merge request allianceauth/allianceauth!1161
2020-02-07 04:55:19 +00:00
Ariel Rin
1fbc39b614 Merge branch 'django_min' into 'master'
HOTFIX: Bump Django minimum to work with AA migrations

See merge request allianceauth/allianceauth!1163
2020-02-07 04:50:39 +00:00
Aaron Kable
36af471c3c passthrough the redirect url 2020-02-07 04:50:31 +00:00
Ariel Rin
a5e86c9a36 Merge branch 'py38' into 'master'
Support and Test against Python3.8

See merge request allianceauth/allianceauth!1162
2020-02-07 04:49:17 +00:00
Ariel Rin
4b27dd40b9 Support and Test against Python3.8 2020-02-07 04:49:17 +00:00
AaronKable
ff0aac9d8a Bump Django minimum to work with AA migrations 2020-02-05 21:05:17 +08:00
Col Crunch
bf24c8253e Version Bump v2.5.0 2020-02-04 23:37:47 -05:00
colcrunch
fd92352302 Merge branch 'feature_grouprequest_infos' into 'master'
Add evelinks package and improve group management UI

See merge request allianceauth/allianceauth!1156
2020-02-05 04:31:13 +00:00
Erik Kalkoken
fcb66a11a3 Improve image tags to also work with eveonline objects, use new tags in group managenet, optimer, timerboard 2020-02-05 04:31:13 +00:00
Col Crunch
63d2021a73 Fix migration conflicts. 2020-02-04 23:23:04 -05:00
Col Crunch
d110d9c74e Merge branch 'fix_groupmanagement_requestlog_date'
Closes !1157
2020-02-04 23:15:30 -05:00
Col Crunch
157bf81dcb Change file name to keep it sequential, and add a descriptive title. 2020-02-04 23:14:32 -05:00
Col Crunch
1beb1b1b4f Merge branch 'group-purge' into 'master'
Closes !1147
2020-02-04 23:02:08 -05:00
colcrunch
13f523679c Merge branch 'group_leads' into 'master'
Add option to add groups as group leaders

Closes #1062

See merge request allianceauth/allianceauth!1146
2020-02-05 03:22:57 +00:00
Aaron Kable
ed816d9aea Add option to add groups as group leaders 2020-02-05 03:22:57 +00:00
colcrunch
ebfb51eed5 Merge branch 'eveonline_tests_1' into 'master'
Add more tests to eveonline module

Closes #1206

See merge request allianceauth/allianceauth!1151
2020-02-04 04:44:16 +00:00
Erik Kalkoken
0127b1ea9e Add more tests to eveonline module 2020-02-04 04:44:16 +00:00
colcrunch
61f41a1459 Merge branch 'nicer_readme' into 'master'
Improve README

See merge request allianceauth/allianceauth!1153
2020-02-04 04:43:26 +00:00
ErikKalkoken
d3fbc133a2 Fix issue #1200 2020-02-03 16:08:33 +01:00
ErikKalkoken
ce7a8e7a3d Replace swagger spec file in eveonline package with current version 2020-02-02 20:28:53 +01:00
ErikKalkoken
2b45610080 Fix date field in groupmanagement / RequestLog 2020-01-30 14:16:55 +01:00
AaronKable
5b4dd6731c tests for the test god 2020-01-28 16:22:43 +08:00
AaronKable
80157a032a Dont show Alt characters as a main when they are not in corp 2020-01-26 21:27:10 +08:00
ErikKalkoken
7b1bf9a4e2 Improve readme and add missing version info to setup 2020-01-24 20:05:55 +01:00
Col Crunch
337c4d9ce5 Version Bump to v2.4.0 2020-01-22 19:46:20 -05:00
colcrunch
9afc36a009 Merge branch 'corpstat_fix' into 'master'
Increase Corpstats Performance

See merge request allianceauth/allianceauth!1138
2020-01-23 00:38:39 +00:00
Aaron Kable
ebff1387c1 Increase Corpstats Performance 2020-01-23 00:38:39 +00:00
colcrunch
801502ec77 Merge branch 'celery_update' into 'master'
Update Dependencies

Closes #1175

See merge request allianceauth/allianceauth!1141
2020-01-23 00:34:35 +00:00
Aaron Kable
c07f59201e Update Dependencies 2020-01-23 00:34:34 +00:00
Col Crunch
98b799d821 Version Bump 2.3.0 2020-01-16 15:28:07 -05:00
Col Crunch
02714956d8 Add CI stage to deploy to pypi on tags. 2020-01-15 23:16:02 -05:00
colcrunch
4d435d58c5 Merge branch 'affiliations' into 'master'
Affiliations for Character updates

See merge request allianceauth/allianceauth!1140
2020-01-16 04:00:58 +00:00
Aaron Kable
1c2fd3be50 Affiliations for Character updates 2020-01-16 04:00:55 +00:00
colcrunch
6222439e21 Merge branch 'fleetup-removal' into 'master'
Fleetup removal

Closes #1179

See merge request allianceauth/allianceauth!1144
2020-01-16 03:54:18 +00:00
colcrunch
46d46ac90b Fleetup removal 2020-01-16 03:54:18 +00:00
colcrunch
a5fe61eb15 Merge branch 'issue-1172' into 'master'
Fix issue #1172: Replace deprecated eve image URLs in apps and services

Closes #1172

See merge request allianceauth/allianceauth!1145
2020-01-16 03:51:16 +00:00
Erik Kalkoken
0bfec36983 Fix issue #1172: Replace deprecated eve image URLs in apps and services 2020-01-16 03:51:16 +00:00
colcrunch
11607ecf24 Merge branch 'tests_eveonline_providers' into 'master'
Add unit tests for eveonline providers and fix coverage counting

See merge request allianceauth/allianceauth!1150
2020-01-16 03:49:16 +00:00
Erik Kalkoken
9970e5535b Add unit tests for eveonline providers and fix coverage counting 2020-01-16 03:49:16 +00:00
Basraah
99492e9c34 Merge branch 'issue-1198' into 'master'
Re-enable automatic testing against all Python versions

Closes #1198

See merge request allianceauth/allianceauth!1149
2020-01-10 06:55:19 +00:00
Erik Kalkoken
1d6ecffb3b Re-enable automatic testing against all Python versions 2020-01-10 06:55:19 +00:00
Aaron Kable
8c33349dcb Purge groups not available to member on state change 2020-01-07 02:41:18 +00:00
Basraah
cfb2c55a4b Merge branch 'issue-1177' into 'master'
Fix issue #1177: Add project description and classifiers to PyPI page

Closes #1177

See merge request allianceauth/allianceauth!1143
2019-12-13 02:14:54 +00:00
ErikKalkoken
e24d29f1d3 Fix issue #1177 2019-12-05 13:02:31 +01:00
Basraah
debd6ef2b9 Version bump to v2.2.2 2019-12-05 02:37:22 +00:00
Basraah
58e9c21e4f Merge branch 'issue-1176' into 'master'
Fix issue #1176: Prevent Django 3 installation

Closes #1176

See merge request allianceauth/allianceauth!1142
2019-12-05 02:36:03 +00:00
ErikKalkoken
c7c3083e3e Fix issue #1176: Prevent Django 3 installation 2019-12-04 14:02:03 +01:00
Basraah
63d061e9f2 Merge branch 'bug-1165' into 'master'
add fix and unittest for issue #1165

See merge request allianceauth/allianceauth!1137
2019-09-12 09:53:05 +00:00
ErikKalkoken
1887bdb90a add fix and unittest for issue #1165 2019-09-08 23:09:20 +02:00
Basraah
69addb068a Merge branch 'patch-3' into 'master'
Add discord warning for tasks

See merge request allianceauth/allianceauth!1136
2019-08-30 03:22:49 +00:00
Aaron Kable
a62c3ce0f9 Add discord warning for tasks 2019-08-30 03:22:49 +00:00
Basraah
aecc94bdb3 Version bump to v2.2.1 2019-08-25 03:20:45 +00:00
Basraah
fcb7f2905a Merge branch 'url_group' into 'master'
urlize group descriptions to hyperlink if required

See merge request allianceauth/allianceauth!1132
2019-08-24 22:48:57 +00:00
Basraah
34c7169ca3 Merge branch 'local_settings_update' into 'master'
Enable utf8mb4 charset option in local.py by default

See merge request allianceauth/allianceauth!1131
2019-08-24 22:47:25 +00:00
Basraah
6e450061f4 Merge branch 'group-rework' into 'master'
Make group joins a bit clearer

See merge request allianceauth/allianceauth!1134
2019-08-24 22:46:46 +00:00
Basraah
fc3d7e9f43 Merge branch 'auto-group' into 'master'
Fix Autogroups, Add Autogroups to admin

Closes #1087

See merge request allianceauth/allianceauth!1133
2019-08-24 22:45:25 +00:00
Aaron Kable
514db4f9a2 Make group joins a bit clearer 2019-08-23 03:54:24 -04:00
Aaron Kable
23a8b65ce2 Fix Autogroups, Add Autogroups to admin 2019-08-22 20:37:55 -04:00
Aaron Kable
f8e6662bc8 urlize group descriptions to hyperlink if required 2019-07-26 00:35:59 -04:00
Col Crunch
89be2456fb Update local.py to have the utf8mb4 charset option enabled in the database settings. This brings the settings inline with the documentation update. 2019-06-30 22:58:13 -04:00
Basraah
bfd3451717 Merge branch 'db_doc' into 'master'
Use utf8mb4 When Creating Auth DB

See merge request allianceauth/allianceauth!1130
2019-06-20 00:49:30 +00:00
Col Crunch
0b759d6a32 Use utf8mb4 when creating auth db rather than utf8 2019-06-19 16:27:18 -04:00
Basraah
65e05084e6 Merge branch 'master' into 'master'
Show "Help" link only to superuser

See merge request allianceauth/allianceauth!1129
2019-06-17 00:56:28 +00:00
Peter Pfeufer
f25a4ed386 Show "Help" lnk only to superuser 2019-06-15 23:44:36 +02:00
Basraah
b2a1d41829 Merge branch 'patch-1' into 'master'
Add Ariel Rin and Col Crunch as developers to readme

See merge request allianceauth/allianceauth!1128
2019-05-29 09:21:51 +00:00
Basraah
2741a92d31 Version bump to v2.2.0 2019-04-14 03:07:22 +00:00
Ariel Rin
3570ce86d7 Add Ariel Rin and Col Crunch as developers to readme 2019-03-17 05:39:43 +00:00
Basraah
d809902d1e Merge branch 'issue-templates' into 'master'
Create Issue Templates

See merge request allianceauth/allianceauth!1111
2019-03-12 22:16:00 +00:00
Ariel Rin
ec4232c00a Create Issue Templates 2019-03-12 22:16:00 +00:00
Basraah
dec793bfac Merge branch 'dependencies' into 'master'
Depencies fix

Closes #1150

See merge request allianceauth/allianceauth!1127
2019-03-12 20:50:00 +10:00
Basraah
fe3fe0527a Merge branch 'timer-change' into 'master'
Rename Citadels and FLEX Structures

See merge request allianceauth/allianceauth!1126
2019-03-12 10:06:53 +00:00
colcrunch
a8855e86ed Change Type [SIZE] notation for Citadels/EC/Refineries to structure names to reduce ambiguity.
Also, Added "Brand names" for the Beacon, Jammer, and Gate.
2019-03-12 10:06:53 +00:00
Basraah
179d1c38e6 Merge branch 'dj-2.1' into 'master'
Django 2.1 Compatibility

See merge request allianceauth/allianceauth!1124
2019-03-12 10:05:05 +00:00
colcrunch
287da73a4f Update StateBackend.authenticate to match ModelBackend
Also, change setup to no longer include Django 1.11
2019-03-12 10:05:05 +00:00
Basraah
e9ed917888 Merge branch 'fix_discourse_usernames' into 'master'
Send usernames as string instead of array

Closes #1149

See merge request allianceauth/allianceauth!1123
2019-03-12 10:00:52 +00:00
Basraah
70d842c971 Merge branch 'jokke_ilujo/Issue1146' into 'master'
Precent encode mumble username for connect link

See merge request allianceauth/allianceauth!1121
2019-03-12 09:58:07 +00:00
Basraah
bcda228e05 Merge branch 'mysql-docs-fix' into 'master'
Add Time Zone table instructions to Install Docs

See merge request allianceauth/allianceauth!1119
2019-03-12 09:56:20 +00:00
colcrunch
000dafc5e6 Add Time Zone table instructions to Install Docs 2019-03-12 09:56:20 +00:00
Basraah
4ea824fe71 Merge branch 'audit-log-update' into 'master'
Add datetime to Audit Log entries.

Closes #1134

See merge request allianceauth/allianceauth!1115
2019-03-12 09:53:43 +00:00
colcrunch
f72f539516 Add datetime to auditlog entries.
Also, change ordering, add pagination, and stripe the table for increased readability.

Action column now also reads "Removed" when a user is removed from a group. Note that this does not change anything on the back-end, so if you use this data for anything else, be aware that while the template is explicit, the data isn't as explicit.
2019-03-12 09:53:43 +00:00
Stephen Shirley
1b192a184f Send usernames as string instead of array
Fixes https://gitlab.com/allianceauth/allianceauth/issues/1149
2019-02-09 11:10:34 +01:00
Erno Kuvaja
0edf896b4c Precent encode mumble username for connect link
This change wraps mumble username on connect link forming with
urllib.parse.quote() to ensure that the username does not contain
unsafe reserved characters and gets passed properly to mumble.

Fixes Issue: #1146
2019-01-21 14:12:50 +00:00
Basraah
7dec4deb70 Version bump to v2.1.1 2018-12-20 22:17:04 +00:00
Basraah
d511221899 Merge branch 'flex-structures' into 'master'
Add Flex Structures to timerboard

See merge request allianceauth/allianceauth!1112
2018-12-20 22:06:01 +00:00
Basraah
d2b7de5221 Merge branch 'master' into 'master'
Update Readme and fix GitLab announcements

See merge request allianceauth/allianceauth!1113
2018-12-20 22:04:39 +00:00
colcrunch
79c5be02e2 Update Readme and fix GitLab announcements 2018-12-20 22:04:39 +00:00
Basraah
09df37438d Add missing order_by, thanks @Fundaris 2018-12-20 22:02:25 +00:00
Basraah
8561e4c6fd Merge branch 'corpstats-fix' into 'master'
update corpstats swagger.json

Closes #1139

See merge request allianceauth/allianceauth!1116
2018-12-20 21:49:35 +00:00
Aaron Kable
976cb4d988 update corpstats swagger.json 2018-12-20 08:27:56 -05:00
soratidus999
20f7d5103c Add FLEX to past timers as well as present 2018-12-01 17:43:35 +10:00
soratidus999
d049ec2832 Add Flex Structures to timerboard
New FLEX structures as per https://support.eveonline.com/hc/en-us/articles/213021829-Upwell-Structures

Cleaned up layout slightly and removed Stations as there are no longer any conquerable stations left
2018-12-01 17:26:51 +10:00
Basraah
00fe2a527e Version bump to v2.1.0 2018-11-30 07:56:54 +00:00
Basraah
f53ec3b43e Merge branch 'corputils_alliance_fix' into 'master'
None is not a valid alliance ID.

Closes #1122

See merge request allianceauth/allianceauth!1106
2018-11-30 07:54:51 +00:00
colcrunch
4d0417f114 None is not a valid alliance ID. 2018-11-30 07:54:51 +00:00
Basraah
00903b64db Merge branch 'github-to-gitlab' into 'master'
Update GitHub references to Gitlab

See merge request allianceauth/allianceauth!1110
2018-11-30 03:55:19 +00:00
Ariel Rin
a3038cad00 Update GitHub references to Gitlab 2018-11-30 03:55:19 +00:00
Basraah
ef99f1afac Merge branch 'services-market-deprecate' into 'master'
replace market docs with deprecation notice

See merge request allianceauth/allianceauth!1105
2018-11-29 23:55:37 +00:00
Basraah
cc00d4bd04 Merge branch 'services-phpbb3-docsupdate' into 'master'
Set default theme in PHPBB3, explain impact in docs

See merge request allianceauth/allianceauth!1107
2018-11-29 23:51:49 +00:00
Basraah
250f26ff6f Merge branch 'master' into 'master'
django-redis-cache does not support redis 3

See merge request allianceauth/allianceauth!1108
2018-11-29 23:51:22 +00:00
Basraah
62b786ca4a Merge branch 'zkill_fix' into 'master'
Fix srp url for zkill api

See merge request allianceauth/allianceauth!1109
2018-11-29 23:51:09 +00:00
Ariel Rin
9cfb47e658 Limit redis to 2.0 due to 3.0 incompatabilities 2018-11-29 23:12:11 +00:00
Mike
ccef27b637 Fix srp url for zkill api 2018-11-17 20:26:16 -05:00
soratidus999
8dee61fd39 Set default theme in PHPBB3, explain impact in docs
The default theme in PHPBB3 needs to be set or users cannot see the forum, this is needed because AA creates users without a theme set.

Docs explain impact and how to avoid when changing theme later on.
2018-11-15 17:45:33 +10:00
soratidus999
ae64bd0e19 replace docs with deprecation notice 2018-11-14 21:59:05 +10:00
Basraah
a75e93dbfc Version bump to v2.1b1 2018-11-14 10:55:04 +00:00
Basraah
0aa66c5729 Merge branch 'patch-1' into 'master'
limit django, django-celery-beat to compatible versions

See merge request allianceauth/allianceauth!1104
2018-11-11 08:06:39 +00:00
Ariel Rin
4c2434219d limit django, django-celery-beat to compatible versions 2018-11-11 01:55:13 +00:00
Basraah
8c65fda33b Fix incorrect hasattr 2018-10-09 23:58:35 +00:00
Basraah
14f2751bbb Fix typo 2018-10-09 19:44:11 +10:00
Basraah
d37a543c39 Update admin status to work with gitlab 2018-10-09 19:43:44 +10:00
Basraah
4947e0c483 Merge branch 'group_application_fix' into 'master'
Group Application Tweaks

See merge request allianceauth/allianceauth!1096
2018-10-09 05:15:30 +00:00
Basraah
f87c630b86 Merge branch 'fleetup-template-bootstrap-fix' into 'master'
Fixes for FleetUp templates

See merge request allianceauth/allianceauth!1100
2018-10-09 05:15:03 +00:00
Basraah
73789ea734 Prevent multiple migration leaf nodes 2018-10-09 02:15:01 +00:00
Basraah
5a16c9c495 Merge branch 'restrict_group_states' into 'master'
Restrict groups by state.

See merge request allianceauth/allianceauth!1095
2018-10-09 02:06:44 +00:00
colcrunch
9dd8357f67 Restrict groups by state. 2018-10-09 02:06:44 +00:00
Basraah
623e77a268 Avoid Dj2.1 until 1.11 depreciation issues are fixed 2018-10-07 22:45:12 +00:00
Basraah
73403b98df Merge branch 'srp_api_fix' into 'master'
Update SRP module for zKill API changes.

See merge request allianceauth/allianceauth!1102
2018-10-07 22:08:03 +00:00
colcrunch
7aa1a2f336 Update SRP module for zKill API changes. 2018-10-07 22:08:03 +00:00
Peter Pfeufer
08e42d2f56 Serving pilot avatar in fleetup character view from the right host 2018-10-06 13:02:26 +02:00
Peter Pfeufer
69248fd7bb Formatting additional informations text in operations 2018-09-29 10:34:41 +02:00
colcrunch
0af188c6ab Disallow applying to groups the user is already a member of 2018-08-29 21:28:14 -04:00
Peter Pfeufer
8b6d32d0d1 Removed unnecessary HTML comment 2018-08-11 11:09:16 +02:00
Peter Pfeufer
3c11c25d69 Corrected usage of Bootstrap classes in FleetUp templates 2018-08-11 11:03:36 +02:00
colcrunch
12e6cc63e8 Refine auto_leave check. 2018-08-05 03:34:16 -04:00
Basraah
d429c8b59a Grant srp.add_srpfleetmain access to create SRP request
See merge request allianceauth/allianceauth!1098
2018-08-05 02:24:27 +00:00
Basraah
ddd7a3551b Add Audit Log to Group Management
See merge request allianceauth/allianceauth!1089
2018-08-05 02:19:51 +00:00
Basraah
49ede92e06 Skip Teamspeak server admin groups and template groups
See merge request allianceauth/allianceauth!1093
2018-08-05 01:34:34 +00:00
Basraah
b813213328 Fix discourse group sync
See merge request allianceauth/allianceauth!1097
2018-08-04 00:06:30 +00:00
Loïc LEUILLIOT
14065b3ca9 Fix discourse group sync 2018-08-04 00:06:30 +00:00
Basraah
41f9dc490a Merge branch 'fix-fat-ship' into 'master'
Extend Ship Type field on FAT link.

See merge request allianceauth/allianceauth!1099
2018-08-03 04:57:24 +00:00
colcrunch
48d25ca73f Extend Ship Type field on FAT link.
Was not previously long enough for gold pods. Extended further for future-proofing.
2018-08-03 00:32:23 -04:00
Col Crunch
e49e04034c Imports are hard
Note to self: Read before commit
2018-08-01 23:00:31 -04:00
Col Crunch
c7860f8e5c oops 2018-08-01 22:50:44 -04:00
Col Crunch
adb982114a Actually use srp.add_srpfleetmain permission
Also adds a new @permissions_required decorator.
2018-08-01 22:38:54 -04:00
colcrunch
5b8983deac Rename Auditable group.
Its better to be explicit.
2018-07-22 20:08:16 -04:00
colcrunch
1730bc3b98 Add check for auditable groups.
To ensure functionality with other possible changes to group management.
2018-07-22 19:38:56 -04:00
Col Crunch
4374064d98 Case matters 2018-07-11 00:48:51 -04:00
Col Crunch
c1d7994045 Add setting to allow for unrestricted leaving of all groups. 2018-07-11 00:47:04 -04:00
Col Crunch
7bda367cc8 No need to allow more than one request. 2018-07-11 00:11:36 -04:00
Basraah
3de7a2ccd2 Version bump to v2.0.5 2018-07-10 02:27:32 +00:00
Basraah
9cc278df31 Merge branch 'corp_stat_fix' into 'master'
Corp Stats update to fix removal of character name endpoints

See merge request allianceauth/allianceauth!1092
2018-07-10 02:01:26 +00:00
Jamie McMillan
a0bab07e2f Fix indentation 2018-06-26 10:25:29 +01:00
randomic
149bbd92ca Skip server admin groups and template groups 2018-06-26 00:03:02 +01:00
Mike
1de3c989d7 fix tests with new endpoints (i think) 2018-06-24 17:41:27 -04:00
Mike
2e547945e2 Corp Stats update to fix removal of character name endpoints 2018-06-24 17:23:08 -04:00
Col Crunch
4d4a9a27af Merge remote-tracking branch 'allianceauth/master' 2018-06-20 16:14:02 -04:00
Basraah
5b41d0995f Update README.md badges 2018-06-06 07:12:10 +00:00
colcrunch
ab98d72022 Fix migration dependencies. 2018-06-06 07:03:57 +00:00
Unknown
8a7cd3f74d Merge remote-tracking branch 'allianceauth/master' 2018-06-06 03:00:03 -04:00
colcrunch
35cb56d6e9 Update 0009_requestlog.py 2018-06-06 06:41:56 +00:00
Basraah
a7a2ffd16b Add .gitlab-ci.yml 2018-06-06 05:46:35 +00:00
Basraah
dbeda324e0 Update tox.ini for GitLab CI 2018-06-06 05:45:23 +00:00
colcrunch
bf1fe99d98 Add Audit Log to Group Management 2018-06-04 01:45:44 -04:00
colcrunch
41429ec7c7 Merge pull request #3 from allianceauth/master
Sync
2018-06-03 20:59:17 -04:00
Adarnof
ee9ed13a66 Remove reference to depreciated bad_gateway model.
Addresses #1078

I too enjoy breaking changes with no warning. Round two.
2018-05-28 17:16:58 -04:00
Stephen Shirley
490ce286ff Add missing <tr> tags for discourse service template 2018-05-26 13:21:39 -04:00
Adarnof
099c2c0a21 Remove reference to depreciated x-user-agent header.
Addresses #1073

I too enjoy breaking changes with no warning.
2018-05-23 22:58:41 -04:00
Peter Pfeufer
46e15f7fa1 German translation corrected
At least the most obvious mistakes ...
2018-05-16 11:20:29 -04:00
Adarnof
6677e63e08 Correct resetting of permission key.
Thanks @Alf-Life
2018-05-11 10:55:56 -04:00
Adarnof
6d6a3a3d6b Allow viewing corpstats added by the user.
Order corpstats by corp name.
2018-05-10 14:25:57 -04:00
colcrunch
5006246cf1 Build TS perm key using State Information (#1044)
Build permkey with state group id
Pass user object to add_user instead of just username

Fixes #1043
2018-05-09 20:39:14 -04:00
Basraah
6187fb9b86 Timer JS fixes (#1054)
Add months to duration output
Update momentjs
Move EVE time generation function to shared source

Fixes timerboard showing EVE time as local time.
Changed to show 24 hour time.
2018-05-09 20:31:02 -04:00
Adarnof
86f57ccd56 Allow reversing service migrations.
This is probably the wrong way as we should really take care of removing the permission we added, but I don't see a reason anyone would need to migrate back that far as auth wouldn't work anymore without XML api (and even so newer installs don't have the settings referenced so permissions are not automagically added by the migration). So noop is bad but acceptable to me.

Thanks @mmolitor87
2018-05-08 10:06:58 -04:00
colcrunch
854096bac7 fix alliancelogo on corp stats page 2018-05-07 23:26:37 -04:00
Adarnof
9d2b3bb157 Include compiled messages.
It doesn't work without these if DEBUG is False. And users can't compile them outside the allianceauth source directory.

When editing translations, compile with: django-admin compilemessages --settings=allianceauth.project_template.project_name.settings.base
2018-05-02 21:42:26 -04:00
Adarnof
22bda62e59 Spanish translations courtesy of @frank1210
Fixed a few problems with translating the menu links - they had leading spaces.
2018-05-02 20:49:21 -04:00
colcrunch
c8ad1dcc7a Merge pull request #2 from allianceauth/master
sync
2018-05-02 20:01:42 -04:00
Adarnof
7212a7a328 Example supervisor config for authenticator. Ensure ICE is active in config. 2018-05-01 16:40:37 -04:00
Adarnof
f6b1b7b6bb Do not check mains when user has no profile.
This can occur when a user is being deleted: Django deletes the UserProfile, followed by the CharacterOwnerships which triggers the main check. As the user doesn't have a profile it explodes.

Thanks @Slevinator
2018-04-30 17:29:06 -04:00
Adarnof
53a9d72c4a Correct reversing states back to groups. 2018-04-30 17:24:31 -04:00
Adarnof
ca10fbcde5 Translate Member/Blue to custom state names.
Closes #1037
2018-04-25 17:20:28 -04:00
randomic
b4d33e5dfc Fix retry logic being suppressed by try block (#1035) 2018-04-24 11:53:13 -04:00
Adarnof
37bed989f1 Requires mariadb-shared for mysqlclient on centos.
Thanks @rlayne
2018-04-22 12:50:22 -04:00
Adarnof
507eda8a7d Version bump to 2.0 2018-04-21 20:44:15 -04:00
Adarnof
cbe67e9ebc Command to reset unverifiable main characters.
Include section in upgrade docs to run this command and the service account validation one.
2018-04-21 20:28:27 -04:00
Adarnof
cd38200506 Section for adding and removing apps.
People know how to add, but tend not to migrate to zero when removing leading to integrity errors.
2018-04-21 19:49:46 -04:00
Adarnof
5d5cf92a19 Remove sudo from docs.
Include section on logging DEBUG messages.
Cleanup some formatting.
2018-04-21 17:00:18 -04:00
Adarnof
98230d0ee3 Log but don't deal with problems refreshing tokens. 2018-04-20 14:15:19 -04:00
Adarnof
e47c04a0b0 Deactivate services when user loses main character.
This will prevent issues with service username formatting when access permissions are granted to the guest state. While users without mains cannot activate a service they could still retain an active account and it's possible to schedule a nickname update task which would subsequently error out.

Also it seems like a security issue if someone has a service account but their EVE character isn't known. cc8a7a18d2 prevented accessing the services page without a main, now this ensures users don't have an account to manage.
2018-04-20 13:28:41 -04:00
Adarnof
b65ccac58f Revoke CharacterOwnership on token deletion.
I'm pretty sure this is what I meant to do initially. I created the OwnershipRecord system under the assumption that CharacterOwnership models were being deleted when they could no longer be validated. That turned out not to be the case - only main characters were rest. This ensures they are deleted when they can no longer be validated.
2018-04-19 17:13:07 -04:00
Adarnof
bee69cc250 User is created inactive.
When users were created they started active, then were changed to inactive and saved. This triggered service account validation logic which is silly to be running on brand new users. I hated seeing those logging messages so now it doesn't happen.

At the same time I do love logging messages so I added some to the authentication process.
2018-04-19 17:10:38 -04:00
Adarnof
a350e175c7 Update to latest ESI routes. 2018-04-18 20:49:05 -04:00
Adarnof
2cd8188ffb Include a functional market nginx config.
Addresses #1021

Thanks @mmolitor87
2018-04-17 21:37:39 -04:00
Adarnof
b8a2d65a1d Create a separate doc page for upgrade from v1. 2018-04-17 20:59:08 -04:00
Ariel Rin
95f72c854d Minor Documentation Update (#1019)
Gunicorn needs to be run whilist in the folder for context, folder path is not enough
Correct static path, note to check nginx user
Capitalization of services and small typos
Service examples updated to their latest versions and download links
Expanded /var/www chowns for Nginx and Apache examples
Add in a troubleshooting note for no images being displayed (permissions issue) and gunicorn not execting (file path context)
Correct formatting. Reword a few parts. Remove "new in 1.15".
2018-04-17 18:55:18 -04:00
Adarnof
cd8bcfbbb5 Build from github to fix tests in py37-dj20
py37-dj111 still fails for some reason. The only difference in the problematic method between 1.11.12 and 2.0 is whitespace. It's fine in py37-dj20 which is all we really care about so I'm inclined to ignore that issue. py37-dj111 isn't even tested on Travis CI so its failure won't be a problem.

Both django-celery-beat and adarnauth-esi have put out releases supporting dj20 so it's not necessary to build from their source.
2018-04-17 17:40:08 -04:00
Adarnof
08f89d2844 Stop using task_self in bound tasks. 2018-04-17 16:21:43 -04:00
Adarnof
f3f156bf57 Use Django's cache framework for task keys.
Remove depreciated only_one decorator.

Prevent including task_self repr in key name.

Because some tasks are nested in a class, they use a task_self argument instead of the normal self which the celery_once package doesn't recognize to strip out.
2018-04-17 16:21:54 -04:00
Adarnof
73e6f576f4 Use celery_once to prevent repeat task queueing.
Prevent group updates from being queued multiple times per user.

Default graceful to prevent raising exceptions.
2018-04-17 16:21:43 -04:00
Adarnof
20236cab8a Use alliance ticker stored in character table. 2018-04-17 16:18:16 -04:00
Adarnof
6c7b65edad Record alliance ticker in character model.
Closes #1018
2018-04-17 16:18:16 -04:00
Adarnof
21782293ea Create missing Corp/Alliance models.
Thanks @Lof79
2018-04-17 12:08:39 -04:00
Adarnof
e52478c9aa Correct URL template tag.
Thanks @Peggle2K
2018-04-16 19:36:41 -04:00
Adarnof
319cba8653 Allow reconnecting characters to old users.
Addresses #1007
2018-04-14 15:13:42 -04:00
Adarnof
df3acccc50 Correct matching start of URL patterns.
Thanks @Peggle2k
2018-04-14 14:12:39 -04:00
Adarnof
19282cac60 Log messages from esi package. 2018-04-14 13:53:41 -04:00
Adarnof
933c12b48d Increase telnet timeout
Should help tolerate slower responses from remote servers.

Closes #751

Thanks @namenmalkav
2018-04-09 22:14:21 -04:00
Adarnof
8a73890646 Ensure ticker is fetched if alliance_or_corp used.
Closes #1011
2018-04-09 21:53:41 -04:00
Ariel Rin
d6df5184a6 Set minute for Cron to stop mass task creation (#1010) 2018-04-08 23:22:06 -04:00
Adarnof
91e1a374b4 Merge pull request #1009 from kormat/corpstats_comma
Remove trailing , from CELERYBEAT_SCHEDULE example.
2018-04-08 10:39:59 -04:00
Stephen Shirley
c725de7b5b Remove trailing , from CELERYBEAT_SCHEDULE example.
If a user copies the example verbatim, celery logs this error:
```
[2018-04-07 14:57:29,930: ERROR/MainProcess] Cannot add entry 'update_all_corpstats' to database schedule: TypeE rror('from_entry() argument after ** must be a mapping, not tuple',). Contents: ({'task': 'allianceauth.corputil s.tasks.update_all_corpstats', 'schedule': <crontab: 0 */6 * * * (m/h/d/dM/MY)>},)
```
2018-04-08 11:42:18 +02:00
Adarnof
ad1fd633b1 Ensure autogroups are removed if new state has config 2018-04-07 20:59:45 -04:00
Adarnof
ef9284030b Remove autogroups if no config for state. 2018-04-07 20:59:45 -04:00
Adarnof
89e5740027 Update autogroups on main character save
Closes #997
2018-04-07 20:59:45 -04:00
Adarnof
106f6bbcea Fix test user creation. 2018-04-07 20:59:45 -04:00
Adarnof
b53c7a624b Use queryset delete to purge non-refreshable tokens. 2018-04-07 20:49:14 -04:00
Adarnof
6fa788a8f9 Use libmysqlclient-dev on Ubuntu
`libmariadbclient-dev` is unavailable on Xenial (and the suggested replacement `libmariadb-client-lgpl-dev-compat` doesn't have the `mysql_config` symlink patch for whatever reason).

https://bugs.launchpad.net/ubuntu/+source/mariadb-client-lgpl/+bug/1575968
https://bugs.launchpad.net/ubuntu/+source/mariadb-client-lgpl/+bug/1546923
https://anonscm.debian.org/cgit/pkg-mysql/mariadb-client-lgpl.git/commit/debian/libmariadb-dev-compat.links?id=0bbbb8ea0bbeab4a6ebb1e62b92c1ca347061be4

Thanks @kormat
2018-04-07 13:39:20 -04:00
Adarnof
19f0788f47 Merge pull request #1002 from randomic/verify-email-option
Add setting for skipping email requirement
2018-04-03 21:15:38 -04:00
Adarnof
7767226000 Still collect emails from newly registered users.
Log in users immediately if no validation required.
Document new optional setting in project template settings file.
2018-04-03 21:09:43 -04:00
randomic
4eb6b73903 Nameformat configs which default to corp where alliance is None (#1003)
Add nameconfig format for alliance_or_corp_ticker
Add nameconfig format for alliance_or_corp_name
Update docs for new nameformats

Correct missing dict key if no alliance.
2018-04-03 19:25:47 -04:00
Adarnof
cb46ecb002 Correct mysql packages to mariadb on Ubuntu
Thanks @kormat
2018-04-03 15:11:05 -04:00
Adarnof
e694921fe6 Include mandatory DB package notice.
Thanks @zuiji
2018-04-03 13:57:26 -04:00
Adarnof
8266661855 Sanitize username on Discord user join.
Thanks @iakopo
2018-04-02 20:38:12 -04:00
Adarnof
cf7ddbe0e1 Set hostname to domain, not localhost.
Stop using sudo commands. Trust the user to handle permissions.

Closes #994
2018-03-23 11:16:59 -04:00
Adarnof
bdb3ab366f Group list API endpoint has moved.
Allow infinite group cache age.

Thanks @TargetZ3R0
2018-03-22 17:59:24 -04:00
colcrunch
1fc71a0738 Fix celerybeat task in ts3 config. (#998) 2018-03-22 15:43:50 -04:00
colcrunch
0b7520e3b1 Fix celerybeat task in ts3 config. 2018-03-22 15:23:52 -04:00
colcrunch
48c8ccfe97 Merge pull request #1 from allianceauth/master
sync
2018-03-22 15:20:48 -04:00
Adarnof
ad79b4f77c Correct logging string formatting. 2018-03-20 15:51:37 -04:00
Adarnof
fd876b8636 Correct model import.
Thanks @TargetZ3R0
2018-03-19 18:25:47 -04:00
Adarnof
21e896642a Stop using the patch method for setting roles.
Switch to dedicated add/remove endpoints.
Allow setting max cache age to None for infinite.

Apparently patch has issues.

Thanks @TargetZ3R0 and Discord devs <3
2018-03-19 18:08:24 -04:00
Adarnof
b4c395f116 Don't force token updates on main character checks. 2018-03-15 19:41:11 -04:00
Adarnof
a38116014d PyCharm defaults to venv 2018-03-15 19:38:52 -04:00
Adarnof
54223db1e9 Merge remote-tracking branch 'origin/patch1' 2018-03-15 19:36:49 -04:00
Adarnof
8a897abc7b Ensure service URL has protocol.
Thanks @jdrc
2018-03-11 01:08:02 -05:00
Adarnof
fe7b078ec8 Wait until token is deleted before assessing ownerships.
Hopefully this will fix the infinite recursion.
Elevate logging messages to Info so they appear in logs with the default configuration.
2018-03-09 11:47:28 -05:00
Adarnof
ce66bdcbd4 Copy v1 database after creating new one for v2 if updating. 2018-03-02 20:18:23 -05:00
Adarnof
f65e563c0c Update project setup description to match repo and docs.
Thanks @soratidus999
2018-03-02 11:26:35 -05:00
Adarnof
e860ba6c22 Remove pre-v1.13 changelog. It's on the wiki. 2018-03-02 04:03:01 -05:00
Adarnof
50b6605a43 Set folder permissions once user is created.
Remove redundant gunicorn webserver config.

Closes #984
2018-03-02 03:37:14 -05:00
Adarnof
d181200642 Mention Discord bot will never come online.
Update phpbb3 version.
2018-03-02 03:12:36 -05:00
Adarnof
386ba25a44 Add explanation of auth project structure.
Update troubleshooting guide for v2.
Remove homoglyph data files included with confusable-homoglyphs>=3.0
2018-03-02 02:58:55 -05:00
Adarnof
5331d194df Instruct selection of only necessary SSO scopes.
Standardize instructions of adding app to settings.
2018-03-02 01:52:16 -05:00
Adarnof
814ecd233e Most apps use menu item hooks now. 2018-02-28 16:49:39 -05:00
Adarnof
f9a8ac4e9b Bump version to b3 in anticipation of next release. 2018-02-28 13:20:36 -05:00
Adarnof
1bd5eecd54 Correct old template URLs.
Remove redundant name from fatlink.
Remove optimer app dependency.
And other general cleanup.

Thanks @TargetZ3R0
2018-02-28 13:16:54 -05:00
Adarnof
2fa1d9998d Handle custom table prefixes on service databases.
Closes #987

Thanks @Ric878
2018-02-28 10:56:30 -05:00
Adarnof
9d9cfebd9e Specify character set in database to avoid key length errors.
Default database setting uncommented.

Addresses #985
2018-02-27 19:56:19 -05:00
Adarnof
cc8a7a18d2 Hook URLs require logged in user with a main character.
Should prevent anything else like #983

Heavily inspired by https://gist.github.com/garrypolley/3762045#gistcomment-2089316
2018-02-26 22:50:58 -05:00
Adarnof
552c795041 Update project description. 2018-02-24 01:46:53 -05:00
Adarnof
3d757e8d90 Make sure wheel is in venv.
It's hit-and-miss when venvs are created it seems. Doesn't hut to install even if it's already there.
2018-02-24 01:26:31 -05:00
Adarnof
1b5ecaed80 Requirement to sign license agreement for contributing.
Restructure user lists.
Remove reference to long-dead alliance in description.
2018-02-24 00:13:47 -05:00
Adarnof
77c93ed96b Correct broken template tags. 2018-02-23 22:23:42 -05:00
Adarnof
3eeed99af2 Basic fleetup instructions including settings. 2018-02-23 21:47:55 -05:00
Adarnof
a143dfbb37 Add Timerboard Structures, step 2 (#976)
Added additional labels for added structure types
(cherry picked from commit d8f4d56dd8)
2018-02-23 21:36:57 -05:00
Adarnof
6b1da3b18a Briefly document the state system.
Ensure add and delete permissions are created. Not sure why I prevented them - maybe a holdover from an earlier iteration of the state system?
2018-02-23 21:34:48 -05:00
Adarnof
f0894f3415 Update group management docs showing merged admin pages.
Give groupmanagement app a more friendly display name.
2018-02-23 21:01:34 -05:00
Adarnof
539295c1b7 Remove unpopulated first/last name fields from list display.
Include only useful information in list display.
2018-02-23 20:33:07 -05:00
Adarnof
54f91a5bfb Simplify admin inline titles.
More descriptive name format config admin list.
2018-02-23 20:25:59 -05:00
Adarnof
f3c0d05c39 Embed authgroup into group admin.
Mirror authgroup admin permissions from group model.
Delete authgroup permissions.
2018-02-23 18:21:58 -05:00
Adarnof
9f9cc7ed42 Embed profile into user admin.
Restrict main character choices to non-main characters or current main.
If superuser, allow choosing any non-main character.
Proxy user permissions to base model.
Allow all staff to see permission list but not edit.
2018-02-23 17:58:14 -05:00
Adarnof
814b2da0ca Redirect all signals from admin proxy models. 2018-02-23 14:44:12 -05:00
Adarnof
7a9bb0c84b Centralize portrait/logo URL creation. 2018-02-23 12:54:21 -05:00
Adarnof
36ae2af29b Deduplicate login tokens. 2018-02-23 12:25:06 -05:00
Adarnof
d192f23e6e Require exactly django-registration==2.4
This is the newest version which allows installation on Django 2.0 (and indeed does work) that also provides the scheme context to emails.
2018-02-23 11:57:06 -05:00
Adarnof
67cd0cd55c Reassess user groups on state change. 2018-02-23 01:50:03 -05:00
Adarnof
9e53d8b429 Correct migration dependency.
I have no idea what 0016 is, but I'm nuking my dev env to be safe.
2018-02-23 01:26:59 -05:00
Adarnof
f5abf82b95 Allow mapping of states to Teamspeak3 groups.
Addresses #950

Happy now, @colcrunch ?
2018-02-23 01:22:51 -05:00
Adarnof
8dd3a25b52 Remove mentions of no longer used invite code. 2018-02-22 18:50:59 -05:00
Adarnof
d0aa46db08 Accept any ordering of groups in test.
I have no idea why the order is reversing itself. Doesn't matter for functionality which of them comes first. This just checks they're both in there and a comma to separate them.
2018-02-22 18:10:24 -05:00
Adarnof
f0ff70566b Include expected state group in test. 2018-02-22 18:01:14 -05:00
Adarnof
efecf5113b Correct celery eagerness during tests.
I have no idea why this setting name has to be changed. The docs for Celery 4.1.0 (installed) indicate it should be called CELERY_TASK_ALWAYS_EAGER - even with namespace removal TASK_ALWAYS_EAGER doesn't work, but the "old" name of CELERY_ALWAYS_EAGER does.
2018-02-22 18:00:03 -05:00
Adarnof
980569de68 Do not attempt to serialize User models 2018-02-22 17:54:35 -05:00
Adarnof
9c74952607 Correct CorpStats tests. 2018-02-22 17:44:59 -05:00
Adarnof
70c2a4a6e4 Use new endpoint for adding Discord users.
Closes #974
2018-02-22 17:41:38 -05:00
Adarnof
99b136b824 Delete Discord users if they've left the server.
Closes #968
2018-02-22 15:37:29 -05:00
Adarnof
ae4116c0f6 Create new role with desired attributes in one call. 2018-02-22 15:22:35 -05:00
Adarnof
3080d7d868 Prevent new roles from being sorted separately.
Addresses #969
2018-02-22 14:43:59 -05:00
Adarnof
08cf8ae1d6 Capture permission changes from proxy model on admin 2018-02-22 14:28:23 -05:00
Adarnof
3ed0f873f3 Capture signals sent by admin proxy models.
This will prevent those weird missing UserProfile and AuthGroup errors.
Add logging to authentication signals.
Correct reverse migration authservicesinfo creation.
Rename proxy models so they look better on the admin site.
2018-02-22 14:25:43 -05:00
Adarnof
5060d3f408 Ensure login tokens always get attached to the user. 2018-02-22 13:40:02 -05:00
Adarnof
ef24bea562 Put missing logout redirect setting back.
Not sure what I did with it.
2018-02-22 13:31:50 -05:00
Adarnof
c18efaa33d Default login scopes to publicData
We need a refresh token to monitor character ownership but don't need any scopes explicitly. publicData provides no private information but grants a refresh token.

https://github.com/ccpgames/sso-issues/issues/17

Rumor has it this scope isn't going away with CREST.

adarnauth-esi will automatically create a new scope model when it encounters one it doesn't recognize.
2018-02-22 13:27:47 -05:00
Adarnof
b6b14f6f1c Ensure all columns are perfectly aligned.
Prevent sorting/searching portraits and killboard links.
Default sorting to character names.
2018-02-22 13:06:07 -05:00
Adarnof
a90a52f426 Ensure api backoff returns result of decorated function
(cherry picked from commit 91ec924acc)
2018-02-22 02:10:27 -05:00
Adarnof
bd5ea38446 Add a warning against editing base.py
Beautify local.py by removing big block comments.
Move some settings back to base.py which don't need to be in local.py
2018-02-21 22:32:23 -05:00
Adarnof
f8248f46e5 Update docs to reflect refreshing changes 2018-02-21 22:08:45 -05:00
Adarnof
b09c454bf0 Can be updated by any user who can view
Thanks @ghoti
2018-02-21 22:02:46 -05:00
colcrunch
d825689da4 Add settings section to service docs. Remove references to settings.py. (#942)
Standardized the addition of settings instructions.
Changed all references of local.py to a more generic 'auth project settings file'.
Included basic apache and nginx configs.
Include database creation steps.
Instruct users to restart gunicorn and celery after altering settings.
Include missing TS3 celerybeat schedule.
2018-02-21 20:39:54 -05:00
Adarnof
a64dda2a2e Handle HTTP429 on nickname API endpoint
Closes #971
2018-02-21 17:47:20 -05:00
colcrunch
8ce8789631 Discord Sanitization Removal (#947)
No need to sanitize, just prune.
2018-02-21 17:40:41 -05:00
Adarnof
2b2f367c30 Updated Strucure Choices
Added Refineries, and a Moon Mining Option
Also changed spacing to be consistent and be easier to read
(cherry picked from commit 0474fa6d17)
2018-02-21 17:23:36 -05:00
Adarnof
4d194457d8 Include state in service group sync.
The "empty" group will never appear as all users have a state so it has been removed.

I haven't yet found a good way to apply this to Teamspeak - perhaps go back to the token generation logic and create one with a user's state instead of "Member" and exempt those names from group sync?

Addresses #950
2018-02-21 17:11:22 -05:00
Adarnof
6f7cf8805d Correct background resizing on Firefox
https://stackoverflow.com/a/24104710
2018-02-20 15:29:56 -05:00
Adarnof
36e39503c8 Use symbolic links for supervisor conf 2018-02-20 14:46:28 -05:00
Adarnof
e7a24c9cd4 Explicitly forbid logging in as allianceserver 2018-02-20 13:07:16 -05:00
Adarnof
bd8a8922cc Detailed superuser main character instructions 2018-02-20 12:52:00 -05:00
Adarnof
396b2e0fb6 Select all esi scopes when registering application
Thanks @RacerX330
2018-02-20 12:35:11 -05:00
Adarnof
36e382fadb Move SSL header instructions to SSL block 2018-02-20 12:29:12 -05:00
Adarnof
d2666f2440 Instructions for accessing superuser account 2018-02-20 12:20:07 -05:00
Adarnof
397ca97f0f Add missing context to teamspeak join template.
Closes #967
2018-02-13 18:07:34 -05:00
Adarnof
631bb439a4 Remove celery setting namespace.
Somehow it prevents celerybeat tasks from being registered.
Doesn't work with or without the namespace prefix on CELERYBEAT_SCHEDULE

Thanks @warlof
2018-02-12 21:55:19 -05:00
Adarnof
a4003e188e Correct string formatting format
Thanks @warlof
2018-02-12 18:53:23 -05:00
Adarnof
f4a9ba2db8 Remove reference to deleted function. 2018-02-09 01:16:27 -05:00
Adarnof
895a62c475 Include leading http(s) on activation link.
Closes #961
2018-02-09 01:11:35 -05:00
Basraah
ac5a0d9dcb Remove obsolete function call 2018-02-04 19:15:10 +10:00
Adarnof
b8644d5c93 Remove unnecessary next URL from lang select.
This is automatically determined by the lang select view if not specified.
https://docs.djangoproject.com/en/2.0/topics/i18n/translation/#the-set-language-redirect-view
Closes #958
2018-02-02 19:55:59 -05:00
ghoti
4d8baf1af0 V2 Fix redirect issues in HRApps (#951)
Fix redirect issues in HRApps
Allow HR managers to delete reviewed apps
2018-01-11 19:06:10 -05:00
Basraah
f70987de09 Version bump 2018-01-09 12:38:49 +10:00
Basraah
9d02b1530c Update contributors 2018-01-09 12:34:21 +10:00
Basraah
3d532dae01 Fix celery in tests
There's actually a better way to structure tests involving celery since
4.0, but that can wait for some time in the future.
2018-01-09 12:11:54 +10:00
Adarnof
02247b067f Include INSTALLED_APPS setting
Clean up section headers and add a few more.
2018-01-08 10:50:24 -05:00
Basraah
63c2668171 Change static path to match default install 2018-01-08 21:15:19 +10:00
Basraah
5575039126 Remove obsolete section on automatic groups 2018-01-08 21:07:29 +10:00
Basraah
f97c8f2ce4 Fix issue causing queue length query to hang 2018-01-08 18:20:29 +10:00
Adarnof
6baab1d006 New apache guide for proxying to wsgi worker 2018-01-08 00:52:10 -05:00
Adarnof
17adf04860 Correct CentOS supervisor config folder.
Install gunicorn before starting auth project.
2018-01-08 00:14:17 -05:00
Adarnof
f871ecb425 Namespace celery settings to correct broker issues 2018-01-07 19:50:34 -05:00
Adarnof
4a425cde78 Set the email from address for SparkPost
Thanks @colcrunch
2018-01-07 19:39:27 -05:00
Basraah
f56252b0cc Fix celery broker url config 2018-01-06 12:16:30 +10:00
Basraah
7ae6c66beb Fix registration of services signals 2018-01-06 12:08:53 +10:00
Basraah
be90fb96ea Remove unnecessary param 2017-12-28 10:18:50 +10:00
Basraah
dd3350b169 Fix field name 2017-12-28 09:20:11 +10:00
Basraah
cdd1ba1fe3 Use coerce to allow PEP440 to partially work 2017-12-23 15:05:51 +10:00
Basraah
10ea12c867 Prevent error on bad version 2017-12-23 14:44:26 +10:00
Basraah
e6358d948a Fix bug preventing users being added to alliance autogroups
Will need `python manage.py migrate eve_autogroups zero` before updating
2017-12-23 14:43:05 +10:00
Adarnof
1101572f78 Set token user to allow ownership creation 2017-12-22 12:20:55 -05:00
Adarnof
0cf8836832 Use primary key for FK assignment 2017-12-22 11:32:45 -05:00
Adarnof
6e4562b0e6 Don't rely on manager in migration 2017-12-22 11:19:53 -05:00
Basraah
856f1e176a Fix copy paste error, add extra debug output 2017-12-22 11:35:49 +10:00
Adarnof
1653a57e7b Do not set request main character
Thanks @GhostMB
2017-12-21 19:28:39 -05:00
Adarnof
5f03e580c2 Make manager available in migrations
Thanks @mmolitor87
2017-12-21 15:37:54 -05:00
Adarnof
d370ae48a2 Full import path
Python doesn't want to play nice.
2017-12-21 15:32:42 -05:00
Adarnof
38baeba254 Load signals with app 2017-12-21 15:25:53 -05:00
Adarnof
478f9b9390 Fix typo
Thanks @mmolitor87
2017-12-21 15:20:20 -05:00
Adarnof
82ad3821c4 Avoid using model property in migration
https://stackoverflow.com/a/3315547

Thanks @mmolitor87
2017-12-21 15:15:01 -05:00
Adarnof
07afaf12d5 Fix bracket.
Thanks @mmolitor87
2017-12-21 14:13:16 -05:00
ghoti
2c98cbd020 Sort completed HR apps by create date (most recent first) (#930) 2017-12-20 18:03:27 -05:00
Adarnof
1fe9d18a1a Include confusable_homoglyphs data files.
django-registration currently requires confusable_homoglyphs~=2.0 which has problems retrieving these data files. Version 2.3.1 is expected to update the dependency to 3.0 which includes these with the package.
2017-12-15 12:43:31 -05:00
Adarnof
02a75a931a Correct corp_id argument 2017-12-15 11:28:54 -05:00
Adarnof
af7a432f29 Include basic app installation instructions. 2017-12-14 18:01:47 -05:00
Adarnof
770aca923f Reorder migrations to allow updating from v1.15.6 2017-12-14 17:48:54 -05:00
Adarnof
d50a74c7c6 Correct project template celery usage. 2017-12-14 10:20:34 -05:00
Adarnof
fcd8554ea7 Define additional arguments for startproject
Allows project creation with Django 2.0
2017-12-14 00:24:22 -05:00
Adarnof
98da0723fc Correct install procedure for gunicorn.
Correct docs index links.
2017-12-14 00:24:10 -05:00
Basraah
f037d7fea6 Change celery tasks to shared task decorator 2017-12-11 00:26:07 +10:00
Basraah
676e68a2bb Update travis config 2017-12-04 13:30:23 +10:00
Basraah
c82d9c7722 Fix migration issue 2017-12-04 13:21:29 +10:00
Basraah
70c4b17518 [v2] Alliance and Corp Autogroups (#902)
* Add pseudo foreign keys to EveCharacter model

* Ensure populate alliance is called on create

* Add unit tests for model

* Add extra signal for state removal/addition

* Add unit tests for signals

* Add tests for manager

* Add migrations

* Add sync command to admin control

* Prevent whitespace being stripped from group names

* Add documentation
2017-12-04 12:52:05 +10:00
Adarnof
995a44de0a Case-insensitive group name to ID translation
Seems Discourse won't let you create `Group` if `group` already exists (`422 Name has already been taken`).

Thanks @huberfe
2017-11-17 18:13:18 -05:00
Adarnof
4f5fc18c66 Merge branch 'v2-dev-master-merge' of https://github.com/allianceauth/allianceauth into v2-dev 2017-11-17 18:00:30 -05:00
Adarnof
4a94f379b4 Correct issues with the SRP merge from v1 2017-11-17 17:29:01 -05:00
mmolitor87
fb4651b11f Change name of location of gunicorn log. (#920) 2017-11-15 13:51:47 -05:00
phaynu
f961db3130 Extending Choices for Questions in hrapplications to Allow Multiselect (#911)
An additional field at the question level defines whether the choices for the question are multi-select or not. The template will render the choices with radio buttons or checkboxes depending on multi-select. Multiple selected choices are saved with a line break between them.
2017-11-10 12:07:17 -05:00
Adarnof
c63464c4c9 Handle new zKillboard API format 2017-11-10 11:57:51 -05:00
Adarnof
cca8b26375 Handle FAT ZeroDivisionErrors 2017-11-10 11:52:22 -05:00
Adarnof
cd0afeca15 Disable SeAT accounts instead of deleting. (#915)
See eveseat/web@1abb402
2017-11-03 19:29:39 -04:00
Basraah
86362bb0dd Refactor mumble service (#914)
* Added in_organisation check to EveCharacter model

* Basic name formatter

* Switch mumble service to use name formatter

* Squash services migrations

* Add name to example service to allow it to be used in tests

* Add name formatter to services

* Add abstract views, model, form for services modules

* Refactor mumble service to new style

* Don't set credentials if setting a provided password

* Add success message to set password view
2017-11-03 16:52:45 -04:00
Adarnof
c4979a22dd Merge pull request #913 from Adarnof/sso_registration
Determine paths to commands using shutil
2017-10-25 19:20:36 -04:00
Adarnof
859c5b3fa1 Determine paths to commands using shutil
Change context names to avoid colliding with base command pythonpath option
2017-10-24 12:48:23 -04:00
Basraah
57a26b90cb Admin status panel for dashboard (#903) 2017-10-18 15:46:58 +10:00
Basraah
7fa45fa471 Add favicons and logo 2017-10-12 13:18:51 +10:00
Basraah
47b5b286d8 Uniform page titles 2017-10-12 10:20:40 +10:00
Basraah
ef37cb3ea5 Name generator/formatter (#897)
* Squash services migrations

* Add name to example service to allow it to be used in tests

* Add name formatter to services

* Add documentation
2017-10-11 12:34:31 +10:00
Adarnof
b95bb9aa6a Merge pull request #891 from Adarnof/sso_registration
Install using project template
2017-10-10 17:09:08 -04:00
Adarnof
cfad4fa8a6 Pycharm refactor failed me. 2017-10-10 10:41:25 -04:00
Adarnof
3f33485ca9 Template supervisor conf.
Update docs to reflect simple installation.

Ensure allianceauth celery workers can find config file for development (doesn't work as celeryapp.py).
2017-10-10 00:29:57 -04:00
Adarnof
1c1dfde2c4 Mention database migrations in update docs. 2017-10-05 15:06:34 -04:00
Adarnof
86e92941df Move default logging directory. 2017-10-05 14:35:25 -04:00
Adarnof
09b788fef6 Remove legacy settings documentation.
Settings are self-documented in the new install template.

Services install docs should be updated to indicate settings need to be added and how to configure them.
2017-10-05 12:50:51 -04:00
Adarnof
d54d80b0b8 Include updating instructions.
Tweak wording of install sections.
2017-10-05 12:48:33 -04:00
Adarnof
3ff29ba3b0 Do not check coverage of bin commands. 2017-10-05 11:08:43 -04:00
Adarnof
2efb45943c Do not check coverage of project template. 2017-10-05 11:00:32 -04:00
Adarnof
d43b8cf0e5 Update install docs to reflect new magical commands. 2017-10-05 03:10:27 -04:00
Basraah
5e8b5e1a15 Add files required for confusable_homoglyphs
Recreating them in CI was causing intermittent build stalls that were
almost impossible to debug.
2017-10-05 15:54:49 +10:00
Adarnof
03447abf5c Update base settings with command. 2017-10-05 01:23:33 -04:00
Adarnof
c22d3a9967 Command line utility to create project.
Shamelessly stolen from wagtail.
2017-10-05 00:52:55 -04:00
Adarnof
5df3672f3b Merge branch 'v2-dev' of https://github.com/allianceauth/allianceauth into sso_registration 2017-10-05 00:36:37 -04:00
Adarnof
c68574efc3 Create project template for deployment. 2017-10-05 00:33:18 -04:00
Basraah
e5d76cbce8 Add night mode (#890)
* Split base template menus out

* Add toggle for night mode setting

* Add vertical flexbox row

Makes each col-* expand to fill the space created by the largest column.

* Use flexbox on main character and group panels

* Fix data table sort control clipping

* Build new css
2017-10-05 14:32:58 +10:00
Adarnof
f121ed4062 Merge branch 'v2-dev' of https://github.com/allianceauth/allianceauth into sso_registration 2017-10-04 23:43:57 -04:00
Adarnof
111105d48b Restructure settings (#882)
* Refactor settings into importable base.

User deployment should use 'from allianceauth.settings.base import *' in their project settings.

* Restructure tests folder.
Remove DOMAIN setting.
Use localhost for services revoked emails.

* Add missing python3.4 typing requirement
typing is included python3.5+

* Remove legacy setup comments
Remove legacy update script
2017-10-05 13:12:49 +10:00
Basraah
d7cb1a2fab Use django-webtest for hanging tests instead of TestCase 2017-10-02 00:06:06 +10:00
Basraah
01d34b54eb N+1 query fixes 2017-10-01 20:55:15 +10:00
Basraah
f33f796421 Remove obsolete template tag 2017-10-01 19:23:43 +10:00
Basraah
0504be2441 Fix redirect URLs 2017-10-01 19:23:25 +10:00
Adarnof
93eca76bf8 Remove legacy setup comments
Remove legacy update script
2017-09-28 23:05:43 -04:00
Adarnof
b3d02b0c37 Add missing python3.4 typing requirement
typing is included python3.5+
2017-09-28 22:33:17 -04:00
Adarnof
67ff9eb379 Merge branch 'v2-dev' of https://github.com/allianceauth/allianceauth into sso_registration
Conflicts:
	alliance_auth/settings.py.example
	test_allianceauth/settings.py
2017-09-28 20:40:02 -04:00
Adarnof
cd6963daa6 Restructure tests folder.
Remove DOMAIN setting.
Use localhost for services revoked emails.
2017-09-28 20:37:33 -04:00
Adarnof
44de49cbb0 Refactor settings into importable base.
User deployment should use 'from allianceauth.settings.base import *' in their project settings.
2017-09-28 17:28:02 -04:00
Basraah
8864afd784 Resolve Dj20 compatibility issues
Temporarily use latest snapshot for esi for dj20

    Correct admin urls include

    Manually correct old ts3 table migration

    Remove obsolete services migrations

    Remove bootstrap pagination package

    Fix url namespacing
2017-09-28 14:47:06 +10:00
Basraah
51e4db73f0 Add latest django-celery-beat snapshot for Dj2.0 testing 2017-09-28 12:33:46 +10:00
Basraah
11fca74fec Add required on_delete parameters for Dj2.0 compatibility 2017-09-28 12:32:33 +10:00
Basraah
fff2cd32d5 Update timerboard views
Adds basic unit tests
Changed to class based views
2017-09-27 19:12:07 +10:00
Basraah
d993a299ef Add pseudo foreign keys to EveCharacter model 2017-09-26 15:13:41 +10:00
Basraah
650408f61c Unit test tweaks 2017-09-26 15:13:37 +10:00
Basraah
ef26cdbbee Improve support for milliseconds backoff 2017-09-26 09:14:58 +10:00
Adarnof
168ab569b9 Increase tested retry after
Apparently tests take longer than 200ms to evaluate here.
2017-09-26 09:14:51 +10:00
Adarnof
b418abc7c8 Retry after in milliseconds
Closes #874
2017-09-26 09:14:32 +10:00
Basraah
280ddb336e Change to use tox to manage unit tests 2017-09-25 17:25:28 +10:00
Basraah
d93f36a180 Ensure populate alliance is called on create 2017-09-25 10:15:13 +10:00
Basraah
941bcd3cd1 Fix allow failures format 2017-09-23 18:47:06 +10:00
Basraah
7591db3168 Add experimental support for Dj2.0 2017-09-23 18:40:25 +10:00
Basraah
e68c840dad CSS Fixes and cleanup
Improves bootstrap compatibility, allowing for dropping in theme CSS.
Also improves responsive design but still has a ways to go.
2017-09-23 18:03:43 +10:00
Basraah
45f7b7d962 Fix broken query 2017-09-23 17:50:53 +10:00
Adarnof
1bf8b6079d Load receivers on ready 2017-09-22 22:49:14 -04:00
Basraah
c59565c038 Removed unused context processor vars 2017-09-23 11:33:06 +10:00
Basraah
53d7916772 Move context processor 2017-09-23 11:32:32 +10:00
Basraah
7beec38881 Move templates and fix namespacing
Move base template

    Refactor services urls and templates

    Refactor groupmanagement urls and templates

    Refactor notifications urls and templates
2017-09-23 06:48:51 +10:00
Basraah
b130cc6c8e Update install docs 2017-09-22 09:57:58 +10:00
Basraah
f8488208fb Documentation updates 2017-09-21 18:07:01 +10:00
Basraah
2efaf40370 Update readme 2017-09-21 16:21:25 +10:00
Basraah
75b99148c6 Remove vagrant config
It's very badly out of date making it very evident it' no longer being
used.
2017-09-21 16:16:39 +10:00
Basraah
f36b038010 Remove EveManager, refactor into model managers
Lots of unused methods removed.
Unit tests added for those that are left.
2017-09-21 14:56:40 +10:00
Basraah
f84de28338 Remove seat API sync 2017-09-21 14:56:21 +10:00
Adarnof
2d92cd6cb2 Minimize swagger spec files. 2017-09-20 01:29:16 -04:00
Adarnof
1a8d163d45 Remove XML provider.
Remove caching provider - ESI calls are cached according to expiry headers now.
Remove evelink as a requirement.
Initialize API response objects using kwargs.

I won't miss XML.
2017-09-19 20:55:26 -04:00
Basraah
954f36dae6 Remove services migrations 2017-09-20 08:11:51 +10:00
Basraah
76ab835347 Add base app, remove explicit template loading 2017-09-20 07:42:47 +10:00
Adarnof
5f4e873f4a Merge pull request #870 from ghoti/v2-dev
settings.py.example fixes
2017-09-19 10:29:39 -04:00
Mike
889fcfd0e0 settings.py.example fixes 2017-09-19 05:00:05 -04:00
Basraah
0326836544 Migrate mumble authenticator to its own repo (#869) 2017-09-19 12:27:14 +10:00
Basraah
18f64b0357 Remove IPBoard3 support (#868) 2017-09-19 11:35:35 +10:00
Basraah
786859294d Restructure Alliance Auth package (#867)
* Refactor allianceauth into its own package

* Add setup

* Add missing default_app_config declarations

* Fix timerboard namespacing

* Remove obsolete future imports

* Remove py2 mock support

* Remove six

* Add experimental 3.7 support and multiple Dj versions

* Remove python_2_unicode_compatible

* Add navhelper as local package

* Update requirements
2017-09-19 09:46:40 +10:00
Basraah
d10580b56b Remove py2 CI 2017-09-18 09:08:54 +10:00
Adarnof
2b51a98d27 Return missing requirement 2017-09-17 19:04:24 -04:00
Adarnof
96c4309230 Add unit tests to CorpUtils 2017-09-17 18:57:08 -04:00
Adarnof
5ce5f37867 Periodic checks of character ownership hashes.
Fix migration naming from merge.
2017-09-17 02:31:46 -04:00
Adarnof
02f2968ee5 Merge branch 'master' of https://github.com/Adarnof/allianceauth into sso_registration
# Conflicts:
#	alliance_auth/__init__.py
#	corputils/models.py
#	corputils/views.py
#	eveonline/tasks.py
#	fleetactivitytracking/views.py
#	hrapplications/admin.py
#	requirements.txt
2017-09-17 01:36:05 -04:00
Adarnof
c1547cab8b Fix that state deletion bug. Thanks @basraah
Also sets state to guest on inactivation, and reassesses on reactivation. With a unit test to boot!
2017-09-06 23:00:12 -04:00
Adarnof
5f20efb0c7 Correct permissions_tool menu item display unit test 2017-08-11 19:49:53 -04:00
Adarnof
3c90868338 Correct available states query for character models missing field data
Ensure state_changed signals are sent
Speed up authentication unit tests
Add authentication unit test for state public availability change
2017-08-11 17:34:43 -04:00
Adarnof
84b1ddc5d9 Enhance admin site.
Regroup auth models.
2017-06-30 17:57:29 -04:00
Adarnof
a235254e48 A whole bunch of unit tests. 2017-06-15 23:01:15 -04:00
Adarnof
40aac6c2dd Fix discord tests 2017-06-14 21:52:32 -04:00
Adarnof
70c496e9fe Remove pycharm .idea folder 2017-06-14 21:03:36 -04:00
Adarnof
4bc91f2381 Fix unit tests.
Discord still failing.
2017-06-14 16:12:56 -04:00
Adarnof
dd3a3e1081 Fix service migration 2017-06-14 00:32:39 -04:00
Adarnof
23bfc3d34a Correct main character reset on token invalidation.
Correct interpretation of missing SERVICES_MIGRATED setting.
Remove legacy api sync celerybeat scheduled task.
2017-06-13 20:34:39 -04:00
Adarnof
7ab88dd663 Alter task to update all eveonline models 2017-06-12 22:20:42 -04:00
Adarnof
ebb44773c2 New view_state_corpstats permission
Cleanup CorpStats permissions
2017-06-12 22:14:35 -04:00
Adarnof
122ab148a4 Move DataTables source to cdnjs 2017-06-08 21:31:19 -04:00
Adarnof
0deb60ac2c Update CorpStats documentation.
Internalize all doc images.
2017-06-08 20:48:44 -04:00
Adarnof
fd8ab2688e Implement DataTables in CorpStats
Site header is broken on pages with DataTables.
2017-06-08 17:28:05 -04:00
Adarnof
b7062c8dda Remove legacy HRApplication models.
Closes #655
2017-06-07 23:12:07 -04:00
Adarnof
97fe2ddfd0 Move templates and urls to apps.
Implement url hooks.
Many apps are now removable.
Default to assuming services have been migrated.
2017-06-07 22:49:46 -04:00
Adarnof
9cc9a36766 Merge branch 'master' of https://github.com/Adarnof/allianceauth into custom_user 2017-06-06 21:48:02 -04:00
Adarnof
d51848aa50 Minor corrections from merge. 2017-06-06 21:47:51 -04:00
Adarnof
00cc89d71c Merge branch 'master' of https://github.com/Adarnof/allianceauth into custom_user
# Conflicts:
#	alliance_auth/settings.py.example
#	eveonline/views.py

Fix some tests.
2017-05-27 18:34:59 -04:00
Adarnof
971ce294ad Merge branch 'master' of https://github.com/Adarnof/allianceauth into custom_user
# Conflicts:
#	alliance_auth/settings.py.example
#	eveonline/views.py

Fix some tests.
2017-05-27 17:25:15 -04:00
Adarnof
ccc7412947 Update test settings 2017-05-27 17:02:59 -04:00
Adarnof
3e4fc7ceb4 Merge branch 'master' of https://github.com/Adarnof/allianceauth into custom_user
# Conflicts:
#	alliance_auth/settings.py.example
#	eveonline/views.py
2017-04-07 00:26:59 -04:00
Adarnof
87973951b8 Correct translation issues.
Correct py3 str model methods.
2017-04-07 00:23:25 -04:00
Adarnof
96b04a269d Merge master.
Fix FAT statistics generation.
2017-04-07 00:21:14 -04:00
Adarnof
f0f1b21226 Begin fixing tests.
Use custom django-navhelper
2017-04-05 00:10:05 -04:00
Adarnof
26405985a2 Change background image.
Remove legacy index.html link images.
2017-03-27 16:17:35 -04:00
Adarnof
3063355eb7 Correct display of non-default CorpStats
Correct display of application comments
Beautify notifications panel
2017-03-27 12:48:24 -04:00
Adarnof
04053c8465 Tweak appearance of CorpStats
Start fixing unit tests
2017-03-27 09:37:20 -04:00
Adarnof
6f36a26694 Allow ordering of ApplicationForm questions
Closes #775
2017-03-26 23:31:56 -04:00
Adarnof
aaf196b477 Apply username sanitizing upon creation
Prevent purging of character ownerships when logging in
Listen to state permission changes for service access verification
2017-03-26 17:45:32 -04:00
Adarnof
06f78a7518 Corpstats views for mains and unregistered
Remove json blob from corpstats model and replace with discrete member models
2017-03-26 17:37:00 -04:00
Adarnof
c6699686ad Prevent altering user states on admin site 2017-03-25 20:19:44 -04:00
Adarnof
64e7c6093e Remove references to legacy is_blue attributes
Provide simple EVE model creation forms with ID, populate the rest from providers
Correct handling of non-main characters in signals
2017-03-25 17:59:38 -04:00
Adarnof
54262a850d Include states in permission auditing 2017-03-25 16:48:21 -04:00
Adarnof
13b0dbc960 Restructure settings.py.example
Add help text to State model
Remove navbar group headings
Fix registration email pluralization
Group memberships on state admin page
Attempt to prevent resetting of state if set on profile admin manually
Embed readthedocs on help page
Rename CorpStats API Index to Registration Index
Default corputils view to main character's corp if available
Correct Application characters listing
Correct string coercion of optimers
Improve readability of SRP values with intcomma
Beautify tables by embeding in panels
Replace slugify with py3-friendly python-slugify
2017-03-25 16:28:26 -04:00
Adarnof
963cecb365 Fix registration.
Fix state assignment.
Fix character ownership transfer.
Disable non-staff passwords.
Fix dashboard groups panel placement.
Fix corpstats viewmodel retrieval.
2017-03-24 19:50:50 -04:00
Adarnof
ab10f062f7 Correct migration syntax. 2017-03-24 10:03:56 -04:00
Adarnof
e15d79b834 Bulk of the profile work is done. 2017-03-23 22:54:25 -04:00
Adarnof
bb87fdd958 temp commit 2017-02-27 18:34:16 -05:00
1016 changed files with 53099 additions and 24977 deletions

View File

@@ -1,24 +1,15 @@
[run]
branch = True
source =
alliance_auth
authentication
corputils
eveonline
fleetactivitytracking
fleetup
groupmanagement
hrapplications
notifications
optimer
permissions_tool
services
srp
timerboard
allianceauth
omit =
*/migrations/*
*/example/*
*/project_template/*
*/bin/*
*/tests/*
*/tests.py
[report]
exclude_lines =

1
.gitattributes vendored
View File

@@ -1 +0,0 @@
*/*.py.example linguist-language=Python

24
.gitignore vendored
View File

@@ -8,6 +8,7 @@ __pycache__/
# Distribution / packaging
.Python
env/
venv/
build/
develop-eggs/
dist/
@@ -41,7 +42,6 @@ nosetests.xml
coverage.xml
# Translations
*.mo
*.pot
# Django stuff:
@@ -53,17 +53,27 @@ docs/_build/
# PyBuilder
target/
.vagrant/
alliance_auth/settings.py
*Thumbs.db
nginx_config.txt
# custom staticfiles
static/*
#celerybeat
*.pid
celerybeat-schedule
#pycharm
.idea/*
.idea/*
/nbproject/
#VSCode
.vscode/
#gitlab configs
.gitlab/
#transifex
.tx/
#other
.flake8
.pylintrc
Makefile

124
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,124 @@
stages:
- gitlab
- test
- deploy
include:
- template: Dependency-Scanning.gitlab-ci.yml
- template: Security/SAST.gitlab-ci.yml
sast:
stage: gitlab
dependency_scanning:
stage: gitlab
before_script:
- apt-get update && apt-get install redis-server libmariadbclient-dev -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
test-3.7-core:
image: python:3.7-buster
script:
- tox -e py37-core
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
artifacts:
when: always
reports:
cobertura: coverage.xml
test-3.8-core:
image: python:3.8-buster
script:
- tox -e py38-core
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
artifacts:
when: always
reports:
cobertura: coverage.xml
test-3.9-core:
image: python:3.9-buster
script:
- tox -e py39-core
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
artifacts:
when: always
reports:
cobertura: coverage.xml
test-3.7-all:
image: python:3.7-buster
script:
- tox -e py37-all
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
artifacts:
when: always
reports:
cobertura: coverage.xml
test-3.8-all:
image: python:3.8-buster
script:
- tox -e py38-all
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
artifacts:
when: always
reports:
cobertura: coverage.xml
test-3.9-all:
image: python:3.9-buster
script:
- tox -e py39-all
before_script:
- apt-get update && apt-get install redis-server -y
- redis-server --daemonize yes
- redis-cli ping
- python -V
- pip install wheel tox
artifacts:
when: always
reports:
cobertura: coverage.xml
deploy_production:
stage: deploy
image: python:3.9-buster
before_script:
- pip install twine wheel
script:
- python setup.py sdist bdist_wheel
- twine upload dist/*
rules:
- if: $CI_COMMIT_TAG

View File

@@ -0,0 +1,14 @@
# Bug
- I have searched [issues](https://gitlab.com/allianceauth/allianceauth/issues?scope=all&utf8=%E2%9C%93&state=all) (Y/N):
- What Version of Alliance Auth:
- What Operating System:
- Version of other components relevant to issue eg. Service, Database:
Please include a brief description of your issue here.
Please include steps to reproduce the issue
Please include any tracebacks or logs
Please include the results of the command `pip list`

View File

@@ -0,0 +1,7 @@
# Feature Request
- Describe the feature are you requesting.
- Is this a Service (external integration), a Module (Alliance Auth extension) or an enhancement to an existing service/module.
- Describe why its useful to you or others.

23
.readthedocs.yml Normal file
View File

@@ -0,0 +1,23 @@
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# Build documentation with MkDocs
#mkdocs:
# configuration: mkdocs.yml
# Optionally build your docs in additional formats such as PDF and ePub
formats: all
# Optionally set the version of Python and requirements required to build your docs
python:
version: 3.7
install:
- requirements: docs/requirements.txt

View File

@@ -1,14 +0,0 @@
language: python
python:
- "2.7"
- "3.5"
# command to install dependencies
install:
- pip install requests
- pip install -r requirements.txt
- pip install -r testing-requirements.txt
# command to run tests
script: coverage run runtests.py
cache: pip
after_success:
coveralls

7
MANIFEST.in Normal file
View File

@@ -0,0 +1,7 @@
include LICENSE
include README.md
include MANIFEST.in
graft allianceauth
global-exclude __pycache__
global-exclude *.py[co]

109
README.md
View File

@@ -1,55 +1,86 @@
Alliance Auth
============
# Alliance Auth
[![Join the chat at https://gitter.im/R4stl1n/allianceauth](https://badges.gitter.im/R4stl1n/allianceauth.svg)](https://gitter.im/R4stl1n/allianceauth?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![license](https://img.shields.io/badge/license-GPLv2-green)](https://pypi.org/project/allianceauth/)
[![python](https://img.shields.io/pypi/pyversions/allianceauth)](https://pypi.org/project/allianceauth/)
[![django](https://img.shields.io/pypi/djversions/allianceauth?label=django)](https://pypi.org/project/allianceauth/)
[![version](https://img.shields.io/pypi/v/allianceauth?label=release)](https://pypi.org/project/allianceauth/)
[![pipeline status](https://gitlab.com/allianceauth/allianceauth/badges/master/pipeline.svg)](https://gitlab.com/allianceauth/allianceauth/commits/master)
[![Documentation Status](https://readthedocs.org/projects/allianceauth/badge/?version=latest)](http://allianceauth.readthedocs.io/?badge=latest)
[![Build Status](https://travis-ci.org/allianceauth/allianceauth.svg?branch=master)](https://travis-ci.org/allianceauth/allianceauth)
[![Coverage Status](https://coveralls.io/repos/github/allianceauth/allianceauth/badge.svg?branch=master)](https://coveralls.io/github/allianceauth/allianceauth?branch=master)
[![coverage report](https://gitlab.com/allianceauth/allianceauth/badges/master/coverage.svg)](https://gitlab.com/allianceauth/allianceauth/commits/master)
[![Chat on Discord](https://img.shields.io/discord/399006117012832262.svg)](https://discord.gg/fjnHAmk)
An auth system for EVE Online to help in-game organizations manage online service access.
EVE service auth to help corps, alliances, and coalitions manage services.
Built for "The 99 Percent" open for anyone to use.
## Content
[Read the docs here.](http://allianceauth.rtfd.io)
- [Overview](#overview)
- [Documentation](http://allianceauth.rtfd.io)
- [Support](#support)
- [Release Notes](https://gitlab.com/allianceauth/allianceauth/-/releases)
- [Developer Team](#developer-team)
- [Contributing](#contributing)
Special Permissions In Admin:
## Overview
auth | user | group_management ( Access to add members to groups within the alliance )
auth | user | jabber_broadcast ( Access to broadcast a message over jabber to own groups )
auth | user | jabber_broadcast_all ( Can choose from all groups and the 'all' option when broadcasting )
auth | user | corp_apis ( View APIs, and jackKnife, of all members in user's corp. )
auth | user | alliance_apis ( View APIs, and jackKnife, of all member in user's alliance member corps. )
auth | user | timer_management ( Access to create and remove timers )
auth | user | timer_view ( Access to timerboard to view timers )
auth | user | srp_management ( Allows for an individual to create and remove srp fleets and fleet data )
auth | user | sigtracker_management ( Allows for an individual to create and remove signitures )
auth | user | sigtracker_view ( Allows for an individual view signitures )
auth | user | optimer_management ( Allows for an individual to create and remove fleet operations )
auth | user | optimer_view ( Allows for an individual view fleet operations )
auth | user | logging_notifications ( Generate notifications from logging )
Alliance Auth (AA) is a web site that helps Eve Online organizations efficiently manage access to applications and services.
auth | user | human_resources ( View applications to user's corp )
hrapplications | application | delete_application ( Can delete applications )
hrapplications | application | accept_application ( Can accept applications )
hrapplications | application | reject_application ( Can reject applications )
hrapplications | application | view_apis ( Can see applicant's API keys )
hrapplications | applicationcomment | add_applicationcomment ( Can comment on applications )
Main features:
Vagrant Instructions:
- Automatically grants or revokes user access to external services (e.g. Discord, Mumble) and web apps (e.g. SRP requests) based on the user's current membership to [in-game organizations](https://allianceauth.readthedocs.io/en/latest/features/core/states/) and [groups](https://allianceauth.readthedocs.io/en/latest/features/core/groups/)
Copy the scripts to the root directory before running
- Provides a central web site where users can directly access web apps (e.g. SRP requests, Fleet Schedule) and manage their access to external services and groups.
Active Developers:
- Includes a set of connectors (called ["services"](https://allianceauth.readthedocs.io/en/latest/features/services/)) for integrating access management with many popular external applications / services like Discord, Mumble, Teamspeak 3, SMF and others
Adarnof
basraah
- Includes a set of web [apps](https://allianceauth.readthedocs.io/en/latest/features/apps/) which add many useful functions, e.g.: fleet schedule, timer board, SRP request management, fleet activity tracker
Beta Testers/ Bug Fixers:
- Can be easily extended with additional services and apps. Many are provided by the community and can be found here: [Community Creations](https://gitlab.com/allianceauth/community-creations)
TrentBartlem ( Testing and Bug Fixes )
IskFiend ( Bug Fixes and Server Configuration )
Mr McClain (Bug Fixes and server configuration )
- English :flag_gb:, Chinese :flag_cn:, German :flag_de:, Spanish :flag_es:, Korean :flag_kr: and Russian :flag_ru: localization
Special Thanks:
For further details about AA - including an installation guide and a full list of included services and plugin apps - please see the [official documentation](http://allianceauth.rtfd.io).
Thanks to Nikdoof, without his old auth implementation this project wouldn't be as far as it is now.
## Screenshot
Here is an example of the Alliance Auth web site with some plug-ins apps and services enabled:
![screenshot](https://i.imgur.com/2tnX9kD.png)
## Support
[Get help on Discord](https://discord.gg/fjnHAmk) or submit an [issue](https://gitlab.com/allianceauth/allianceauth/issues).
## Development Team
### Active Developers
- [Aaron Kable](https://gitlab.com/aaronkable/)
- [Ariel Rin](https://gitlab.com/soratidus999/)
- [Basraah](https://gitlab.com/basraah/)
- [Col Crunch](https://gitlab.com/colcrunch/)
- [Erik Kalkoken](https://gitlab.com/ErikKalkoken/)
### Former Developers
- [Adarnof](https://gitlab.com/adarnof/)
### Beta Testers / Bug Fixers
- [ghoti](https://gitlab.com/ChainsawMcGinny/)
- [kaezon](https://github.com/kaezon/)
- [mmolitor87](https://gitlab.com/mmolitor87/)
- [orbitroom](https://github.com/orbitroom/)
- [TargetZ3R0](https://github.com/TargetZ3R0)
- [tehfiend](https://github.com/tehfiend/)
Special thanks to [Nikdoof](https://github.com/nikdoof/), as his [auth](https://github.com/nikdoof/test-auth) was the foundation for the original work on this project.
## Contributing
Alliance Auth is maintained and developed by the community and we welcome every contribution!
To see what needs to be worked on please review our issue list or chat with our active developers on Discord.
Also, please make sure you have signed the [License Agreement](https://developers.eveonline.com/resource/license-agreement) by logging in at [https://developers.eveonline.com](https://developers.eveonline.com) before submitting any pull requests.
In addition to the core AA system we also very much welcome contributions to our growing list of 3rd party services and plugin apps. Please see [AA Community Creations](https://gitlab.com/allianceauth/community-creations) for details.

View File

@@ -1,2 +0,0 @@
/settings.py
!/*.example

View File

@@ -1,8 +0,0 @@
from __future__ import absolute_import, unicode_literals
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celeryapp import app as celery_app # noqa
__version__ = '1.15.7'
NAME = 'Alliance Auth v%s' % __version__

View File

@@ -1,17 +0,0 @@
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'alliance_auth.settings')
from django.conf import settings # noqa
app = Celery('alliance_auth')
# Using a string here means the worker don't have to serialize
# the configuration object to child processes.
app.config_from_object('django.conf:settings')
# Load task modules from all registered Django app configs.
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

View File

@@ -1,706 +0,0 @@
# -*- coding: UTF-8 -*-
"""
Django settings for alliance_auth project.
Generated by 'django-admin startproject' using Django 1.10.1.
For more information on this file, see
https://docs.djangoproject.com/en/1.10/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.10/ref/settings/
"""
import os
from django.contrib import messages
from celery.schedules import crontab
# Celery configuration
BROKER_URL = 'redis://localhost:6379/0'
CELERYBEAT_SCHEDULER = "django_celery_beat.schedulers.DatabaseScheduler"
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = ''
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = 'True' == os.environ.get('AA_DEBUG','True')
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
'django_celery_beat',
'bootstrapform',
'authentication',
'services',
'eveonline',
'groupmanagement',
'hrapplications',
'timerboard',
'srp',
'optimer',
'corputils',
'fleetactivitytracking',
'fleetup',
'notifications',
'esi',
'permissions_tool',
'geelweb.django.navhelper',
'bootstrap_pagination',
'captcha',
# Services
'services.modules.mumble',
'services.modules.discord',
'services.modules.discourse',
'services.modules.ipboard',
'services.modules.ips4',
'services.modules.market',
'services.modules.openfire',
'services.modules.seat',
'services.modules.smf',
'services.modules.phpbb3',
'services.modules.xenforo',
'services.modules.teamspeak3',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.locale.LocaleMiddleware',
]
ROOT_URLCONF = 'alliance_auth.urls'
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale/'),
)
ugettext = lambda s: s
LANGUAGES = (
('en', ugettext('English')),
('de', ugettext('German')),
)
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'customization/templates'),
os.path.join(BASE_DIR, 'stock/templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'services.context_processors.auth_settings',
'notifications.context_processors.user_notification_count',
'authentication.context_processors.states',
'authentication.context_processors.membership_state',
'groupmanagement.context_processors.can_manage_groups',
],
},
},
]
WSGI_APPLICATION = 'alliance_auth.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'alliance_auth',
'USER': os.environ.get('AA_DB_DEFAULT_USER', 'allianceserver'),
'PASSWORD': os.environ.get('AA_DB_DEFAULT_PASSWORD', 'password'),
'HOST': os.environ.get('AA_DB_DEFAULT_HOST', '127.0.0.1'),
'PORT': os.environ.get('AA_DB_DEFAULT_PORT', '3306'),
},
}
# If you have run the authentication.0013_service_modules migration
# you will need to set this to True in order to install service modules
# which were involved in that migration after it has been run.
# If you are on a fresh install with no existing database you can safely
# set this to True
# If you have not run the authentication.0013_service_modules migration
# leave this set to False.
SERVICES_MIGRATED = False
# Password validation
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LOGIN_URL = 'auth_login_user'
SUPERUSER_STATE_BYPASS = 'True' == os.environ.get('AA_SUPERUSER_STATE_BYPASS', 'True')
# Internationalization
# https://docs.djangoproject.com/en/1.10/topics/i18n/
LANGUAGE_CODE = os.environ.get('AA_LANGUAGE_CODE', 'en-us')
TIME_ZONE = os.environ.get('AA_TIME_ZONE', 'UTC')
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "customization/static"),
os.path.join(BASE_DIR, "stock/static"),
)
# Bootstrap messaging css workaround
MESSAGE_TAGS = {
messages.ERROR: 'danger'
}
CACHES = {
"default": {
"BACKEND": "redis_cache.RedisCache",
"LOCATION": "localhost:6379",
"OPTIONS": {
"DB": 1,
}
}
}
# Google Recaptcha
CAPTCHA_ENABLED = False
RECAPTCHA_PUBLIC_KEY = 'MyRecaptchaKey'
RECAPTCHA_PRIVATE_KEY = 'MyRecaptchaPrivateKey'
NOCAPTCHA = True
#####################################################
##
## Auth configuration starts here
##
#####################################################
#########################
# CELERY SCHEDULED TASKS
#########################
CELERYBEAT_SCHEDULE = {
'run_api_refresh': {
'task': 'eveonline.tasks.run_api_refresh',
'schedule': crontab(minute=0, hour="*/3"),
},
'run_corp_update': {
'task': 'eveonline.tasks.run_corp_update',
'schedule': crontab(minute=0, hour="*/2"),
},
'update_all_corpstats': {
'task': 'corputils.tasks.update_all_corpstats',
'schedule': crontab(minute=0, hour="*/6"),
},
}
#################
# EMAIL SETTINGS
#################
# DOMAIN - The alliance auth domain_url
# EMAIL_HOST - SMTP Server URL
# EMAIL_PORT - SMTP Server PORT
# EMAIL_HOST_USER - Email Username (for gmail, the entire address)
# EMAIL_HOST_PASSWORD - Email Password
# EMAIL_USE_TLS - Set to use TLS encryption
#################
DOMAIN = os.environ.get('AA_DOMAIN', 'https://example.com')
EMAIL_HOST = os.environ.get('AA_EMAIL_HOST', 'smtp.gmail.com')
EMAIL_PORT = int(os.environ.get('AA_EMAIL_PORT', '587'))
EMAIL_HOST_USER = os.environ.get('AA_EMAIL_HOST_USER', '')
EMAIL_HOST_PASSWORD = os.environ.get('AA_EMAIL_HOST_PASSWORD', '')
EMAIL_USE_TLS = 'True' == os.environ.get('AA_EMAIL_USE_TLS', 'True')
####################
# Front Page Links
####################
# KILLBOARD_URL - URL for your killboard. Blank to hide link
# MEDIA_URL - URL for your media page (youtube etc). Blank to hide link
# FORUM_URL - URL for your forums. Blank to hide link
# SITE_NAME - Name of the auth site.
####################
KILLBOARD_URL = os.environ.get('AA_KILLBOARD_URL', '')
EXTERNAL_MEDIA_URL = os.environ.get('AA_EXTERNAL_MEDIA_URL', '')
FORUM_URL = os.environ.get('AA_FORUM_URL', '')
SITE_NAME = os.environ.get('AA_SITE_NAME', 'Alliance Auth')
###################
# SSO Settings
###################
# Get client ID and client secret from registering an app at
# https://developers.eveonline.com/
# Callback URL should be https://example.com/sso/callback
###################
ESI_SSO_CLIENT_ID = os.environ.get('AA_ESI_SSO_CLIENT_ID', '')
ESI_SSO_CLIENT_SECRET = os.environ.get('AA_ESI_SSO_CLIENT_SECRET', '')
ESI_SSO_CALLBACK_URL = os.environ.get('AA_ESI_SSO_CALLBACK_URL', '')
#########################
# Default Group Settings
#########################
# DEFAULT_AUTH_GROUP - Default group members are put in
# DEFAULT_BLUE_GROUP - Default group for blue members
# MEMBER_CORP_GROUPS - Assign members to a group representing their main corp
# BLUE_CORP_GROUPS - Assign blues to a group representing their main corp
#########################
DEFAULT_AUTH_GROUP = os.environ.get('AA_DEFAULT_ALLIANCE_GROUP', 'Member')
DEFAULT_BLUE_GROUP = os.environ.get('AA_DEFAULT_BLUE_GROUP', 'Blue')
MEMBER_CORP_GROUPS = 'True' == os.environ.get('AA_MEMBER_CORP_GROUPS', 'True')
MEMBER_ALLIANCE_GROUPS = 'True' == os.environ.get('AA_MEMBER_ALLIANCE_GROUPS', 'False')
BLUE_CORP_GROUPS = 'True' == os.environ.get('AA_BLUE_CORP_GROUPS', 'False')
BLUE_ALLIANCE_GROUPS = 'True' == os.environ.get('AA_BLUE_ALLIANCE_GROUPS', 'False')
#########################
# Tenant Configuration
#########################
# CORP_IDS - A list of corporation IDs to treat as members.
# ALLIANCE_IDS - A list of alliance IDs to treat as members.
# Any corps in a specified alliance will be treated as members, so do not include them in CORP_IDS
#########################
CORP_IDS = []
ALLIANCE_IDS = []
#########################
# Standings Configuration
#########################
# Add a corp API key to add blue standings to grant access.
# CORP_API_ID - Set this to the api id for the corp API key
# CORP_API_VCODE - Set this to the api vcode for the corp API key
# BLUE_STANDING - The lowest standings value to consider blue
# STANDING_LEVEL - The level of standings to query. Accepted values are 'corp' and 'alliance'.
# BLUE_CORP_IDS - A list of corps to remain blue regardless of in-game standings
# BLUE_ALLIANCE_IDS - A list of alliances to remain blue regardless of in-game standings
########################
CORP_API_ID = os.environ.get('AA_CORP_API_ID', '')
CORP_API_VCODE = os.environ.get('AA_CORP_API_VCODE', '')
BLUE_STANDING = float(os.environ.get('AA_BLUE_STANDING', '5.0'))
STANDING_LEVEL = os.environ.get('AA_STANDING_LEVEL', 'corp')
BLUE_CORP_IDS = []
BLUE_ALLIANCE_IDS = []
########################
# API Configuration
########################
# MEMBER_API_MASK - Numeric value of minimum API mask required for members
# MEMBER_API_ACCOUNT - Require API to be for Account and not character restricted
# BLUE_API_MASK - Numeric value of minimum API mask required for blues
# BLUE_API_ACCOUNT - Require API to be for Account and not character restricted
# REJECT_OLD_APIS - Require each submitted API be newer than the latest submitted API
# REJECT_OLD_APIS_MARGIN - Margin from latest submitted API ID within which a newly submitted API is still accepted
# API_SSO_VALIDATION - Require users to prove ownership of newly entered API keys via SSO
# Requires SSO to be configured.
#######################
MEMBER_API_MASK = os.environ.get('AA_MEMBER_API_MASK', 268435455)
MEMBER_API_ACCOUNT = 'True' == os.environ.get('AA_MEMBER_API_ACCOUNT', 'True')
BLUE_API_MASK = os.environ.get('AA_BLUE_API_MASK', 8388608)
BLUE_API_ACCOUNT = 'True' == os.environ.get('AA_BLUE_API_ACCOUNT', 'False')
REJECT_OLD_APIS = 'True' == os.environ.get('AA_REJECT_OLD_APIS', 'False')
REJECT_OLD_APIS_MARGIN = os.environ.get('AA_REJECT_OLD_APIS_MARGIN', 50)
API_SSO_VALIDATION = 'True' == os.environ.get('AA_API_SSO_VALIDATION', 'False')
#######################
# EVE Provider Settings
#######################
# EVEONLINE_CHARACTER_PROVIDER - Name of default data source for getting eve character data
# EVEONLINE_CORP_PROVIDER - Name of default data source for getting eve corporation data
# EVEONLINE_ALLIANCE_PROVIDER - Name of default data source for getting eve alliance data
# EVEONLINE_ITEMTYPE_PROVIDER - Name of default data source for getting eve item type data
#
# Available sources are 'esi' and 'xml'. Leaving blank results in the default 'esi' being used.
#######################
EVEONLINE_CHARACTER_PROVIDER = os.environ.get('AA_EVEONLINE_CHARACTER_PROVIDER', '')
EVEONLINE_CORP_PROVIDER = os.environ.get('AA_EVEONLINE_CORP_PROVIDER', '')
EVEONLINE_ALLIANCE_PROVIDER = os.environ.get('AA_EVEONLINE_ALLIANCE_PROVIDER', '')
EVEONLINE_ITEMTYPE_PROVIDER = os.environ.get('AA_EVEONLINE_ITEMTYPE_PROVIDER', '')
#####################
# Alliance Market
#####################
MARKET_URL = os.environ.get('AA_MARKET_URL', 'http://example.com/market')
MARKET_DB = {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'alliance_market',
'USER': os.environ.get('AA_DB_MARKET_USER', 'allianceserver-market'),
'PASSWORD': os.environ.get('AA_DB_MARKET_PASSWORD', 'password'),
'HOST': os.environ.get('AA_DB_MARKET_HOST', '127.0.0.1'),
'PORT': os.environ.get('AA_DB_MARKET_PORT', '3306'),
}
#####################
# HR Configuration
#####################
# API_KEY_AUDIT_URL - URL for viewing API keys.
# Other URLs are supported. Use string formatting notation {} with parameter names api_id, vcode, pk
# Example URL formats are shown below:
# Old Jacknife: 'https://example.com/jacknife/eveapi/audit.php?usid={api_id}&apik={vcode}'
# New Jacknife: 'https://example.com/jacknife/?usid={api_id}&apik={vcode}'
# SeAT: 'https://seat.example.com/api-key/detail/{api_id}'
# Django Admin: '/admin/eveonline/eveapikeypair/{pk}/change'
# Leave blank for the verification code to be shown in a popup on click.
#####################
API_KEY_AUDIT_URL = os.environ.get('AA_API_KEY_AUDIT_URL', '')
#####################
# Forum Configuration
#####################
# IPBOARD_ENDPOINT - Api endpoint if using ipboard
# IPBOARD_APIKEY - Api key to interact with ipboard
# IPBOARD_APIMODULE - Module for alliance auth *leave alone*
#####################
IPBOARD_ENDPOINT = os.environ.get('AA_IPBOARD_ENDPOINT', 'example.com/interface/board/index.php')
IPBOARD_APIKEY = os.environ.get('AA_IPBOARD_APIKEY', 'somekeyhere')
IPBOARD_APIMODULE = 'aa'
########################
# XenForo Configuration
########################
XENFORO_ENDPOINT = os.environ.get('AA_XENFORO_ENDPOINT', 'example.com/api.php')
XENFORO_DEFAULT_GROUP = os.environ.get('AA_XENFORO_DEFAULT_GROUP', 0)
XENFORO_APIKEY = os.environ.get('AA_XENFORO_APIKEY', 'yourapikey')
#####################
######################
# Jabber Configuration
######################
# JABBER_URL - Jabber address url
# JABBER_PORT - Jabber service portal
# JABBER_SERVER - Jabber server url
# OPENFIRE_ADDRESS - Address of the openfire admin console including port
# Please use http with 9090 or https with 9091
# OPENFIRE_SECRET_KEY - Openfire REST API secret key
# BROADCAST_USER - Broadcast user JID
# BROADCAST_USER_PASSWORD - Broadcast user password
######################
JABBER_URL = os.environ.get('AA_JABBER_URL', "example.com")
JABBER_PORT = int(os.environ.get('AA_JABBER_PORT', '5223'))
JABBER_SERVER = os.environ.get('AA_JABBER_SERVER', "example.com")
OPENFIRE_ADDRESS = os.environ.get('AA_OPENFIRE_ADDRESS', "http://example.com:9090")
OPENFIRE_SECRET_KEY = os.environ.get('AA_OPENFIRE_SECRET_KEY', "somekey")
BROADCAST_USER = os.environ.get('AA_BROADCAST_USER', "broadcast@") + JABBER_URL
BROADCAST_USER_PASSWORD = os.environ.get('AA_BROADCAST_USER_PASSWORD', "somepassword")
BROADCAST_SERVICE_NAME = os.environ.get('AA_BROADCAST_SERVICE_NAME', "broadcast")
######################################
# Mumble Configuration
######################################
# MUMBLE_URL - Mumble server host
# Do not include leading http:// or mumble://
######################################
MUMBLE_URL = os.environ.get('AA_MUMBLE_URL', "example.com")
######################################
# PHPBB3 Configuration
######################################
PHPBB3_DB = {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'alliance_forum',
'USER': os.environ.get('AA_DB_PHPBB3_USER', 'allianceserver-phpbb3'),
'PASSWORD': os.environ.get('AA_DB_PHPBB3_PASSWORD', 'password'),
'HOST': os.environ.get('AA_DB_PHPBB3_HOST', '127.0.0.1'),
'PORT': os.environ.get('AA_DB_PHPBB3_PORT', '3306'),
}
######################################
# Teamspeak3 Configuration
######################################
# TEAMSPEAK3_SERVER_IP - Teamspeak3 server ip
# TEAMSPEAK3_SERVER_PORT - Teamspeak3 server port
# TEAMSPEAK3_SERVERQUERY_USER - Teamspeak3 serverquery username
# TEAMSPEAK3_SERVERQUERY_PASSWORD - Teamspeak3 serverquery password
# TEAMSPEAK3_VIRTUAL_SERVER - Virtual server id
# TEAMSPEAK3_AUTHED_GROUP_ID - Default authed group id
# TEAMSPEAK3_PUBLIC_URL - teamspeak3 public url used for link creation
######################################
TEAMSPEAK3_SERVER_IP = os.environ.get('AA_TEAMSPEAK3_SERVER_IP', '127.0.0.1')
TEAMSPEAK3_SERVER_PORT = int(os.environ.get('AA_TEAMSPEAK3_SERVER_PORT', '10011'))
TEAMSPEAK3_SERVERQUERY_USER = os.environ.get('AA_TEAMSPEAK3_SERVERQUERY_USER', 'serveradmin')
TEAMSPEAK3_SERVERQUERY_PASSWORD = os.environ.get('AA_TEAMSPEAK3_SERVERQUERY_PASSWORD', 'passwordhere')
TEAMSPEAK3_VIRTUAL_SERVER = int(os.environ.get('AA_TEAMSPEAK3_VIRTUAL_SERVER', '1'))
TEAMSPEAK3_PUBLIC_URL = os.environ.get('AA_TEAMSPEAK3_PUBLIC_URL', 'example.com')
######################################
# Discord Configuration
######################################
# DISCORD_GUILD_ID - ID of the guild to manage
# DISCORD_BOT_TOKEN - oauth token of the app bot user
# DISCORD_APP_ID - oauth app client ID
# DISCORD_APP_SECRET - oauth app secret
# DISCORD_CALLBACK_URL - oauth callback url
# DISCORD_SYNC_NAMES - enable to force discord nicknames to be set to eve char name (bot needs Manage Nicknames permission)
######################################
DISCORD_GUILD_ID = os.environ.get('AA_DISCORD_GUILD_ID', '')
DISCORD_BOT_TOKEN = os.environ.get('AA_DISCORD_BOT_TOKEN', '')
DISCORD_APP_ID = os.environ.get('AA_DISCORD_APP_ID', '')
DISCORD_APP_SECRET = os.environ.get('AA_DISCORD_APP_SECRET', '')
DISCORD_CALLBACK_URL = os.environ.get('AA_DISCORD_CALLBACK_URL', 'http://example.com/discord/callback')
DISCORD_SYNC_NAMES = 'True' == os.environ.get('AA_DISCORD_SYNC_NAMES', 'False')
######################################
# Discourse Configuration
######################################
# DISCOURSE_URL - Web address of the forums (no trailing slash)
# DISCOURSE_API_USERNAME - API account username
# DISCOURSE_API_KEY - API Key
# DISCOURSE_SSO_SECRET - SSO secret key
######################################
DISCOURSE_URL = os.environ.get('AA_DISCOURSE_URL', '')
DISCOURSE_API_USERNAME = os.environ.get('AA_DISCOURSE_API_USERNAME', '')
DISCOURSE_API_KEY = os.environ.get('AA_DISCOURSE_API_KEY', '')
DISCOURSE_SSO_SECRET = os.environ.get('AA_DISCOURSE_SSO_SECRET', '')
#####################################
# IPS4 Configuration
#####################################
# IPS4_URL - base url of the IPS4 install (no trailing slash)
# IPS4_API_KEY - API key provided by IPS4
#####################################
IPS4_URL = os.environ.get('AA_IPS4_URL', 'http://example.com/ips4')
IPS4_API_KEY = os.environ.get('AA_IPS4_API_KEY', '')
IPS4_DB = {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'alliance_ips4',
'USER': os.environ.get('AA_DB_IPS4_USER', 'allianceserver-ips4'),
'PASSWORD': os.environ.get('AA_DB_IPS4_PASSWORD', 'password'),
'HOST': os.environ.get('AA_DB_IPS4_HOST', '127.0.0.1'),
'PORT': os.environ.get('AA_DB_IPS4_PORT', '3306'),
}
#####################################
# SEAT Configuration
#####################################
# SEAT_URL - base url of the seat install (no trailing slash)
# SEAT_XTOKEN - API key X-Token provided by SeAT
#####################################
SEAT_URL = os.environ.get('AA_SEAT_URL', 'http://example.com/seat')
SEAT_XTOKEN = os.environ.get('AA_SEAT_XTOKEN', '')
######################################
# SMF Configuration
######################################
SMF_URL = os.environ.get('AA_SMF_URL', 'https://example.com')
SMF_DB = {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'alliance_smf',
'USER': os.environ.get('AA_DB_SMF_USER', 'allianceserver-smf'),
'PASSWORD': os.environ.get('AA_DB_SMF_PASSWORD', 'password'),
'HOST': os.environ.get('AA_DB_SMF_HOST', '127.0.0.1'),
'PORT': os.environ.get('AA_DB_SMF_PORT', '3306'),
}
######################################
# Fleet-Up Configuration
######################################
# FLEETUP_APP_KEY - The app key from http://fleet-up.com/Api/MyApps
# FLEETUP_USER_ID - The user id from http://fleet-up.com/Api/MyKeys
# FLEETUP_API_ID - The API id from http://fleet-up.com/Api/MyKeys
# FLEETUP_GROUP_ID - The id of the group you want to pull data from, see http://fleet-up.com/Api/Endpoints#groups_mygroupmemberships
######################################
FLEETUP_APP_KEY = os.environ.get('AA_FLEETUP_APP_KEY', '')
FLEETUP_USER_ID = os.environ.get('AA_FLEETUP_USER_ID', '')
FLEETUP_API_ID = os.environ.get('AA_FLEETUP_API_ID', '')
FLEETUP_GROUP_ID = os.environ.get('AA_FLEETUP_GROUP_ID', '')
#####################################
# Logging Configuration
#####################################
# Set log_file and console level to desired state:
# DEBUG - basically stack trace, explains every step
# INFO - model creation, deletion, updates, etc
# WARN - unexpected function outcomes that do not impact user
# ERROR - unexcpeted function outcomes which prevent user from achieving desired outcome
# EXCEPTION - something critical went wrong, unhandled
#####################################
# Recommended level for log_file is INFO, console is DEBUG
# Change log level of individual apps below to narrow your debugging
#####################################
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
'datefmt' : "%d/%b/%Y %H:%M:%S"
},
'simple': {
'format': '%(levelname)s %(message)s'
},
},
'handlers': {
'log_file': {
'level': 'INFO', # edit this line to change logging level to file
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_DIR,'log/allianceauth.log'),
'formatter': 'verbose',
'maxBytes': 1024*1024*5, # edit this line to change max log file size
'backupCount': 5, # edit this line to change number of log backups
},
'console': {
'level': 'DEBUG', # edit this line to change logging level to console
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
'notifications': { # creates notifications for users with logging_notifications permission
'level': 'ERROR', # edit this line to change logging level to notifications
'class': 'notifications.handlers.NotificationHandler',
'formatter': 'verbose',
},
},
'loggers': {
'authentication': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'celerytask': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'eveonline': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'groupmanagement': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'hrapplications': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'portal': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'registration': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'services': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'srp': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'timerboard': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'sigtracker': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'optimer': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'corputils': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'fleetactivitytracking': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'ERROR',
},
'fleetup': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'util': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'django': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'ERROR',
},
}
}
# Conditionally add databases only if configured
if 'services.modules.phpbb3' in INSTALLED_APPS:
DATABASES['phpbb3'] = PHPBB3_DB
if 'services.modules.smf' in INSTALLED_APPS:
DATABASES['smf'] = SMF_DB
if 'services.modules.market' in INSTALLED_APPS:
DATABASES['market'] = MARKET_DB
if 'services.modules.ips4' in INSTALLED_APPS:
DATABASES['ips4'] = IPS4_DB
# Ensure corp/alliance IDs are expected types
STR_CORP_IDS = [str(id) for id in CORP_IDS]
STR_ALLIANCE_IDS = [str(id) for id in ALLIANCE_IDS]
STR_BLUE_CORP_IDS = [str(id) for id in BLUE_CORP_IDS]
STR_BLUE_ALLIANCE_IDS = [str(id) for id in BLUE_ALLIANCE_IDS]
# Conditionally add periodic tasks for services if installed
if 'services.modules.teamspeak3' in INSTALLED_APPS:
CELERYBEAT_SCHEDULE['run_ts3_group_update'] = {
'task': 'services.modules.teamspeak3.tasks.run_ts3_group_update',
'schedule': crontab(minute='*/30'),
}
if 'services.modules.seat' in INSTALLED_APPS:
CELERYBEAT_SCHEDULE['run_seat_api_sync'] = {
'task': 'services.modules.seat.tasks.run_api_sync',
'schedule': crontab(minute='*/30'),
}

View File

@@ -1,98 +0,0 @@
from __future__ import unicode_literals
from django.db.models.signals import m2m_changed, pre_save
from django.contrib.auth.models import User, Group, Permission
from services.signals import m2m_changed_user_groups, pre_save_user
from services.signals import m2m_changed_group_permissions, m2m_changed_user_permissions
from authentication.signals import pre_save_auth_state
from authentication.tasks import make_member, make_blue
from authentication.models import AuthServicesInfo
from authentication.states import MEMBER_STATE, BLUE_STATE, NONE_STATE
from eveonline.models import EveCharacter
class AuthUtils:
def __init__(self):
pass
@staticmethod
def _create_user(username):
return User.objects.create(username=username)
@classmethod
def create_user(cls, username, disconnect_signals=False):
if disconnect_signals:
cls.disconnect_signals()
user = cls._create_user(username)
user.authservicesinfo.state = NONE_STATE
user.authservicesinfo.save()
if disconnect_signals:
cls.connect_signals()
return user
@classmethod
def create_member(cls, username):
cls.disconnect_signals()
user = cls._create_user(username)
user.authservicesinfo.state = MEMBER_STATE
user.authservicesinfo.save()
make_member(user.authservicesinfo)
cls.connect_signals()
return user
@classmethod
def create_blue(cls, username):
cls.disconnect_signals()
user = cls._create_user(username)
user.authservicesinfo.state = BLUE_STATE
user.authservicesinfo.save()
make_blue(user.authservicesinfo)
cls.connect_signals()
return user
@classmethod
def disconnect_signals(cls):
m2m_changed.disconnect(m2m_changed_user_groups, sender=User.groups.through)
m2m_changed.disconnect(m2m_changed_group_permissions, sender=Group.permissions.through)
m2m_changed.disconnect(m2m_changed_user_permissions, sender=User.user_permissions.through)
pre_save.disconnect(pre_save_user, sender=User)
pre_save.disconnect(pre_save_auth_state, sender=AuthServicesInfo)
@classmethod
def connect_signals(cls):
m2m_changed.connect(m2m_changed_user_groups, sender=User.groups.through)
m2m_changed.connect(m2m_changed_group_permissions, sender=Group.permissions.through)
m2m_changed.connect(m2m_changed_user_permissions, sender=User.user_permissions.through)
pre_save.connect(pre_save_user, sender=User)
pre_save.connect(pre_save_auth_state, sender=AuthServicesInfo)
@classmethod
def add_main_character(cls, user, name, character_id, corp_id='', corp_name='', corp_ticker='', alliance_id='',
alliance_name=''):
EveCharacter.objects.create(
character_id=character_id,
character_name=name,
corporation_id=corp_id,
corporation_name=corp_name,
corporation_ticker=corp_ticker,
alliance_id=alliance_id,
alliance_name=alliance_name,
api_id='1234',
user=user
)
AuthServicesInfo.objects.update_or_create(user=user, defaults={'main_char_id': character_id})
@classmethod
def add_permissions_to_groups(cls, perms, groups, disconnect_signals=True):
if disconnect_signals:
cls.disconnect_signals()
for group in groups:
for perm in perms:
group.permissions.add(perm)
if disconnect_signals:
cls.connect_signals()

View File

@@ -1,556 +0,0 @@
"""
Alliance Auth Test Suite Django settings.
"""
import os
from django.contrib import messages
import alliance_auth
# Use nose to run all tests
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
NOSE_ARGS = [
#'--with-coverage',
#'--cover-package=',
#'--exe', # If your tests need this to be found/run, check they py files are not chmodded +x
]
# Celery configuration
CELERY_ALWAYS_EAGER = True # Forces celery to run locally for testing
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(alliance_auth.__file__)))
SECRET_KEY = 'testing only'
DEBUG = True
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
'django_celery_beat',
'bootstrapform',
'authentication',
'services',
'eveonline',
'groupmanagement',
'hrapplications',
'timerboard',
'srp',
'optimer',
'corputils',
'fleetactivitytracking',
'fleetup',
'notifications',
'esi',
'permissions_tool',
'geelweb.django.navhelper',
'bootstrap_pagination',
'services.modules.mumble',
'services.modules.discord',
'services.modules.discourse',
'services.modules.ipboard',
'services.modules.ips4',
'services.modules.market',
'services.modules.openfire',
'services.modules.seat',
'services.modules.smf',
'services.modules.phpbb3',
'services.modules.xenforo',
'services.modules.teamspeak3',
'django_nose',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.locale.LocaleMiddleware',
]
ROOT_URLCONF = 'alliance_auth.urls'
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale/'),
)
ugettext = lambda s: s
LANGUAGES = (
('en', ugettext('English')),
('de', ugettext('German')),
)
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'customization/templates'),
os.path.join(BASE_DIR, 'stock/templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'services.context_processors.auth_settings',
'notifications.context_processors.user_notification_count',
'authentication.context_processors.states',
'authentication.context_processors.membership_state',
'groupmanagement.context_processors.can_manage_groups',
],
},
},
]
# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'alliance_auth',
'USER': os.environ.get('AA_DB_DEFAULT_USER', None),
'PASSWORD': os.environ.get('AA_DB_DEFAULT_PASSWORD', None),
'HOST': os.environ.get('AA_DB_DEFAULT_HOST', None)
},
}
LOGIN_URL = 'auth_login_user'
SUPERUSER_STATE_BYPASS = 'True' == os.environ.get('AA_SUPERUSER_STATE_BYPASS', 'True')
# Internationalization
# https://docs.djangoproject.com/en/1.10/topics/i18n/
LANGUAGE_CODE = os.environ.get('AA_LANGUAGE_CODE', 'en')
TIME_ZONE = os.environ.get('AA_TIME_ZONE', 'UTC')
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "customization/static"),
os.path.join(BASE_DIR, "stock/static"),
)
# Bootstrap messaging css workaround
MESSAGE_TAGS = {
messages.ERROR: 'danger'
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
#####################################################
##
## Auth configuration starts here
##
#####################################################
###########################
# ALLIANCE / CORP TOGGLE
###########################
# Specifies to run membership checks against corp or alliance
# Set to FALSE for alliance
# Set to TRUE for corp
###########################
IS_CORP = 'True' == os.environ.get('AA_IS_CORP', 'True')
#################
# EMAIL SETTINGS
#################
# DOMAIN - The alliance auth domain_url
# EMAIL_HOST - SMTP Server URL
# EMAIL_PORT - SMTP Server PORT
# EMAIL_HOST_USER - Email Username (for gmail, the entire address)
# EMAIL_HOST_PASSWORD - Email Password
# EMAIL_USE_TLS - Set to use TLS encryption
#################
DOMAIN = os.environ.get('AA_DOMAIN', 'https://example.com')
EMAIL_HOST = os.environ.get('AA_EMAIL_HOST', 'smtp.example.com')
EMAIL_PORT = int(os.environ.get('AA_EMAIL_PORT', '587'))
EMAIL_HOST_USER = os.environ.get('AA_EMAIL_HOST_USER', '')
EMAIL_HOST_PASSWORD = os.environ.get('AA_EMAIL_HOST_PASSWORD', '')
EMAIL_USE_TLS = 'True' == os.environ.get('AA_EMAIL_USE_TLS', 'True')
####################
# Front Page Links
####################
# KILLBOARD_URL - URL for your killboard. Blank to hide link
# MEDIA_URL - URL for your media page (youtube etc). Blank to hide link
# FORUM_URL - URL for your forums. Blank to hide link
# SITE_NAME - Name of the auth site.
####################
KILLBOARD_URL = os.environ.get('AA_KILLBOARD_URL', '')
EXTERNAL_MEDIA_URL = os.environ.get('AA_EXTERNAL_MEDIA_URL', '')
FORUM_URL = os.environ.get('AA_FORUM_URL', '')
SITE_NAME = os.environ.get('AA_SITE_NAME', 'Test Alliance Auth')
###################
# SSO Settings
###################
# Optional SSO.
# Get client ID and client secret from registering an app at
# https://developers.eveonline.com/
# Callback URL should be http://mydomain.com/sso/callback
# Leave callback blank to hide SSO button on login page
###################
ESI_SSO_CLIENT_ID = os.environ.get('AA_ESI_SSO_CLIENT_ID', '')
ESI_SSO_CLIENT_SECRET = os.environ.get('AA_ESI_SSO_CLIENT_SECRET', '')
ESI_SSO_CALLBACK_URL = os.environ.get('AA_ESI_SSO_CALLBACK_URL', '')
#########################
# Default Group Settings
#########################
# DEFAULT_AUTH_GROUP - Default group members are put in
# DEFAULT_BLUE_GROUP - Default group for blue members
# MEMBER_CORP_GROUPS - Assign members to a group representing their main corp
# BLUE_CORP_GROUPS - Assign blues to a group representing their main corp
#########################
DEFAULT_AUTH_GROUP = os.environ.get('AA_DEFAULT_ALLIANCE_GROUP', 'Member')
DEFAULT_BLUE_GROUP = os.environ.get('AA_DEFAULT_BLUE_GROUP', 'Blue')
MEMBER_CORP_GROUPS = 'True' == os.environ.get('AA_MEMBER_CORP_GROUPS', 'True')
MEMBER_ALLIANCE_GROUPS = 'True' == os.environ.get('AA_MEMBER_ALLIANCE_GROUPS', 'False')
BLUE_CORP_GROUPS = 'True' == os.environ.get('AA_BLUE_CORP_GROUPS', 'False')
BLUE_ALLIANCE_GROUPS = 'True' == os.environ.get('AA_BLUE_ALLIANCE_GROUPS', 'False')
#########################
# Corp Configuration
#########################
# If running in alliance mode, the following should be for the executor corp#
# CORP_ID - Set this to your corp ID (get this from https://zkillboard.com/corporation/#######)
# CORP_NAME - Set this to your Corporation Name
# CORP_API_ID - Set this to the api id for the corp API key
# CORP_API_VCODE - Set this to the api vcode for the corp API key
########################
CORP_ID = os.environ.get('AA_CORP_ID', '1234')
CORP_NAME = os.environ.get('AA_CORP_NAME', 'Alliance Auth Test Corp')
CORP_API_ID = os.environ.get('AA_CORP_API_ID', '')
CORP_API_VCODE = os.environ.get('AA_CORP_API_VCODE', '')
#########################
# Alliance Configuration
#########################
# ALLIANCE_ID - Set this to your Alliance ID (get this from https://zkillboard.com/alliance/#######)
# ALLIANCE_NAME - Set this to your Alliance Name
########################
ALLIANCE_ID = os.environ.get('AA_ALLIANCE_ID', '12345')
ALLIANCE_NAME = os.environ.get('AA_ALLIANCE_NAME', 'Alliance Auth Test Alliance')
########################
# API Configuration
########################
# MEMBER_API_MASK - Numeric value of minimum API mask required for members
# MEMBER_API_ACCOUNT - Require API to be for Account and not character restricted
# BLUE_API_MASK - Numeric value of minimum API mask required for blues
# BLUE_API_ACCOUNT - Require API to be for Account and not character restricted
# REJECT_OLD_APIS - Require each submitted API be newer than the latest submitted API
# REJECT_OLD_APIS_MARGIN - Margin from latest submitted API ID within which a newly submitted API is still accepted
# API_SSO_VALIDATION - Require users to prove ownership of newly entered API keys via SSO
# Requires SSO to be configured.
#######################
MEMBER_API_MASK = os.environ.get('AA_MEMBER_API_MASK', 268435455)
MEMBER_API_ACCOUNT = 'True' == os.environ.get('AA_MEMBER_API_ACCOUNT', 'True')
BLUE_API_MASK = os.environ.get('AA_BLUE_API_MASK', 8388608)
BLUE_API_ACCOUNT = 'True' == os.environ.get('AA_BLUE_API_ACCOUNT', 'True')
REJECT_OLD_APIS = 'True' == os.environ.get('AA_REJECT_OLD_APIS', 'False')
REJECT_OLD_APIS_MARGIN = os.environ.get('AA_REJECT_OLD_APIS_MARGIN', 50)
API_SSO_VALIDATION = 'True' == os.environ.get('AA_API_SSO_VALIDATION', 'False')
#######################
# EVE Provider Settings
#######################
# EVEONLINE_CHARACTER_PROVIDER - Name of default data source for getting eve character data
# EVEONLINE_CORP_PROVIDER - Name of default data source for getting eve corporation data
# EVEONLINE_ALLIANCE_PROVIDER - Name of default data source for getting eve alliance data
# EVEONLINE_ITEMTYPE_PROVIDER - Name of default data source for getting eve item type data
#
# Available sources are 'esi' and 'xml'. Leaving blank results in the default 'esi' being used.
#######################
EVEONLINE_CHARACTER_PROVIDER = os.environ.get('AA_EVEONLINE_CHARACTER_PROVIDER', 'xml')
EVEONLINE_CORP_PROVIDER = os.environ.get('AA_EVEONLINE_CORP_PROVIDER', 'xml')
EVEONLINE_ALLIANCE_PROVIDER = os.environ.get('AA_EVEONLINE_ALLIANCE_PROVIDER', 'xml')
EVEONLINE_ITEMTYPE_PROVIDER = os.environ.get('AA_EVEONLINE_ITEMTYPE_PROVIDER', 'xml')
#####################
# Alliance Market
#####################
MARKET_URL = os.environ.get('AA_MARKET_URL', 'http://yourdomain.com/market')
#####################
# HR Configuration
#####################
# JACK_KNIFE_URL - Url for the audit page of API Jack knife
# Should seriously replace with your own.
#####################
JACK_KNIFE_URL = os.environ.get('AA_JACK_KNIFE_URL', 'http://example.com/eveapi/audit.php')
#####################
# Forum Configuration
#####################
# IPBOARD_ENDPOINT - Api endpoint if using ipboard
# IPBOARD_APIKEY - Api key to interact with ipboard
# IPBOARD_APIMODULE - Module for alliance auth *leave alone*
#####################
IPBOARD_ENDPOINT = os.environ.get('AA_IPBOARD_ENDPOINT', 'example.com/interface/board/index.php')
IPBOARD_APIKEY = os.environ.get('AA_IPBOARD_APIKEY', 'somekeyhere')
IPBOARD_APIMODULE = 'aa'
########################
# XenForo Configuration
########################
XENFORO_ENDPOINT = os.environ.get('AA_XENFORO_ENDPOINT', 'example.com/api.php')
XENFORO_DEFAULT_GROUP = os.environ.get('AA_XENFORO_DEFAULT_GROUP', 0)
XENFORO_APIKEY = os.environ.get('AA_XENFORO_APIKEY', 'yourapikey')
#####################
######################
# Jabber Configuration
######################
# JABBER_URL - Jabber address url
# JABBER_PORT - Jabber service portal
# JABBER_SERVER - Jabber server url
# OPENFIRE_ADDRESS - Address of the openfire admin console including port
# Please use http with 9090 or https with 9091
# OPENFIRE_SECRET_KEY - Openfire REST API secret key
# BROADCAST_USER - Broadcast user JID
# BROADCAST_USER_PASSWORD - Broadcast user password
######################
JABBER_URL = os.environ.get('AA_JABBER_URL', "example.com")
JABBER_PORT = int(os.environ.get('AA_JABBER_PORT', '5223'))
JABBER_SERVER = os.environ.get('AA_JABBER_SERVER', "example.com")
OPENFIRE_ADDRESS = os.environ.get('AA_OPENFIRE_ADDRESS', "http://example.com:9090")
OPENFIRE_SECRET_KEY = os.environ.get('AA_OPENFIRE_SECRET_KEY', "somekey")
BROADCAST_USER = os.environ.get('AA_BROADCAST_USER', "broadcast@") + JABBER_URL
BROADCAST_USER_PASSWORD = os.environ.get('AA_BROADCAST_USER_PASSWORD', "somepassword")
BROADCAST_SERVICE_NAME = os.environ.get('AA_BROADCAST_SERVICE_NAME', "broadcast")
######################################
# Mumble Configuration
######################################
# MUMBLE_URL - Mumble server url
# MUMBLE_SERVER_ID - Mumble server id
######################################
MUMBLE_URL = os.environ.get('AA_MUMBLE_URL', "example.com")
MUMBLE_SERVER_ID = int(os.environ.get('AA_MUMBLE_SERVER_ID', '1'))
######################################
# PHPBB3 Configuration
######################################
######################################
# Teamspeak3 Configuration
######################################
# TEAMSPEAK3_SERVER_IP - Teamspeak3 server ip
# TEAMSPEAK3_SERVER_PORT - Teamspeak3 server port
# TEAMSPEAK3_SERVERQUERY_USER - Teamspeak3 serverquery username
# TEAMSPEAK3_SERVERQUERY_PASSWORD - Teamspeak3 serverquery password
# TEAMSPEAK3_VIRTUAL_SERVER - Virtual server id
# TEAMSPEAK3_AUTHED_GROUP_ID - Default authed group id
# TEAMSPEAK3_PUBLIC_URL - teamspeak3 public url used for link creation
######################################
TEAMSPEAK3_SERVER_IP = os.environ.get('AA_TEAMSPEAK3_SERVER_IP', '127.0.0.1')
TEAMSPEAK3_SERVER_PORT = int(os.environ.get('AA_TEAMSPEAK3_SERVER_PORT', '10011'))
TEAMSPEAK3_SERVERQUERY_USER = os.environ.get('AA_TEAMSPEAK3_SERVERQUERY_USER', 'serveradmin')
TEAMSPEAK3_SERVERQUERY_PASSWORD = os.environ.get('AA_TEAMSPEAK3_SERVERQUERY_PASSWORD', 'passwordhere')
TEAMSPEAK3_VIRTUAL_SERVER = int(os.environ.get('AA_TEAMSPEAK3_VIRTUAL_SERVER', '1'))
TEAMSPEAK3_PUBLIC_URL = os.environ.get('AA_TEAMSPEAK3_PUBLIC_URL', 'example.com')
######################################
# Discord Configuration
######################################
# DISCORD_GUILD_ID - ID of the guild to manage
# DISCORD_BOT_TOKEN - oauth token of the app bot user
# DISCORD_INVITE_CODE - invite code to the server
# DISCORD_APP_ID - oauth app client ID
# DISCORD_APP_SECRET - oauth app secret
# DISCORD_CALLBACK_URL - oauth callback url
# DISCORD_SYNC_NAMES - enable to force discord nicknames to be set to eve char name (bot needs Manage Nicknames permission)
######################################
DISCORD_GUILD_ID = os.environ.get('AA_DISCORD_GUILD_ID', '0118999')
DISCORD_BOT_TOKEN = os.environ.get('AA_DISCORD_BOT_TOKEN', 'bottoken')
DISCORD_INVITE_CODE = os.environ.get('AA_DISCORD_INVITE_CODE', 'invitecode')
DISCORD_APP_ID = os.environ.get('AA_DISCORD_APP_ID', 'appid')
DISCORD_APP_SECRET = os.environ.get('AA_DISCORD_APP_SECRET', 'secret')
DISCORD_CALLBACK_URL = os.environ.get('AA_DISCORD_CALLBACK_URL', 'http://example.com/discord/callback')
DISCORD_SYNC_NAMES = 'True' == os.environ.get('AA_DISCORD_SYNC_NAMES', 'False')
######################################
# Discourse Configuration
######################################
# DISCOURSE_URL - Web address of the forums (no trailing slash)
# DISCOURSE_API_USERNAME - API account username
# DISCOURSE_API_KEY - API Key
# DISCOURSE_SSO_SECRET - SSO secret key
######################################
DISCOURSE_URL = os.environ.get('AA_DISCOURSE_URL', 'https://example.com')
DISCOURSE_API_USERNAME = os.environ.get('AA_DISCOURSE_API_USERNAME', '')
DISCOURSE_API_KEY = os.environ.get('AA_DISCOURSE_API_KEY', '')
DISCOURSE_SSO_SECRET = 'd836444a9e4084d5b224a60c208dce14'
# Example secret from https://meta.discourse.org/t/official-single-sign-on-for-discourse/13045
#####################################
# IPS4 Configuration
#####################################
# IPS4_URL - base url of the IPS4 install (no trailing slash)
# IPS4_API_KEY - API key provided by IPS4
#####################################
IPS4_URL = os.environ.get('AA_IPS4_URL', 'http://example.com/ips4')
IPS4_API_KEY = os.environ.get('AA_IPS4_API_KEY', '')
#####################################
# SEAT Configuration
#####################################
# SEAT_URL - base url of the seat install (no trailing slash)
# SEAT_XTOKEN - API key X-Token provided by SeAT
#####################################
SEAT_URL = os.environ.get('AA_SEAT_URL', 'http://example.com/seat')
SEAT_XTOKEN = os.environ.get('AA_SEAT_XTOKEN', 'tokentokentoken')
######################################
# SMF Configuration
######################################
SMF_URL = os.environ.get('AA_SMF_URL', '')
######################################
# Fleet-Up Configuration
######################################
# FLEETUP_APP_KEY - The app key from http://fleet-up.com/Api/MyApps
# FLEETUP_USER_ID - The user id from http://fleet-up.com/Api/MyKeys
# FLEETUP_API_ID - The API id from http://fleet-up.com/Api/MyKeys
# FLEETUP_GROUP_ID - The id of the group you want to pull data from, see http://fleet-up.com/Api/Endpoints#groups_mygroupmemberships
######################################
FLEETUP_APP_KEY = os.environ.get('AA_FLEETUP_APP_KEY', '')
FLEETUP_USER_ID = os.environ.get('AA_FLEETUP_USER_ID', '')
FLEETUP_API_ID = os.environ.get('AA_FLEETUP_API_ID', '')
FLEETUP_GROUP_ID = os.environ.get('AA_FLEETUP_GROUP_ID', '')
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.MD5PasswordHasher',
]
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
'datefmt' : "%d/%b/%Y %H:%M:%S"
},
'simple': {
'format': '%(levelname)s %(message)s'
},
},
'handlers': {
'console': {
'level': 'DEBUG', # edit this line to change logging level to console
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
'notifications': { # creates notifications for users with logging_notifications permission
'level': 'ERROR', # edit this line to change logging level to notifications
'class': 'notifications.handlers.NotificationHandler',
'formatter': 'verbose',
},
},
'loggers': {
'authentication': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'celerytask': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'eveonline': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'groupmanagement': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'hrapplications': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'portal': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'registration': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'services': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'srp': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'timerboard': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'sigtracker': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'optimer': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'corputils': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'fleetactivitytracking': {
'handlers': ['console', 'notifications'],
'level': 'ERROR',
},
'util': {
'handlers': ['console', 'notifications'],
'level': 'DEBUG',
},
'django': {
'handlers': ['console', 'notifications'],
'level': 'ERROR',
},
}
}
LOGGING = None # Comment out to enable logging for debugging

View File

@@ -1,222 +0,0 @@
from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns
from django.utils.translation import ugettext_lazy as _
from django.contrib import admin
import django.contrib.auth.views
import authentication.views
import eveonline.views
import services.views
import groupmanagement.views
import optimer.views
import timerboard.views
import fleetactivitytracking.views
import fleetup.urls
import srp.views
import notifications.views
import hrapplications.views
import corputils.urls
import esi.urls
import permissions_tool.urls
from alliance_auth import NAME
admin.site.site_header = NAME
from alliance_auth.hooks import get_hooks
# Functional/Untranslated URL's
urlpatterns = [
# Locale
url(r'^i18n/', include('django.conf.urls.i18n')),
# Admin urls
url(r'^admin/', include(admin.site.urls)),
# SSO
url(r'^sso/', include(esi.urls, namespace='esi')),
url(r'^sso/login$', authentication.views.sso_login, name='auth_sso_login'),
# Index
url(_(r'^$'), authentication.views.index_view, name='auth_index'),
# Authentication
url(r'^logout_user/', authentication.views.logout_user, name='auth_logout_user'),
# Eve Online
url(r'^main_character_change/(\w+)/$', eveonline.views.main_character_change,
name='auth_main_character_change'),
url(r'^api_verify_owner/(\w+)/$', eveonline.views.api_sso_validate, name='auth_api_sso'),
# SRP URLS
url(r'^srp_fleet_remove/(\w+)$', srp.views.srp_fleet_remove, name='auth_srp_fleet_remove'),
url(r'^srp_fleet_disable/(\w+)$', srp.views.srp_fleet_disable, name='auth_srp_fleet_disable'),
url(r'^srp_fleet_enable/(\w+)$', srp.views.srp_fleet_enable, name='auth_srp_fleet_enable'),
url(r'^srp_fleet_mark_completed/(\w+)', srp.views.srp_fleet_mark_completed,
name='auth_srp_fleet_mark_completed'),
url(r'^srp_fleet_mark_uncompleted/(\w+)', srp.views.srp_fleet_mark_uncompleted,
name='auth_srp_fleet_mark_uncompleted'),
url(r'^srp_request_remove/', srp.views.srp_request_remove,
name="auth_srp_request_remove"),
url(r'srp_request_approve/', srp.views.srp_request_approve,
name='auth_srp_request_approve'),
url(r'srp_request_reject/', srp.views.srp_request_reject,
name='auth_srp_request_reject'),
url(_(r'srp_request_amount_update/(\w+)'), srp.views.srp_request_update_amount,
name="auth_srp_request_update_amount"),
# Notifications
url(r'^remove_notifications/(\w+)/$', notifications.views.remove_notification, name='auth_remove_notification'),
url(r'^notifications/mark_all_read/$', notifications.views.mark_all_read, name='auth_mark_all_notifications_read'),
url(r'^notifications/delete_all_read/$', notifications.views.delete_all_read,
name='auth_delete_all_read_notifications'),
]
# User viewed/translated URLS
urlpatterns += i18n_patterns(
# Fleetup
url(r'^fleetup/', include(fleetup.urls.urlpatterns)),
# Authentication
url(_(r'^login_user/'), authentication.views.login_user, name='auth_login_user'),
url(_(r'^register_user/'), authentication.views.register_user_view, name='auth_register_user'),
url(_(r'^user/password/$'), django.contrib.auth.views.password_change, name='password_change'),
url(_(r'^user/password/done/$'), django.contrib.auth.views.password_change_done,
name='password_change_done'),
url(_(r'^user/password/reset/$'), django.contrib.auth.views.password_reset,
name='password_reset'),
url(_(r'^user/password/password/reset/done/$'), django.contrib.auth.views.password_reset_done,
name='password_reset_done'),
url(_(r'^user/password/reset/complete/$'), django.contrib.auth.views.password_reset_complete,
name='password_reset_complete'),
url(_(r'^user/password/reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$'),
django.contrib.auth.views.password_reset_confirm, name='password_reset_confirm'),
# Portal Urls
url(_(r'^dashboard/$'), eveonline.views.dashboard_view, name='auth_dashboard'),
url(_(r'^help/$'), authentication.views.help_view, name='auth_help'),
# Eveonline Urls
url(_(r'^add_api_key/'), eveonline.views.add_api_key, name='auth_add_api_key'),
url(_(r'^refresh_api_pair/([0-9]+)/$'), eveonline.views.user_refresh_api, name='auth_user_refresh_api'),
url(_(r'^delete_api_pair/(\w+)/$'), eveonline.views.api_key_removal, name='auth_api_key_removal'),
url(_(r'^characters/'), eveonline.views.characters_view, name='auth_characters'),
# Corputils
url(_(r'^corpstats/'), include(corputils.urls, namespace='corputils')),
# Group management
url(_(r'^groups/'), groupmanagement.views.groups_view, name='auth_groups'),
url(_(r'^group/management/'), groupmanagement.views.group_management,
name='auth_group_management'),
url(_(r'^group/membership/$'), groupmanagement.views.group_membership,
name='auth_group_membership'),
url(_(r'^group/membership/(\w+)/$'), groupmanagement.views.group_membership_list,
name='auth_group_membership_list'),
url(_(r'^group/membership/(\w+)/remove/(\w+)/$'), groupmanagement.views.group_membership_remove,
name='auth_group_membership_remove'),
url(_(r'^group/request_add/(\w+)'), groupmanagement.views.group_request_add,
name='auth_group_request_add'),
url(_(r'^group/request/accept/(\w+)'), groupmanagement.views.group_accept_request,
name='auth_group_accept_request'),
url(_(r'^group/request/reject/(\w+)'), groupmanagement.views.group_reject_request,
name='auth_group_reject_request'),
url(_(r'^group/request_leave/(\w+)'), groupmanagement.views.group_request_leave,
name='auth_group_request_leave'),
url(_(r'group/leave_request/accept/(\w+)'), groupmanagement.views.group_leave_accept_request,
name='auth_group_leave_accept_request'),
url(_(r'^group/leave_request/reject/(\w+)'), groupmanagement.views.group_leave_reject_request,
name='auth_group_leave_reject_request'),
# HR Application Management
url(_(r'^hr_application_management/'), hrapplications.views.hr_application_management_view,
name="auth_hrapplications_view"),
url(_(r'^hr_application_create/$'), hrapplications.views.hr_application_create_view,
name="auth_hrapplication_create_view"),
url(_(r'^hr_application_create/(\d+)'), hrapplications.views.hr_application_create_view,
name="auth_hrapplication_create_view"),
url(_(r'^hr_application_remove/(\w+)'), hrapplications.views.hr_application_remove,
name="auth_hrapplication_remove"),
url(_(r'hr_application_view/(\w+)'), hrapplications.views.hr_application_view,
name="auth_hrapplication_view"),
url(_(r'hr_application_personal_view/(\w+)'), hrapplications.views.hr_application_personal_view,
name="auth_hrapplication_personal_view"),
url(_(r'hr_application_personal_removal/(\w+)'),
hrapplications.views.hr_application_personal_removal,
name="auth_hrapplication_personal_removal"),
url(_(r'hr_application_approve/(\w+)'), hrapplications.views.hr_application_approve,
name="auth_hrapplication_approve"),
url(_(r'hr_application_reject/(\w+)'), hrapplications.views.hr_application_reject,
name="auth_hrapplication_reject"),
url(_(r'hr_application_search/'), hrapplications.views.hr_application_search,
name="auth_hrapplication_search"),
url(_(r'hr_mark_in_progress/(\w+)'), hrapplications.views.hr_application_mark_in_progress,
name="auth_hrapplication_mark_in_progress"),
# Fleet Operations Timers
url(_(r'^optimer/$'), optimer.views.optimer_view, name='auth_optimer_view'),
url(_(r'^add_optimer/$'), optimer.views.add_optimer_view, name='auth_add_optimer_view'),
url(_(r'^remove_optimer/(\w+)'), optimer.views.remove_optimer, name='auth_remove_optimer'),
url(_(r'^edit_optimer/(\w+)$'), optimer.views.edit_optimer, name='auth_edit_optimer'),
# Service Urls
url(_(r'^services/$'), services.views.services_view, name='auth_services'),
# Timer URLS
url(_(r'^timers/$'), timerboard.views.timer_view, name='auth_timer_view'),
url(_(r'^add_timer/$'), timerboard.views.add_timer_view, name='auth_add_timer_view'),
url(_(r'^remove_timer/(\w+)'), timerboard.views.remove_timer, name='auth_remove_timer'),
url(_(r'^edit_timer/(\w+)$'), timerboard.views.edit_timer, name='auth_edit_timer'),
# SRP URLS
url(_(r'^srp/$'), srp.views.srp_management, name='auth_srp_management_view'),
url(_(r'^srp_all/$'), srp.views.srp_management_all, name='auth_srp_management_all_view'),
url(_(r'^srp_fleet_view/(\w+)$'), srp.views.srp_fleet_view, name='auth_srp_fleet_view'),
url(_(r'^srp_fleet_add_view/$'), srp.views.srp_fleet_add_view, name='auth_srp_fleet_add_view'),
url(_(r'^srp_fleet_edit/(\w+)$'), srp.views.srp_fleet_edit_view, name='auth_srp_fleet_edit_view'),
url(_(r'^srp_request/(\w+)'), srp.views.srp_request_view, name='auth_srp_request_view'),
# Tools
url(_(r'^tool/fleet_formatter_tool/$'), services.views.fleet_formatter_view,
name='auth_fleet_format_tool_view'),
# Notifications
url(_(r'^notifications/$'), notifications.views.notification_list, name='auth_notification_list'),
url(_(r'^notifications/(\w+)/$'), notifications.views.notification_view, name='auth_notification_view'),
# FleetActivityTracking (FAT)
url(r'^fat/$', fleetactivitytracking.views.fatlink_view, name='auth_fatlink_view'),
url(r'^fat/statistics/$', fleetactivitytracking.views.fatlink_statistics_view, name='auth_fatlink_view_statistics'),
url(r'^fat/statistics/corp/(\w+)$', fleetactivitytracking.views.fatlink_statistics_corp_view, name='auth_fatlink_view_statistics_corp'),
url(r'^fat/statistics/corp/(?P<corpid>\w+)/(?P<year>[0-9]+)/(?P<month>[0-9]+)/', fleetactivitytracking.views.fatlink_statistics_corp_view,
name='auth_fatlink_view_statistics_corp_month'),
url(r'^fat/statistics/(?P<year>[0-9]+)/(?P<month>[0-9]+)/$', fleetactivitytracking.views.fatlink_statistics_view,
name='auth_fatlink_view_statistics_month'),
url(r'^fat/user/statistics/$', fleetactivitytracking.views.fatlink_personal_statistics_view,
name='auth_fatlink_view_personal_statistics'),
url(r'^fat/user/statistics/(?P<year>[0-9]+)/$', fleetactivitytracking.views.fatlink_personal_statistics_view,
name='auth_fatlink_view_personal_statistics_year'),
url(r'^fat/user/statistics/(?P<year>[0-9]+)/(?P<month>[0-9]+)/$',
fleetactivitytracking.views.fatlink_monthly_personal_statistics_view,
name='auth_fatlink_view_personal_statistics_month'),
url(r'^fat/user/(?P<char_id>[0-9]+)/statistics/(?P<year>[0-9]+)/(?P<month>[0-9]+)/$',
fleetactivitytracking.views.fatlink_monthly_personal_statistics_view,
name='auth_fatlink_view_user_statistics_month'),
url(r'^fat/create/$', fleetactivitytracking.views.create_fatlink_view, name='auth_create_fatlink_view'),
url(r'^fat/modify/$', fleetactivitytracking.views.modify_fatlink_view, name='auth_modify_fatlink_view'),
url(r'^fat/modify/(?P<hash>[a-zA-Z0-9_-]+)/([a-z0-9_-]+)$',
fleetactivitytracking.views.modify_fatlink_view),
url(r'^fat/link/$', fleetactivitytracking.views.fatlink_view, name='auth_click_fatlink_view'),
url(r'^fat/link/(?P<hash>[a-zA-Z0-9]+)/(?P<fatname>[a-z0-9_-]+)/$',
fleetactivitytracking.views.click_fatlink_view),
url(r'^permissions/', include(permissions_tool.urls))
)
# Append hooked service urls
services = get_hooks('services_hook')
for svc in services:
urlpatterns += svc().urlpatterns

View File

@@ -1,20 +0,0 @@
"""
WSGI config for alliance_auth project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "alliance_auth.settings")
# virtualenv wrapper, uncomment below to activate
# activate_env=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'env/bin/activate_this.py')
# execfile(activate_env, dict(__file__=activate_env))
application = get_wsgi_application()

8
allianceauth/__init__.py Normal file
View File

@@ -0,0 +1,8 @@
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
__version__ = '2.9.0a1'
__title__ = 'Alliance Auth'
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
NAME = '%s v%s' % (__title__, __version__)
default_app_config = 'allianceauth.apps.AllianceAuthConfig'

5
allianceauth/apps.py Normal file
View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class AllianceAuthConfig(AppConfig):
name = 'allianceauth'

View File

@@ -0,0 +1 @@
default_app_config = 'allianceauth.authentication.apps.AuthenticationConfig'

View File

@@ -0,0 +1,615 @@
from django.conf import settings
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User as BaseUser, \
Permission as BasePermission, Group
from django.db.models import Q, F
from allianceauth.services.hooks import ServicesHook
from django.db.models.signals import pre_save, post_save, pre_delete, \
post_delete, m2m_changed
from django.db.models.functions import Lower
from django.dispatch import receiver
from django.forms import ModelForm
from django.utils.html import format_html
from django.urls import reverse
from django.utils.text import slugify
from allianceauth.authentication.models import State, get_guest_state,\
CharacterOwnership, UserProfile, OwnershipRecord
from allianceauth.hooks import get_hooks
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo,\
EveAllianceInfo
from allianceauth.eveonline.tasks import update_character
from .app_settings import AUTHENTICATION_ADMIN_USERS_MAX_GROUPS, \
AUTHENTICATION_ADMIN_USERS_MAX_CHARS
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
_has_auto_groups = True
else:
_has_auto_groups = False
def make_service_hooks_update_groups_action(service):
"""
Make a admin action for the given service
:param service: services.hooks.ServicesHook
:return: fn to update services groups for the selected users
"""
def update_service_groups(modeladmin, request, queryset):
if hasattr(service, 'update_groups_bulk'):
service.update_groups_bulk(queryset)
else:
for user in queryset: # queryset filtering doesn't work here?
service.update_groups(user)
update_service_groups.__name__ = str('update_{}_groups'.format(slugify(service.name)))
update_service_groups.short_description = "Sync groups for selected {} accounts".format(service.title)
return update_service_groups
def make_service_hooks_sync_nickname_action(service):
"""
Make a sync_nickname admin action for the given service
:param service: services.hooks.ServicesHook
:return: fn to sync nickname for the selected users
"""
def sync_nickname(modeladmin, request, queryset):
if hasattr(service, 'sync_nicknames_bulk'):
service.sync_nicknames_bulk(queryset)
else:
for user in queryset: # queryset filtering doesn't work here?
service.sync_nickname(user)
sync_nickname.__name__ = str('sync_{}_nickname'.format(slugify(service.name)))
sync_nickname.short_description = "Sync nicknames for selected {} accounts".format(service.title)
return sync_nickname
class QuerysetModelForm(ModelForm):
# allows specifying FK querysets through kwarg
def __init__(self, querysets=None, *args, **kwargs):
querysets = querysets or {}
super().__init__(*args, **kwargs)
for field, qs in querysets.items():
self.fields[field].queryset = qs
class UserProfileInline(admin.StackedInline):
model = UserProfile
readonly_fields = ('state',)
form = QuerysetModelForm
verbose_name = ''
verbose_name_plural = 'Profile'
def get_formset(self, request, obj=None, **kwargs):
# main_character field can only show current value or unclaimed alts
# if superuser, allow selecting from any unclaimed main
query = Q()
if obj and obj.profile.main_character:
query |= Q(pk=obj.profile.main_character_id)
if request.user.is_superuser:
query |= Q(userprofile__isnull=True)
else:
query |= Q(character_ownership__user=obj)
qs = EveCharacter.objects.filter(query)
formset = super().get_formset(request, obj=obj, **kwargs)
def get_kwargs(self, index):
return {'querysets': {'main_character': EveCharacter.objects.filter(query)}}
formset.get_form_kwargs = get_kwargs
return formset
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
def user_profile_pic(obj):
"""profile pic column data for user objects
works for both User objects and objects with `user` as FK to User
To be used for all user based admin lists (requires CSS)
"""
user_obj = obj.user if hasattr(obj, 'user') else obj
if user_obj.profile.main_character:
return format_html(
'<img src="{}" class="img-circle">',
user_obj.profile.main_character.portrait_url(size=32)
)
else:
return None
user_profile_pic.short_description = ''
def user_username(obj):
"""user column data for user objects
works for both User objects and objects with `user` as FK to User
To be used for all user based admin lists
"""
link = reverse(
'admin:{}_{}_change'.format(
obj._meta.app_label,
type(obj).__name__.lower()
),
args=(obj.pk,)
)
user_obj = obj.user if hasattr(obj, 'user') else obj
if user_obj.profile.main_character:
return format_html(
'<strong><a href="{}">{}</a></strong><br>{}',
link,
user_obj.username,
user_obj.profile.main_character.character_name
)
else:
return format_html(
'<strong><a href="{}">{}</a></strong>',
link,
user_obj.username,
)
user_username.short_description = 'user / main'
user_username.admin_order_field = 'username'
def user_main_organization(obj):
"""main organization column data for user objects
works for both User objects and objects with `user` as FK to User
To be used for all user based admin lists
"""
user_obj = obj.user if hasattr(obj, 'user') else obj
if not user_obj.profile.main_character:
result = None
else:
corporation = user_obj.profile.main_character.corporation_name
if user_obj.profile.main_character.alliance_id:
result = format_html('{}<br>{}',
corporation,
user_obj.profile.main_character.alliance_name
)
else:
result = corporation
return result
user_main_organization.short_description = 'Corporation / Alliance (Main)'
user_main_organization.admin_order_field = \
'profile__main_character__corporation_name'
class MainCorporationsFilter(admin.SimpleListFilter):
"""Custom filter to filter on corporations from mains only
works for both User objects and objects with `user` as FK to User
To be used for all user based admin lists
"""
title = 'corporation'
parameter_name = 'main_corporation_id__exact'
def lookups(self, request, model_admin):
qs = EveCharacter.objects\
.exclude(userprofile=None)\
.values('corporation_id', 'corporation_name')\
.distinct()\
.order_by(Lower('corporation_name'))
return tuple(
[(x['corporation_id'], x['corporation_name']) for x in qs]
)
def queryset(self, request, qs):
if self.value() is None:
return qs.all()
else:
if qs.model == User:
return qs\
.filter(profile__main_character__corporation_id=\
self.value())
else:
return qs\
.filter(user__profile__main_character__corporation_id=\
self.value())
class MainAllianceFilter(admin.SimpleListFilter):
"""Custom filter to filter on alliances from mains only
works for both User objects and objects with `user` as FK to User
To be used for all user based admin lists
"""
title = 'alliance'
parameter_name = 'main_alliance_id__exact'
def lookups(self, request, model_admin):
qs = EveCharacter.objects\
.exclude(alliance_id=None)\
.exclude(userprofile=None)\
.values('alliance_id', 'alliance_name')\
.distinct()\
.order_by(Lower('alliance_name'))
return tuple(
[(x['alliance_id'], x['alliance_name']) for x in qs]
)
def queryset(self, request, qs):
if self.value() is None:
return qs.all()
else:
if qs.model == User:
return qs\
.filter(profile__main_character__alliance_id=self.value())
else:
return qs\
.filter(user__profile__main_character__alliance_id=\
self.value())
def update_main_character_model(modeladmin, request, queryset):
tasks_count = 0
for obj in queryset:
if obj.profile.main_character:
update_character.delay(obj.profile.main_character.character_id)
tasks_count += 1
modeladmin.message_user(
request,
'Update from ESI started for {} characters'.format(tasks_count)
)
update_main_character_model.short_description = \
'Update main character model from ESI'
class UserAdmin(BaseUserAdmin):
"""Extending Django's UserAdmin model
Behavior of groups and characters columns can be configured via settings
"""
class Media:
css = {
"all": ("authentication/css/admin.css",)
}
class RealGroupsFilter(admin.SimpleListFilter):
"""Custom filter to get groups w/o Autogroups"""
title = 'group'
parameter_name = 'group_id__exact'
def lookups(self, request, model_admin):
qs = Group.objects.all().order_by(Lower('name'))
if _has_auto_groups:
qs = qs\
.filter(managedalliancegroup__isnull=True)\
.filter(managedcorpgroup__isnull=True)
return tuple([(x.pk, x.name) for x in qs])
def queryset(self, request, queryset):
if self.value() is None:
return queryset.all()
else:
return queryset.filter(groups__pk=self.value())
def get_actions(self, request):
actions = super(BaseUserAdmin, self).get_actions(request)
actions[update_main_character_model.__name__] = (
update_main_character_model,
update_main_character_model.__name__,
update_main_character_model.short_description
)
for hook in get_hooks('services_hook'):
svc = hook()
# Check update_groups is redefined/overloaded
if svc.update_groups.__module__ != ServicesHook.update_groups.__module__:
action = make_service_hooks_update_groups_action(svc)
actions[action.__name__] = (
action,
action.__name__,
action.short_description
)
# Create sync nickname action if service implements it
if svc.sync_nickname.__module__ != ServicesHook.sync_nickname.__module__:
action = make_service_hooks_sync_nickname_action(svc)
actions[action.__name__] = (
action, action.__name__,
action.short_description
)
return actions
def _list_2_html_w_tooltips(self, my_items: list, max_items: int) -> str:
"""converts list of strings into HTML with cutoff and tooltip"""
items_truncated_str = ', '.join(my_items[:max_items])
if not my_items:
result = None
elif len(my_items) <= max_items:
result = items_truncated_str
else:
items_truncated_str += ', (...)'
items_all_str = ', '.join(my_items)
result = format_html(
'<span data-tooltip="{}" class="tooltip">{}</span>',
items_all_str,
items_truncated_str
)
return result
inlines = BaseUserAdmin.inlines + [UserProfileInline]
ordering = ('username', )
list_select_related = True
show_full_result_count = True
list_display = (
user_profile_pic,
user_username,
'_state',
'_groups',
user_main_organization,
'_characters',
'is_active',
'date_joined',
'_role'
)
list_display_links = None
list_filter = (
'profile__state',
RealGroupsFilter,
MainCorporationsFilter,
MainAllianceFilter,
'is_active',
'date_joined',
'is_staff',
'is_superuser'
)
search_fields = (
'username',
'character_ownerships__character__character_name'
)
def _characters(self, obj):
my_characters = [
x.character.character_name
for x in CharacterOwnership.objects\
.filter(user=obj)\
.order_by('character__character_name')\
.select_related()
]
return self._list_2_html_w_tooltips(
my_characters,
AUTHENTICATION_ADMIN_USERS_MAX_CHARS
)
_characters.short_description = 'characters'
def _state(self, obj):
return obj.profile.state.name
_state.short_description = 'state'
_state.admin_order_field = 'profile__state'
def _groups(self, obj):
if not _has_auto_groups:
my_groups = [x.name for x in obj.groups.order_by('name')]
else:
my_groups = [
x.name for x in obj.groups\
.filter(managedalliancegroup__isnull=True)\
.filter(managedcorpgroup__isnull=True)\
.order_by('name')
]
return self._list_2_html_w_tooltips(
my_groups,
AUTHENTICATION_ADMIN_USERS_MAX_GROUPS
)
_groups.short_description = 'groups'
def _role(self, obj):
if obj.is_superuser:
role = 'Superuser'
elif obj.is_staff:
role = 'Staff'
else:
role = 'User'
return role
_role.short_description = 'role'
def has_change_permission(self, request, obj=None):
return request.user.has_perm('auth.change_user')
def has_add_permission(self, request, obj=None):
return request.user.has_perm('auth.add_user')
def has_delete_permission(self, request, obj=None):
return request.user.has_perm('auth.delete_user')
def formfield_for_manytomany(self, db_field, request, **kwargs):
"""overriding this formfield to have sorted lists in the form"""
if db_field.name == "groups":
kwargs["queryset"] = Group.objects.all().order_by(Lower('name'))
return super().formfield_for_manytomany(db_field, request, **kwargs)
@admin.register(State)
class StateAdmin(admin.ModelAdmin):
list_select_related = True
list_display = ('name', 'priority', '_user_count')
def _user_count(self, obj):
return obj.userprofile_set.all().count()
_user_count.short_description = 'Users'
fieldsets = (
(None, {
'fields': ('name', 'permissions', 'priority'),
}),
('Membership', {
'fields': (
'public',
'member_characters',
'member_corporations',
'member_alliances'
),
})
)
filter_horizontal = [
'member_characters',
'member_corporations',
'member_alliances',
'permissions'
]
def formfield_for_manytomany(self, db_field, request, **kwargs):
"""overriding this formfield to have sorted lists in the form"""
if db_field.name == "member_characters":
kwargs["queryset"] = EveCharacter.objects.all()\
.order_by(Lower('character_name'))
elif db_field.name == "member_corporations":
kwargs["queryset"] = EveCorporationInfo.objects.all()\
.order_by(Lower('corporation_name'))
elif db_field.name == "member_alliances":
kwargs["queryset"] = EveAllianceInfo.objects.all()\
.order_by(Lower('alliance_name'))
return super().formfield_for_manytomany(db_field, request, **kwargs)
def has_delete_permission(self, request, obj=None):
if obj == get_guest_state():
return False
return super(StateAdmin, self).has_delete_permission(request, obj=obj)
def get_fieldsets(self, request, obj=None):
if obj == get_guest_state():
return (
(None, {
'fields': ('permissions', 'priority'),
}),
)
return super(StateAdmin, self).get_fieldsets(request, obj=obj)
class BaseOwnershipAdmin(admin.ModelAdmin):
class Media:
css = {
"all": ("authentication/css/admin.css",)
}
list_select_related = True
list_display = (
user_profile_pic,
user_username,
user_main_organization,
'character',
)
search_fields = (
'user__username',
'character__character_name',
'character__corporation_name',
'character__alliance_name'
)
list_filter = (
MainCorporationsFilter,
MainAllianceFilter,
)
def get_readonly_fields(self, request, obj=None):
if obj and obj.pk:
return 'owner_hash', 'character'
return tuple()
@admin.register(OwnershipRecord)
class OwnershipRecordAdmin(BaseOwnershipAdmin):
list_display = BaseOwnershipAdmin.list_display + ('created',)
@admin.register(CharacterOwnership)
class CharacterOwnershipAdmin(BaseOwnershipAdmin):
def has_add_permission(self, request):
return False
class PermissionAdmin(admin.ModelAdmin):
actions = None
readonly_fields = [field.name for field in BasePermission._meta.fields]
list_display = ('admin_name', 'name', 'codename', 'content_type')
list_filter = ('content_type__app_label',)
@staticmethod
def admin_name(obj):
return str(obj)
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
def has_module_permission(self, request):
return True
def has_change_permission(self, request, obj=None):
# can see list but not edit it
return not obj
# Hack to allow registration of django.contrib.auth models in our authentication app
class User(BaseUser):
class Meta:
proxy = True
verbose_name = BaseUser._meta.verbose_name
verbose_name_plural = BaseUser._meta.verbose_name_plural
class Permission(BasePermission):
class Meta:
proxy = True
verbose_name = BasePermission._meta.verbose_name
verbose_name_plural = BasePermission._meta.verbose_name_plural
try:
admin.site.unregister(BaseUser)
finally:
admin.site.register(User, UserAdmin)
admin.site.register(Permission, PermissionAdmin)
@receiver(pre_save, sender=User)
def redirect_pre_save(sender, signal=None, *args, **kwargs):
pre_save.send(BaseUser, *args, **kwargs)
@receiver(post_save, sender=User)
def redirect_post_save(sender, signal=None, *args, **kwargs):
post_save.send(BaseUser, *args, **kwargs)
@receiver(pre_delete, sender=User)
def redirect_pre_delete(sender, signal=None, *args, **kwargs):
pre_delete.send(BaseUser, *args, **kwargs)
@receiver(post_delete, sender=User)
def redirect_post_delete(sender, signal=None, *args, **kwargs):
post_delete.send(BaseUser, *args, **kwargs)
@receiver(m2m_changed, sender=User.groups.through)
def redirect_m2m_changed_groups(sender, signal=None, *args, **kwargs):
m2m_changed.send(BaseUser, *args, **kwargs)
@receiver(m2m_changed, sender=User.user_permissions.through)
def redirect_m2m_changed_permissions(sender, signal=None, *args, **kwargs):
m2m_changed.send(BaseUser, *args, **kwargs)

View File

@@ -0,0 +1,46 @@
from django.conf import settings
def _clean_setting(
name: str,
default_value: object,
min_value: int = None,
max_value: int = None,
required_type: type = None
):
"""cleans the input for a custom setting
Will use `default_value` if settings does not exit or has the wrong type
or is outside define boundaries (for int only)
Need to define `required_type` if `default_value` is `None`
Will assume `min_value` of 0 for int (can be overriden)
Returns cleaned value for setting
"""
if default_value is None and not required_type:
raise ValueError('You must specify a required_type for None defaults')
if not required_type:
required_type = type(default_value)
if min_value is None and required_type == int:
min_value = 0
if (hasattr(settings, name)
and isinstance(getattr(settings, name), required_type)
and (min_value is None or getattr(settings, name) >= min_value)
and (max_value is None or getattr(settings, name) <= max_value)
):
return getattr(settings, name)
else:
return default_value
AUTHENTICATION_ADMIN_USERS_MAX_GROUPS = \
_clean_setting('AUTHENTICATION_ADMIN_USERS_MAX_GROUPS', 10)
AUTHENTICATION_ADMIN_USERS_MAX_CHARS = \
_clean_setting('AUTHENTICATION_ADMIN_USERS_MAX_CHARS', 5)

View File

@@ -0,0 +1,12 @@
from django.apps import AppConfig
from django.core.checks import register, Tags
class AuthenticationConfig(AppConfig):
name = 'allianceauth.authentication'
label = 'authentication'
def ready(self):
super(AuthenticationConfig, self).ready()
from allianceauth.authentication import checks, signals
register(Tags.security)(checks.check_login_scopes_setting)

View File

@@ -0,0 +1,96 @@
import logging
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User, Permission
from .models import UserProfile, CharacterOwnership, OwnershipRecord
logger = logging.getLogger(__name__)
class StateBackend(ModelBackend):
@staticmethod
def _get_state_permissions(user_obj):
"""returns permissions for state of given user object"""
if hasattr(user_obj, "profile") and user_obj.profile:
return Permission.objects.filter(state=user_obj.profile.state)
else:
return Permission.objects.none()
def get_state_permissions(self, user_obj, obj=None):
return self._get_permissions(user_obj, obj, 'state')
def get_all_permissions(self, user_obj, obj=None):
if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
return set()
if not hasattr(user_obj, '_perm_cache'):
user_obj._perm_cache = self.get_user_permissions(user_obj)
user_obj._perm_cache.update(self.get_group_permissions(user_obj))
user_obj._perm_cache.update(self.get_state_permissions(user_obj))
return user_obj._perm_cache
def authenticate(self, request=None, token=None, **credentials):
if not token:
return None
try:
ownership = CharacterOwnership.objects.get(character__character_id=token.character_id)
if ownership.owner_hash == token.character_owner_hash:
logger.debug('Authenticating {0} by ownership of character {1}'.format(ownership.user, token.character_name))
return ownership.user
else:
logger.debug('{0} has changed ownership. Creating new user account.'.format(token.character_name))
ownership.delete()
return self.create_user(token)
except CharacterOwnership.DoesNotExist:
try:
# insecure legacy main check for pre-sso registration auth installs
profile = UserProfile.objects.get(main_character__character_id=token.character_id)
logger.debug('Authenticating {0} by their main character {1} without active ownership.'.format(profile.user, profile.main_character))
# attach an ownership
token.user = profile.user
CharacterOwnership.objects.create_by_token(token)
return profile.user
except UserProfile.DoesNotExist:
# now we check historical records to see if this is a returning user
records = OwnershipRecord.objects.filter(owner_hash=token.character_owner_hash).filter(character__character_id=token.character_id)
if records.exists():
# we've seen this character owner before. Re-attach to their old user account
user = records[0].user
token.user = user
co = CharacterOwnership.objects.create_by_token(token)
logger.debug('Authenticating {0} by matching owner hash record of character {1}'.format(user, co.character))
if not user.profile.main_character:
# set this as their main by default if they have none
user.profile.main_character = co.character
user.profile.save()
return user
logger.debug('Unable to authenticate character {0}. Creating new user.'.format(token.character_name))
return self.create_user(token)
def create_user(self, token):
username = self.iterate_username(token.character_name) # build unique username off character name
user = User.objects.create_user(username, is_active=False) # prevent login until email set
user.set_unusable_password() # prevent login via password
user.save()
token.user = user
co = CharacterOwnership.objects.create_by_token(token) # assign ownership to this user
user.profile.main_character = co.character # assign main character as token character
user.profile.save()
logger.debug('Created new user {0}'.format(user))
return user
@staticmethod
def iterate_username(name):
name = str.replace(name, "'", "")
name = str.replace(name, ' ', '_')
if User.objects.filter(username__startswith=name).exists():
u = User.objects.filter(username__startswith=name)
num = len(u)
username = "%s_%s" % (name, num)
while u.filter(username=username).exists():
num += 1
username = "%s_%s" % (name, num)
else:
username = name
return username

View File

@@ -0,0 +1,12 @@
from django.core.checks import Error
from django.conf import settings
def check_login_scopes_setting(*args, **kwargs):
errors = []
try:
assert settings.LOGIN_TOKEN_SCOPES
except (AssertionError, AttributeError):
errors.append(Error('LOGIN_TOKEN_SCOPES setting cannot be blank.',
hint='SSO tokens used for logging in must require scopes to be refreshable.'))
return errors

View File

@@ -0,0 +1,68 @@
from django.conf.urls import include
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import PermissionDenied
from functools import wraps
from django.shortcuts import redirect
from django.contrib import messages
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.decorators import login_required
def user_has_main_character(user):
return bool(user.profile.main_character)
def decorate_url_patterns(urls, decorator):
url_list, app_name, namespace = include(urls)
def process_patterns(url_patterns):
for pattern in url_patterns:
if hasattr(pattern, 'url_patterns'):
# this is an include - apply to all nested patterns
process_patterns(pattern.url_patterns)
else:
# this is a pattern
pattern.callback = decorator(pattern.callback)
process_patterns(url_list)
return url_list, app_name, namespace
def main_character_required(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
if user_has_main_character(request.user):
return view_func(request, *args, **kwargs)
messages.error(request, _('A main character is required to perform that action. Add one below.'))
return redirect('authentication:dashboard')
return login_required(_wrapped_view)
def permissions_required(perm, login_url=None, raise_exception=False):
"""
Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page if necessary.
If the raise_exception parameter is given the PermissionDenied exception
is raised.
This decorator is identical to the django permission_required except it
allows for passing a tuple/list of perms that will return true if any one
of them is present.
"""
def check_perms(user):
if isinstance(perm, str):
perms = (perm,)
else:
perms = perm
# First check if the user has the permission (even anon users)
for perm_ in perms:
perm_ = (perm_,)
if user.has_perms(perm_):
return True
# In case the 403 handler should be called raise the exception
if raise_exception:
raise PermissionDenied
# As the last resort, show the login form
return False
return user_passes_test(check_perms, login_url=login_url)

View File

@@ -0,0 +1,8 @@
from django import forms
from django.utils.translation import ugettext_lazy as _
from allianceauth.authentication.models import User
class RegistrationForm(forms.Form):
email = forms.EmailField(label=_('Email'), max_length=254, required=True)
class _meta:
model = User

View File

@@ -0,0 +1,14 @@
from django.conf.urls import url, include
from allianceauth.authentication import views
urlpatterns = [
url(r'^activate/complete/$', views.activation_complete, name='registration_activation_complete'),
# The activation key can make use of any character from the
# URL-safe base64 alphabet, plus the colon as a separator.
url(r'^activate/(?P<activation_key>[-:\w]+)/$', views.ActivationView.as_view(), name='registration_activate'),
url(r'^register/$', views.RegistrationView.as_view(), name='registration_register'),
url(r'^register/complete/$', views.registration_complete, name='registration_complete'),
url(r'^register/closed/$', views.registration_closed, name='registration_disallowed'),
url(r'', include('django.contrib.auth.urls')),
]

View File

@@ -0,0 +1,20 @@
from django.core.management.base import BaseCommand
from allianceauth.authentication.models import UserProfile
class Command(BaseCommand):
help = 'Ensures all main characters have an active ownership'
def handle(self, *args, **options):
profiles = UserProfile.objects.filter(main_character__isnull=False).filter(
main_character__character_ownership__isnull=True)
if profiles.exists():
for profile in profiles:
self.stdout.write(self.style.ERROR(
'{0} does not have an ownership. Resetting user {1} main character.'.format(profile.main_character,
profile.user)))
profile.main_character = None
profile.save()
self.stdout.write(self.style.WARNING('Reset {0} main characters.'.format(profiles.count())))
else:
self.stdout.write(self.style.SUCCESS('All main characters have active ownership.'))

View File

@@ -0,0 +1,75 @@
import logging
from django.db import transaction
from django.db.models import Manager, QuerySet, Q
from allianceauth.eveonline.models import EveCharacter
logger = logging.getLogger(__name__)
def available_states_query(character):
query = Q(public=True)
if character.character_id:
query |= Q(member_characters__character_id=character.character_id)
if character.corporation_id:
query |= Q(member_corporations__corporation_id=character.corporation_id)
if character.alliance_id:
query |= Q(member_alliances__alliance_id=character.alliance_id)
return query
class CharacterOwnershipManager(Manager):
def create_by_token(self, token):
if not EveCharacter.objects.filter(character_id=token.character_id).exists():
EveCharacter.objects.create_character(token.character_id)
return self.create(character=EveCharacter.objects.get(character_id=token.character_id), user=token.user,
owner_hash=token.character_owner_hash)
class StateQuerySet(QuerySet):
def available_to_character(self, character):
return self.filter(available_states_query(character))
def available_to_user(self, user):
if user.profile.main_character:
return self.available_to_character(user.profile.main_character)
else:
return self.none()
def get_for_user(self, user):
states = self.available_to_user(user)
if states.exists():
return states[0]
else:
from allianceauth.authentication.models import get_guest_state
return get_guest_state()
def delete(self):
with transaction.atomic():
for state in self:
for profile in state.userprofile_set.all():
profile.assign_state(state=self.model.objects.exclude(pk=state.pk).get_for_user(profile.user))
super(StateQuerySet, self).delete()
class StateManager(Manager):
def get_queryset(self):
return StateQuerySet(self.model, using=self._db)
def available_to_character(self, character):
return self.get_queryset().available_to_character(character)
def available_to_user(self, user):
return self.get_queryset().available_to_user(user)
def get_for_character(self, character):
states = self.get_queryset().available_to_character(character)
if states.exists():
return states[0]
else:
from allianceauth.authentication.models import get_guest_state
return get_guest_state()
def get_for_user(self, user):
return self.get_queryset().get_for_user(user)

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-09-02 06:07
# Generated by Django 1.10.1 on 2016-09-12 13:04
from __future__ import unicode_literals
from django.db import migrations
@@ -8,11 +8,10 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('services', '0002_auto_20161016_0135'),
('authentication', '0007_remove_authservicesinfo_is_blue'),
('eveonline', '0001_initial'),
('auth', '0001_initial'),
]
operations = [
migrations.DeleteModel(
name='GroupCache',
),
]

View File

@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2016-12-11 23:14
from __future__ import unicode_literals
from django.db import migrations
import logging
logger = logging.getLogger(__name__)
class Migration(migrations.Migration):
dependencies = [
('authentication', '0012_remove_add_delete_authservicesinfo_permissions'),
]
operations = [
# Remove fields
migrations.RemoveField(
model_name='authservicesinfo',
name='discord_uid',
),
migrations.RemoveField(
model_name='authservicesinfo',
name='discourse_enabled',
),
migrations.RemoveField(
model_name='authservicesinfo',
name='forum_username',
),
migrations.RemoveField(
model_name='authservicesinfo',
name='ipboard_username',
),
migrations.RemoveField(
model_name='authservicesinfo',
name='ips4_id',
),
migrations.RemoveField(
model_name='authservicesinfo',
name='ips4_username',
),
migrations.RemoveField(
model_name='authservicesinfo',
name='jabber_username',
),
migrations.RemoveField(
model_name='authservicesinfo',
name='market_username',
),
migrations.RemoveField(
model_name='authservicesinfo',
name='mumble_username',
),
migrations.RemoveField(
model_name='authservicesinfo',
name='smf_username',
),
migrations.RemoveField(
model_name='authservicesinfo',
name='teamspeak3_perm_key',
),
migrations.RemoveField(
model_name='authservicesinfo',
name='teamspeak3_uid',
),
migrations.RemoveField(
model_name='authservicesinfo',
name='xenforo_username',
),
]

View File

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-09-09 23:19
from __future__ import unicode_literals
from django.db import migrations
def create_permission(apps, schema_editor):
User = apps.get_model('auth', 'User')
ContentType = apps.get_model('contenttypes', 'ContentType')
Permission = apps.get_model('auth', 'Permission')
ct = ContentType.objects.get_for_model(User)
Permission.objects.get_or_create(codename="view_fleetup", content_type=ct, name="view_fleetup")
class Migration(migrations.Migration):
dependencies = [
('authentication', '0013_service_modules'),
]
operations = [
migrations.RunPython(create_permission, migrations.RunPython.noop)
]

View File

@@ -0,0 +1,285 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-22 23:09
from __future__ import unicode_literals
import allianceauth.authentication.models
import django.db.models.deletion
from django.conf import settings
from django.contrib.auth.hashers import make_password
from django.db import migrations, models
def create_guest_state(apps, schema_editor):
State = apps.get_model('authentication', 'State')
State.objects.update_or_create(name='Guest', defaults={'priority': 0, 'public': True})
def create_member_state(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
State = apps.get_model('authentication', 'State')
EveAllianceInfo = apps.get_model('eveonline', 'EveAllianceInfo')
EveCorporationInfo = apps.get_model('eveonline', 'EveCorporationInfo')
member_state_name = getattr(settings, 'DEFAULT_AUTH_GROUP', 'Member')
s = State.objects.update_or_create(name=member_state_name, defaults={'priority': 100, 'public': False})[0]
try:
# move group permissions to state
g = Group.objects.get(name=member_state_name)
[s.permissions.add(p.pk) for p in g.permissions.all()]
g.delete()
except Group.DoesNotExist:
pass
# auto-populate member IDs
CORP_IDS = getattr(settings, 'CORP_IDS', [])
ALLIANCE_IDS = getattr(settings, 'ALLIANCE_IDS', [])
[s.member_corporations.add(c.pk) for c in EveCorporationInfo.objects.filter(corporation_id__in=CORP_IDS)]
[s.member_alliances.add(a.pk) for a in EveAllianceInfo.objects.filter(alliance_id__in=ALLIANCE_IDS)]
def create_member_group(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
State = apps.get_model('authentication', 'State')
member_state_name = getattr(settings, 'DEFAULT_AUTH_GROUP', 'Member')
try:
g, _ = Group.objects.get_or_create(name=member_state_name)
# move permissions back
state = State.objects.get(name=member_state_name)
[g.permissions.add(p.pk) for p in state.permissions.all()]
# move users back
for profile in state.userprofile_set.all().select_related('user'):
profile.user.groups.add(g.pk)
except State.DoesNotExist:
pass
def create_blue_state(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
State = apps.get_model('authentication', 'State')
EveAllianceInfo = apps.get_model('eveonline', 'EveAllianceInfo')
EveCorporationInfo = apps.get_model('eveonline', 'EveCorporationInfo')
blue_state_name = getattr(settings, 'DEFAULT_BLUE_GROUP', 'Blue')
s = State.objects.update_or_create(name=blue_state_name, defaults={'priority': 50, 'public': False})[0]
try:
# move group permissions to state
g = Group.objects.get(name=blue_state_name)
[s.permissions.add(p.pk) for p in g.permissions.all()]
g.delete()
except Group.DoesNotExist:
pass
# auto-populate blue member IDs
BLUE_CORP_IDS = getattr(settings, 'BLUE_CORP_IDS', [])
BLUE_ALLIANCE_IDS = getattr(settings, 'BLUE_ALLIANCE_IDS', [])
[s.member_corporations.add(c.pk) for c in EveCorporationInfo.objects.filter(corporation_id__in=BLUE_CORP_IDS)]
[s.member_alliances.add(a.pk) for a in EveAllianceInfo.objects.filter(alliance_id__in=BLUE_ALLIANCE_IDS)]
def create_blue_group(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
State = apps.get_model('authentication', 'State')
blue_state_name = getattr(settings, 'DEFAULT_BLUE_GROUP', 'Blue')
try:
g, _ = Group.objects.get_or_create(name=blue_state_name)
# move permissions back
state = State.objects.get(name=blue_state_name)
[g.permissions.add(p.pk) for p in state.permissions.all()]
# move users back
for profile in state.userprofile_set.all().select_related('user'):
profile.user.groups.add(g.pk)
except State.DoesNotExist:
pass
def purge_tokens(apps, schema_editor):
Token = apps.get_model('esi', 'Token')
Token.objects.filter(refresh_token__isnull=True).delete()
def populate_ownerships(apps, schema_editor):
Token = apps.get_model('esi', 'Token')
CharacterOwnership = apps.get_model('authentication', 'CharacterOwnership')
EveCharacter = apps.get_model('eveonline', 'EveCharacter')
unique_character_owners = [t['character_id'] for t in
Token.objects.all().values('character_id').annotate(n=models.Count('user')) if
t['n'] == 1 and EveCharacter.objects.filter(character_id=t['character_id']).exists()]
tokens = Token.objects.filter(character_id__in=unique_character_owners)
for c_id in unique_character_owners:
# find newest refreshable token and use it as basis for CharacterOwnership
ts = tokens.filter(character_id=c_id).exclude(refresh_token__isnull=True).order_by('created')
if ts.exists():
token = ts[0]
char = EveCharacter.objects.get(character_id=token.character_id)
CharacterOwnership.objects.create(user_id=token.user_id, character_id=char.id, owner_hash=token.character_owner_hash)
def create_profiles(apps, schema_editor):
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
State = apps.get_model('authentication', 'State')
UserProfile = apps.get_model('authentication', 'UserProfile')
EveCharacter = apps.get_model('eveonline', 'EveCharacter')
# grab AuthServicesInfo if they have a unique main_char_id and the EveCharacter exists
unique_mains = [auth['main_char_id'] for auth in
AuthServicesInfo.objects.exclude(main_char_id='').values('main_char_id').annotate(
n=models.Count('main_char_id')) if
auth['n'] == 1 and EveCharacter.objects.filter(character_id=auth['main_char_id']).exists()]
auths = AuthServicesInfo.objects.filter(main_char_id__in=unique_mains).select_related('user')
blue_state_name = getattr(settings, 'DEFAULT_BLUE_GROUP', 'Blue')
member_state_name = getattr(settings, 'DEFAULT_AUTH_GROUP', 'Member')
states = {
'Member': State.objects.get(name=member_state_name),
'Blue': State.objects.get(name=blue_state_name),
}
guest_state = State.objects.get(name='Guest')
for auth in auths:
# carry states and mains forward
state = states.get(auth.state, guest_state)
char = EveCharacter.objects.get(character_id=auth.main_char_id)
UserProfile.objects.create(user=auth.user, state=state, main_character=char)
for auth in AuthServicesInfo.objects.exclude(main_char_id__in=unique_mains).select_related('user'):
# prepare empty profiles
UserProfile.objects.create(user=auth.user, state=guest_state)
def recreate_authservicesinfo(apps, schema_editor):
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
UserProfile = apps.get_model('authentication', 'UserProfile')
User = apps.get_model('auth', 'User')
blue_state_name = getattr(settings, 'DEFAULT_BLUE_GROUP', 'Blue')
member_state_name = getattr(settings, 'DEFAULT_AUTH_GROUP', 'Member')
states = {
member_state_name: 'Member',
blue_state_name: 'Blue',
}
# recreate all missing AuthServicesInfo models
AuthServicesInfo.objects.bulk_create([AuthServicesInfo(user_id=u.pk) for u in User.objects.all()])
# repopulate main characters
for profile in UserProfile.objects.exclude(main_character__isnull=True).select_related('user', 'main_character'):
AuthServicesInfo.objects.update_or_create(user=profile.user,
defaults={'main_char_id': profile.main_character.character_id})
# repopulate states we understand
for profile in UserProfile.objects.exclude(state__name='Guest').filter(
state__name__in=[member_state_name, blue_state_name]).select_related('user', 'state'):
AuthServicesInfo.objects.update_or_create(user=profile.user, defaults={'state': states[profile.state.name]})
def disable_passwords(apps, schema_editor):
User = apps.get_model('auth', 'User')
for u in User.objects.exclude(is_staff=True):
# remove passwords for non-staff users to prevent password-based authentication
# set_unusable_password is unavailable in migrations because :reasons:
u.password = make_password(None)
u.save()
class Migration(migrations.Migration):
dependencies = [
('auth', '0008_alter_user_username_max_length'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('eveonline', '0008_remove_apikeys'),
('authentication', '0014_fleetup_permission'),
('esi', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='CharacterOwnership',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('owner_hash', models.CharField(max_length=28, unique=True)),
('character', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='character_ownership', to='eveonline.EveCharacter')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='character_ownerships', to=settings.AUTH_USER_MODEL)),
],
options={
'default_permissions': ('change', 'delete'),
'ordering': ['user', 'character__character_name'],
},
),
migrations.CreateModel(
name='State',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=20, unique=True)),
('priority', models.IntegerField(help_text='Users get assigned the state with the highest priority available to them.', unique=True)),
('public', models.BooleanField(default=False, help_text='Make this state available to any character.')),
('member_alliances', models.ManyToManyField(blank=True, help_text='Alliances to whose members this state is available.', to='eveonline.EveAllianceInfo')),
('member_characters', models.ManyToManyField(blank=True, help_text='Characters to which this state is available.', to='eveonline.EveCharacter')),
('member_corporations', models.ManyToManyField(blank=True, help_text='Corporations to whose members this state is available.', to='eveonline.EveCorporationInfo')),
('permissions', models.ManyToManyField(blank=True, to='auth.Permission')),
],
options={
'ordering': ['-priority'],
},
),
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('main_character', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='eveonline.EveCharacter')),
('state', models.ForeignKey(default=allianceauth.authentication.models.get_guest_state_pk, on_delete=django.db.models.deletion.SET_DEFAULT, to='authentication.State')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
],
options={
'default_permissions': ('change',),
},
),
migrations.RunPython(create_guest_state, migrations.RunPython.noop),
migrations.RunPython(create_member_state, create_member_group),
migrations.RunPython(create_blue_state, create_blue_group),
migrations.RunPython(purge_tokens, migrations.RunPython.noop),
migrations.RunPython(populate_ownerships, migrations.RunPython.noop),
migrations.RunPython(create_profiles, recreate_authservicesinfo),
migrations.RemoveField(
model_name='authservicesinfo',
name='user',
),
migrations.DeleteModel(
name='AuthServicesInfo',
),
migrations.RunPython(disable_passwords, migrations.RunPython.noop),
migrations.CreateModel(
name='Permission',
fields=[
],
options={
'proxy': True,
'verbose_name': 'permission',
'verbose_name_plural': 'permissions',
},
bases=('auth.permission',),
managers=[
('objects', django.contrib.auth.models.PermissionManager()),
],
),
migrations.CreateModel(
name='User',
fields=[
],
options={
'proxy': True,
'verbose_name': 'user',
'verbose_name_plural': 'users',
},
bases=('auth.user',),
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

View File

@@ -0,0 +1,40 @@
# Generated by Django 2.0.4 on 2018-04-14 18:28
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
def create_initial_records(apps, schema_editor):
OwnershipRecord = apps.get_model('authentication', 'OwnershipRecord')
CharacterOwnership = apps.get_model('authentication', 'CharacterOwnership')
OwnershipRecord.objects.bulk_create([
OwnershipRecord(user=o.user, character=o.character, owner_hash=o.owner_hash) for o in CharacterOwnership.objects.all()
])
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('eveonline', '0009_on_delete'),
('authentication', '0015_user_profiles'),
]
operations = [
migrations.CreateModel(
name='OwnershipRecord',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('owner_hash', models.CharField(db_index=True, max_length=28)),
('created', models.DateTimeField(auto_now=True)),
('character', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ownership_records', to='eveonline.EveCharacter')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ownership_records', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-created'],
},
),
migrations.RunPython(create_initial_records, migrations.RunPython.noop)
]

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
def remove_permission(apps, schema_editor):
User = apps.get_model('auth', 'User')
ContentType = apps.get_model('contenttypes', 'ContentType')
Permission = apps.get_model('auth', 'Permission')
ct = ContentType.objects.get_for_model(User)
Permission.objects.filter(codename="view_fleetup", content_type=ct, name="view_fleetup").delete()
class Migration(migrations.Migration):
dependencies = [
('authentication', '0016_ownershiprecord'),
]
operations = [
migrations.RunPython(remove_permission, migrations.RunPython.noop)
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0017_remove_fleetup_permission'),
]
operations = [
migrations.AlterField(
model_name='characterownership',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='ownershiprecord',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='state',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='userprofile',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -0,0 +1,117 @@
import logging
from django.contrib.auth.models import User, Permission
from django.db import models, transaction
from django.utils.translation import ugettext_lazy as _
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo, EveAllianceInfo
from allianceauth.notifications import notify
from .managers import CharacterOwnershipManager, StateManager
logger = logging.getLogger(__name__)
class State(models.Model):
name = models.CharField(max_length=20, unique=True)
permissions = models.ManyToManyField(Permission, blank=True)
priority = models.IntegerField(unique=True,
help_text="Users get assigned the state with the highest priority available to them.")
member_characters = models.ManyToManyField(EveCharacter, blank=True,
help_text="Characters to which this state is available.")
member_corporations = models.ManyToManyField(EveCorporationInfo, blank=True,
help_text="Corporations to whose members this state is available.")
member_alliances = models.ManyToManyField(EveAllianceInfo, blank=True,
help_text="Alliances to whose members this state is available.")
public = models.BooleanField(default=False, help_text="Make this state available to any character.")
objects = StateManager()
class Meta:
ordering = ['-priority']
def __str__(self):
return self.name
def available_to_character(self, character):
return self in State.objects.available_to_character(character)
def available_to_user(self, user):
return self in State.objects.available_to_user(user)
def delete(self, **kwargs):
with transaction.atomic():
for profile in self.userprofile_set.all():
profile.assign_state(state=State.objects.exclude(pk=self.pk).get_for_user(profile.user))
super(State, self).delete(**kwargs)
def get_guest_state():
try:
return State.objects.get(name='Guest')
except State.DoesNotExist:
return State.objects.create(name='Guest', priority=0, public=True)
def get_guest_state_pk():
return get_guest_state().pk
class UserProfile(models.Model):
class Meta:
default_permissions = ('change',)
user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE)
main_character = models.OneToOneField(EveCharacter, blank=True, null=True, on_delete=models.SET_NULL)
state = models.ForeignKey(State, on_delete=models.SET_DEFAULT, default=get_guest_state_pk)
def assign_state(self, state=None, commit=True):
if not state:
state = State.objects.get_for_user(self.user)
if self.state != state:
self.state = state
if commit:
logger.info('Updating {} state to {}'.format(self.user, self.state))
self.save(update_fields=['state'])
notify(
self.user,
_('State changed to: %s' % state),
_('Your user\'s state is now: %(state)s')
% ({'state': state}),
'info'
)
from allianceauth.authentication.signals import state_changed
state_changed.send(
sender=self.__class__, user=self.user, state=self.state
)
def __str__(self):
return str(self.user)
class CharacterOwnership(models.Model):
class Meta:
default_permissions = ('change', 'delete')
ordering = ['user', 'character__character_name']
character = models.OneToOneField(EveCharacter, on_delete=models.CASCADE, related_name='character_ownership')
owner_hash = models.CharField(max_length=28, unique=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='character_ownerships')
objects = CharacterOwnershipManager()
def __str__(self):
return "%s: %s" % (self.user, self.character)
class OwnershipRecord(models.Model):
character = models.ForeignKey(EveCharacter, on_delete=models.CASCADE, related_name='ownership_records')
owner_hash = models.CharField(max_length=28, db_index=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='ownership_records')
created = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created']
def __str__(self):
return "%s: %s on %s" % (self.user, self.character, self.created)

View File

@@ -0,0 +1,159 @@
import logging
from .models import CharacterOwnership, UserProfile, get_guest_state, State, OwnershipRecord
from django.contrib.auth.models import User
from django.db.models import Q
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete, m2m_changed
from django.dispatch import receiver, Signal
from esi.models import Token
from allianceauth.eveonline.models import EveCharacter
logger = logging.getLogger(__name__)
state_changed = Signal(providing_args=['user', 'state'])
def trigger_state_check(state):
# evaluate all current members to ensure they still have access
for profile in state.userprofile_set.all():
profile.assign_state()
# we may now be available to others with lower states
check_states = State.objects.filter(priority__lt=state.priority)
for profile in UserProfile.objects.filter(state__in=check_states):
if state.available_to_user(profile.user):
profile.assign_state(state)
@receiver(m2m_changed, sender=State.member_characters.through)
def state_member_characters_changed(sender, instance, action, *args, **kwargs):
if action.startswith('post_'):
logger.debug('State {} member characters changed. Re-evaluating membership.'.format(instance))
trigger_state_check(instance)
@receiver(m2m_changed, sender=State.member_corporations.through)
def state_member_corporations_changed(sender, instance, action, *args, **kwargs):
if action.startswith('post_'):
logger.debug('State {} member corporations changed. Re-evaluating membership.'.format(instance))
trigger_state_check(instance)
@receiver(m2m_changed, sender=State.member_alliances.through)
def state_member_alliances_changed(sender, instance, action, *args, **kwargs):
if action.startswith('post_'):
logger.debug('State {} member alliances changed. Re-evaluating membership.'.format(instance))
trigger_state_check(instance)
@receiver(post_save, sender=State)
def state_saved(sender, instance, *args, **kwargs):
logger.debug('State {} saved. Re-evaluating membership.'.format(instance))
trigger_state_check(instance)
# Is there a smarter way to intercept pre_save with a diff main_character or state?
@receiver(post_save, sender=UserProfile)
def reassess_on_profile_save(sender, instance, created, *args, **kwargs):
# catches post_save from profiles to trigger necessary service and state checks
if not created:
update_fields = kwargs.pop('update_fields', []) or []
if 'state' not in update_fields:
logger.debug('Profile for {} saved without state change. Re-evaluating state.'.format(instance.user))
instance.assign_state()
@receiver(post_save, sender=User)
def create_required_models(sender, instance, created, *args, **kwargs):
# ensure all users have a model
if created:
logger.debug('User {} created. Creating default UserProfile.'.format(instance))
UserProfile.objects.get_or_create(user=instance)
@receiver(post_save, sender=Token)
def record_character_ownership(sender, instance, created, *args, **kwargs):
if created:
logger.debug('New token for {0} character {1} saved. Evaluating ownership.'.format(instance.user,
instance.character_name))
if instance.user:
query = Q(owner_hash=instance.character_owner_hash) & Q(user=instance.user)
else:
query = Q(owner_hash=instance.character_owner_hash)
# purge ownership records if the hash or auth user account has changed
CharacterOwnership.objects.filter(character__character_id=instance.character_id).exclude(query).delete()
# create character if needed
if EveCharacter.objects.filter(character_id=instance.character_id).exists() is False:
logger.debug('Token is for a new character. Creating model for {0} ({1})'.format(instance.character_name,
instance.character_id))
EveCharacter.objects.create_character(instance.character_id)
char = EveCharacter.objects.get(character_id=instance.character_id)
# check if we need to create ownership
if instance.user and not CharacterOwnership.objects.filter(
character__character_id=instance.character_id).exists():
logger.debug("Character {0} is not yet owned. Assigning ownership to {1}".format(instance.character_name,
instance.user))
CharacterOwnership.objects.update_or_create(character=char,
defaults={'owner_hash': instance.character_owner_hash,
'user': instance.user})
@receiver(pre_delete, sender=CharacterOwnership)
def validate_main_character(sender, instance, *args, **kwargs):
try:
if instance.user.profile.main_character == instance.character:
logger.info("Ownership of a main character {0} has been revoked. Resetting {1} main character.".format(
instance.character, instance.user))
# clear main character as user no longer owns them
instance.user.profile.main_character = None
instance.user.profile.save()
except UserProfile.DoesNotExist:
# a user is being deleted
pass
@receiver(post_delete, sender=Token)
def validate_ownership(sender, instance, *args, **kwargs):
if not Token.objects.filter(character_owner_hash=instance.character_owner_hash).filter(refresh_token__isnull=False).exists():
logger.info("No remaining tokens to validate ownership of character {0}. Revoking ownership.".format(instance.character_name))
CharacterOwnership.objects.filter(owner_hash=instance.character_owner_hash).delete()
@receiver(pre_save, sender=User)
def assign_state_on_active_change(sender, instance, *args, **kwargs):
# set to guest state if inactive, assign proper state if reactivated
if instance.pk:
old_instance = User.objects.get(pk=instance.pk)
if old_instance.is_active != instance.is_active:
if instance.is_active:
logger.debug("User {0} has been activated. Assigning state.".format(instance))
instance.profile.assign_state()
else:
logger.debug(
"User {0} has been deactivated. Revoking state and assigning to guest state.".format(instance))
instance.profile.state = get_guest_state()
instance.profile.save(update_fields=['state'])
@receiver(post_save, sender=EveCharacter)
def check_state_on_character_update(sender, instance, *args, **kwargs):
# if this is a main character updating, check that user's state
try:
logger.debug("Character {0} has been saved. Assessing owner's state for changes.".format(instance))
instance.userprofile.assign_state()
except UserProfile.DoesNotExist:
logger.debug("Character {0} is not a main character. No state assessment required.".format(instance))
pass
@receiver(post_save, sender=CharacterOwnership)
def ownership_record_creation(sender, instance, created, *args, **kwargs):
if created:
records = OwnershipRecord.objects.filter(owner_hash=instance.owner_hash).filter(character=instance.character)
if records.exists():
if records[0].user == instance.user: # most recent record is sorted first
logger.debug("Already have ownership record of {0} by user {1}".format(instance.character, instance.user))
return
logger.info("Character {0} has a new owner {1}. Creating ownership record.".format(instance.character, instance.user))
OwnershipRecord.objects.create(user=instance.user, character=instance.character, owner_hash=instance.owner_hash)

View File

@@ -0,0 +1,29 @@
/*
CSS for allianceauth admin site
*/
/* styling for profile pic */
.img-circle {
border-radius: 50%;
}
.column-user_profile_pic {
width: 1px;
white-space: nowrap;
}
/* tooltip */
.tooltip {
position: relative ;
}
.tooltip:hover::after {
content: attr(data-tooltip) ;
position: absolute ;
top: 1.1em ;
left: 1em ;
min-width: 200px ;
border: 1px #808080 solid ;
padding: 8px ;
color: black ;
background-color: rgb(255, 255, 204) ;
z-index: 1 ;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,43 @@
import logging
from esi.errors import TokenExpiredError, TokenInvalidError, IncompleteResponseError
from esi.models import Token
from celery import shared_task
from allianceauth.authentication.models import CharacterOwnership
logger = logging.getLogger(__name__)
@shared_task
def check_character_ownership(owner_hash):
tokens = Token.objects.filter(character_owner_hash=owner_hash)
if tokens:
for t in tokens:
old_hash = t.character_owner_hash
try:
t.update_token_data(commit=False)
except (TokenExpiredError, TokenInvalidError):
t.delete()
continue
except (KeyError, IncompleteResponseError):
# We can't validate the hash hasn't changed but also can't assume it has. Abort for now.
logger.warning("Failed to validate owner hash of {0} due to problems contacting SSO servers.".format(
tokens[0].character_name))
break
if not t.character_owner_hash == old_hash:
logger.info(
'Character %s has changed ownership. Revoking %s tokens.' % (t.character_name, tokens.count()))
tokens.delete()
break
if not Token.objects.filter(character_owner_hash=owner_hash).exists():
logger.info('No tokens found with owner hash %s. Revoking ownership.' % owner_hash)
CharacterOwnership.objects.filter(owner_hash=owner_hash).delete()
@shared_task
def check_all_character_ownership():
for c in CharacterOwnership.objects.all().only('owner_hash'):
check_character_ownership.delay(c.owner_hash)

View File

@@ -0,0 +1,165 @@
{% extends "allianceauth/base.html" %}
{% load static %}
{% load i18n %}
{% block page_title %}{% trans "Dashboard" %}{% endblock %}
{% block content %}
<h1 class="page-header text-center">{% trans "Dashboard" %}</h1>
{% if user.is_staff %}
{% include 'allianceauth/admin-status/include.html' %}
{% endif %}
<div class="col-sm-12">
<div class="row vertical-flexbox-row2">
<div class="col-sm-6 text-center">
<div class="panel panel-primary" style="height:100%">
<div class="panel-heading">
<h3 class="panel-title">
{% blocktrans with state=request.user.profile.state %}
Main Character (State: {{ state }})
{% endblocktrans %}
</h3>
</div>
<div class="panel-body">
{% if request.user.profile.main_character %}
{% with request.user.profile.main_character as main %}
<div class="hidden-xs">
<div class="col-lg-4 col-sm-2">
<table class="table">
<tr>
<td class="text-center">
<img class="ra-avatar"src="{{ main.portrait_url_128 }}">
</td>
</tr>
<tr>
<td class="text-center">{{ main.character_name }}</td>
</tr>
</table>
</div>
<div class="col-lg-4 col-sm-2">
<table class="table">
<tr>
<td class="text-center">
<img class="ra-avatar"src="{{ main.corporation_logo_url_128 }}">
</td>
</tr>
<tr>
<td class="text-center">{{ main.corporation_name }}</td>
</tr>
</table>
</div>
<div class="col-lg-4 col-sm-2">
{% if main.alliance_id %}
<table class="table">
<tr>
<td class="text-center">
<img class="ra-avatar"src="{{ main.alliance_logo_url_128 }}">
</td>
</tr>
<tr>
<td class="text-center">{{ main.alliance_name }}</td>
<tr>
</table>
{% endif %}
</div>
</div>
<div class="table visible-xs-block">
<p>
<img class="ra-avatar" src="{{ main.portrait_url_64 }}">
<img class="ra-avatar" src="{{ main.corporation_logo_url_64 }}">
<img class="ra-avatar" src="{{ main.alliance_logo_url_64 }}">
</p>
<p>
<strong>{{ main.character_name }}</strong><br>
{{ main.corporation_name }}<br>
{{ main.alliance_name }}
</p>
</div>
{% endwith %}
{% else %}
<div class="alert alert-danger" role="alert">
{% trans "No main character set." %}
</div>
{% endif %}
<div class="clearfix"></div>
<div class="row">
<div class="col-sm-6 button-wrapper">
<a href="{% url 'authentication:add_character' %}" class="btn btn-block btn-info"
title="Add Character">{% trans 'Add Character' %}</a>
</div>
<div class="col-sm-6 button-wrapper">
<a href="{% url 'authentication:change_main_character' %}" class="btn btn-block btn-info"
title="Change Main Character">{% trans "Change Main" %}</a>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 text-center">
<div class="panel panel-success" style="height:100%">
<div class="panel-heading">
<h3 class="panel-title">{% trans "Group Memberships" %}</h3>
</div>
<div class="panel-body">
<div style="height: 240px;overflow:-moz-scrollbars-vertical;overflow-y:auto;">
<table class="table table-aa">
{% for group in groups %}
<tr>
<td>{{ group.name }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
</div>
</div>
<div class="clearfix"></div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title text-center" style="text-align: center">
{% trans 'Characters' %}
</h3>
</div>
<div class="panel-body">
<table class="table table-aa hidden-xs">
<thead>
<tr>
<th class="text-center"></th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Corp' %}</th>
<th class="text-center">{% trans 'Alliance' %}</th>
</tr>
</thead>
<tbody>
{% for char in characters %}
<tr>
<td class="text-center"><img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}">
</td>
<td class="text-center">{{ char.character_name }}</td>
<td class="text-center">{{ char.corporation_name }}</td>
<td class="text-center">{{ char.alliance_name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<table class="table table-aa visible-xs-block" style="width: 100%">
<tbody>
{% for char in characters %}
<tr>
<td class="text-center" style="vertical-align: middle">
<img class="ra-avatar img-circle" src="{{ char.portrait_url_32 }}">
</td>
<td class="text-center" style="vertical-align: middle; width: 100%">
<strong>{{ char.character_name }}</strong><br>
{{ char.corporation_name }}<br>
{{ char.alliance_name|default:"" }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,55 @@
{% load static %}
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<meta property="og:title" content="{{ SITE_NAME }}">
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{% static 'icons/apple-touch-icon.png' %}">
<meta property="og:description" content="Alliance Auth - An auth system for EVE Online to help in-game organizations manage online service access.">
{% include 'allianceauth/icons.html' %}
<title>{% block title %}{{ SITE_NAME }}{% endblock %}</title>
{% include 'bundles/bootstrap-css.html' %}
{% include 'bundles/fontawesome.html' %}
{% block extra_include %}
{% endblock %}
<style>
body {
background: url('{% static 'authentication/img/background.jpg' %}') no-repeat center center fixed;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
.panel-transparent {
background: rgba(48, 48, 48, 0.7);
color: #ffffff;
}
.panel-body {
}
#lang-select {
width: 40%;
margin-left: auto;
margin-right: auto;
}
{% block extra_style %}
{% endblock %}
</style>
</head>
<body>
<div class="container" style="margin-top:150px">
{% block content %}
{% endblock %}
</div>
</body>
</html>

View File

@@ -0,0 +1,14 @@
{% load i18n %}
<div class="dropdown">
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<select onchange="this.form.submit()" class="form-control" id="lang-select" name="language">
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
</form>
</div>

View File

@@ -0,0 +1,8 @@
{% extends 'public/middle_box.html' %}
{% load static %}
{% block page_title %}Login{% endblock %}
{% block middle_box_content %}
<a href="{% url 'auth_sso_login' %}{% if request.GET.next %}?next={{request.GET.next}}{%endif%}">
<img class="img-responsive center-block" src="{% static 'img/sso/EVE_SSO_Login_Buttons_Large_Black.png' %}" border=0>
</a>
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends 'public/base.html' %}
{% load static %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="col-md-4 col-md-offset-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.level_tag}}">{{ message }}</div>
{% endfor %}
{% endif %}
<div class="panel panel-default panel-transparent">
<div class="panel-body">
<div class="col-md-12">
{% block middle_box_content %}
{% endblock %}
</div>
</div>
{% include 'public/lang_select.html' %}
</div>
</div>
{% endblock %}
{% block extra_include %}
{% include 'bundles/bootstrap-js.html' %}
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends 'public/base.html' %}
{% load static %}
{% load bootstrap %}
{% load i18n %}
{% block page_title %}Registration{% endblock %}
{% block extra_include %}
{% include 'bundles/bootstrap-css.html' %}
{% include 'bundles/fontawesome.html' %}
{% include 'bundles/bootstrap-js.html' %}
{% endblock %}
{% block content %}
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-default panel-transparent">
<div class="panel-body">
<form method="POST">
{% csrf_token %}
{{ form|bootstrap }}
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Register" %}</button>
</form>
</div>
</div>
{% include 'public/lang_select.html' %}
</div>
{% endblock %}

View File

@@ -0,0 +1,5 @@
{% extends 'public/middle_box.html' %}
{% load i18n %}
{% block middle_box_content %}
<div class="alert alert-danger">{% trans 'Invalid or expired activation link.' %}</div>
{% endblock %}

View File

@@ -0,0 +1,13 @@
You're receiving this email because someone has entered this email address while registering for an account on {{ site.domain }}
If this was you, please click on the link below to confirm your email address:
<a href="{{ scheme }}://{{ url }}">Confirm email address</a>
Link not working? Try copy/pasting this URL into your browser:
{{ scheme }}://{{ url }}
This link will expire in {{ expiration_days }} day(s).
If this was not you, it is safe to ignore this email.

View File

@@ -0,0 +1 @@
Confirm your Alliance Auth account email address

View File

@@ -1,5 +1,5 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your
{% blocktrans trimmed %}You're receiving this email because you requested a password reset for your
user account.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}

View File

@@ -0,0 +1,14 @@
{% extends 'public/middle_box.html' %}
{% load bootstrap %}
{% load i18n %}
{% load static %}
{% block page_title %}Register{% endblock %}
{% block middle_box_content %}
<form class="form-signin" role="form" action="" method="POST">
{% csrf_token %}
{{ form|bootstrap }}
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Submit" %}</button>
<br/>
</form>
{% endblock %}

View File

@@ -0,0 +1,18 @@
from django.urls import reverse
def get_admin_change_view_url(obj: object) -> str:
"""returns URL to admin change view for given object"""
return reverse(
'admin:{}_{}_change'.format(
obj._meta.app_label, type(obj).__name__.lower()
),
args=(obj.pk,)
)
def get_admin_search_url(ModelClass: type) -> str:
"""returns URL to search URL for model of given object"""
return '{}{}/'.format(
reverse('admin:app_list', args=(ModelClass._meta.app_label,)),
ModelClass.__name__.lower()
)

View File

@@ -0,0 +1,635 @@
from urllib.parse import quote
from unittest.mock import patch, MagicMock
from django.conf import settings
from django.contrib import admin
from django.contrib.admin.sites import AdminSite
from django.contrib.auth.models import User as BaseUser, Group
from django.test import TestCase, RequestFactory, Client
from allianceauth.authentication.models import (
CharacterOwnership, State, OwnershipRecord
)
from allianceauth.eveonline.models import (
EveCharacter, EveCorporationInfo, EveAllianceInfo
)
from allianceauth.services.hooks import ServicesHook
from allianceauth.tests.auth_utils import AuthUtils
from ..admin import (
BaseUserAdmin,
CharacterOwnershipAdmin,
PermissionAdmin,
StateAdmin,
MainCorporationsFilter,
MainAllianceFilter,
OwnershipRecordAdmin,
User,
UserAdmin,
user_main_organization,
user_profile_pic,
user_username,
update_main_character_model,
make_service_hooks_update_groups_action,
make_service_hooks_sync_nickname_action
)
from . import get_admin_change_view_url, get_admin_search_url
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
_has_auto_groups = True
from allianceauth.eveonline.autogroups.models import AutogroupsConfig
else:
_has_auto_groups = False
MODULE_PATH = 'allianceauth.authentication.admin'
class MockRequest(object):
def __init__(self, user=None):
self.user = user
class TestCaseWithTestData(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
for MyModel in [
EveAllianceInfo, EveCorporationInfo, EveCharacter, Group, User
]:
MyModel.objects.all().delete()
# groups
cls.group_1 = Group.objects.create(
name='Group 1'
)
cls.group_2 = Group.objects.create(
name='Group 2'
)
# user 1 - corp and alliance, normal user
character_1 = EveCharacter.objects.create(
character_id=1001,
character_name='Bruce Wayne',
corporation_id=2001,
corporation_name='Wayne Technologies',
corporation_ticker='WT',
alliance_id=3001,
alliance_name='Wayne Enterprises',
alliance_ticker='WE',
)
character_1a = EveCharacter.objects.create(
character_id=1002,
character_name='Batman',
corporation_id=2001,
corporation_name='Wayne Technologies',
corporation_ticker='WT',
alliance_id=3001,
alliance_name='Wayne Enterprises',
alliance_ticker='WE',
)
alliance = EveAllianceInfo.objects.create(
alliance_id=3001,
alliance_name='Wayne Enterprises',
alliance_ticker='WE',
executor_corp_id=2001
)
EveCorporationInfo.objects.create(
corporation_id=2001,
corporation_name='Wayne Technologies',
corporation_ticker='WT',
member_count=42,
alliance=alliance
)
cls.user_1 = User.objects.create_user(
character_1.character_name.replace(' ', '_'),
'abc@example.com',
'password'
)
CharacterOwnership.objects.create(
character=character_1,
owner_hash='x1' + character_1.character_name,
user=cls.user_1
)
CharacterOwnership.objects.create(
character=character_1a,
owner_hash='x1' + character_1a.character_name,
user=cls.user_1
)
cls.user_1.profile.main_character = character_1
cls.user_1.profile.save()
cls.user_1.groups.add(cls.group_1)
# user 2 - corp only, staff
character_2 = EveCharacter.objects.create(
character_id=1003,
character_name='Clark Kent',
corporation_id=2002,
corporation_name='Daily Planet',
corporation_ticker='DP',
alliance_id=None
)
EveCorporationInfo.objects.create(
corporation_id=2002,
corporation_name='Daily Plane',
corporation_ticker='DP',
member_count=99,
alliance=None
)
cls.user_2 = User.objects.create_user(
character_2.character_name.replace(' ', '_'),
'abc@example.com',
'password'
)
CharacterOwnership.objects.create(
character=character_2,
owner_hash='x1' + character_2.character_name,
user=cls.user_2
)
cls.user_2.profile.main_character = character_2
cls.user_2.profile.save()
cls.user_2.groups.add(cls.group_2)
cls.user_2.is_staff = True
cls.user_2.save()
# user 3 - no main, no group, superuser
character_3 = EveCharacter.objects.create(
character_id=1101,
character_name='Lex Luthor',
corporation_id=2101,
corporation_name='Lex Corp',
corporation_ticker='LC',
alliance_id=None
)
EveCorporationInfo.objects.create(
corporation_id=2101,
corporation_name='Lex Corp',
corporation_ticker='LC',
member_count=666,
alliance=None
)
EveAllianceInfo.objects.create(
alliance_id=3101,
alliance_name='Lex World Domination',
alliance_ticker='LWD',
executor_corp_id=2101
)
cls.user_3 = User.objects.create_user(
character_3.character_name.replace(' ', '_'),
'abc@example.com',
'password'
)
CharacterOwnership.objects.create(
character=character_3,
owner_hash='x1' + character_3.character_name,
user=cls.user_3
)
cls.user_3.is_superuser = True
cls.user_3.save()
def make_generic_search_request(ModelClass: type, search_term: str):
User.objects.create_superuser(
username='superuser', password='secret', email='admin@example.com'
)
c = Client()
c.login(username='superuser', password='secret')
return c.get(
'%s?q=%s' % (get_admin_search_url(ModelClass), quote(search_term))
)
class TestCharacterOwnershipAdmin(TestCaseWithTestData):
def setUp(self):
self.modeladmin = CharacterOwnershipAdmin(
model=User, admin_site=AdminSite()
)
def test_change_view_loads_normally(self):
User.objects.create_superuser(
username='superuser', password='secret', email='admin@example.com'
)
c = Client()
c.login(username='superuser', password='secret')
ownership = self.user_1.character_ownerships.first()
response = c.get(get_admin_change_view_url(ownership))
self.assertEqual(response.status_code, 200)
def test_search_works(self):
obj = CharacterOwnership.objects\
.filter(user=self.user_1)\
.first()
response = make_generic_search_request(type(obj), obj.user.username)
expected = 200
self.assertEqual(response.status_code, expected)
class TestOwnershipRecordAdmin(TestCaseWithTestData):
def setUp(self):
self.modeladmin = OwnershipRecordAdmin(
model=User, admin_site=AdminSite()
)
def test_change_view_loads_normally(self):
User.objects.create_superuser(
username='superuser', password='secret', email='admin@example.com'
)
c = Client()
c.login(username='superuser', password='secret')
ownership_record = OwnershipRecord.objects\
.filter(user=self.user_1)\
.first()
response = c.get(get_admin_change_view_url(ownership_record))
self.assertEqual(response.status_code, 200)
def test_search_works(self):
obj = OwnershipRecord.objects.first()
response = make_generic_search_request(type(obj), obj.user.username)
expected = 200
self.assertEqual(response.status_code, expected)
class TestStateAdmin(TestCaseWithTestData):
def setUp(self):
self.modeladmin = StateAdmin(
model=User, admin_site=AdminSite()
)
def test_change_view_loads_normally(self):
User.objects.create_superuser(
username='superuser', password='secret', email='admin@example.com'
)
c = Client()
c.login(username='superuser', password='secret')
guest_state = AuthUtils.get_guest_state()
response = c.get(get_admin_change_view_url(guest_state))
self.assertEqual(response.status_code, 200)
member_state = AuthUtils.get_member_state()
response = c.get(get_admin_change_view_url(member_state))
self.assertEqual(response.status_code, 200)
def test_search_works(self):
obj = State.objects.first()
response = make_generic_search_request(type(obj), obj.name)
expected = 200
self.assertEqual(response.status_code, expected)
class TestUserAdmin(TestCaseWithTestData):
def setUp(self):
self.factory = RequestFactory()
self.modeladmin = UserAdmin(
model=User, admin_site=AdminSite()
)
self.character_1 = self.user_1.character_ownerships.first().character
def _create_autogroups(self):
"""create autogroups for corps and alliances"""
if _has_auto_groups:
autogroups_config = AutogroupsConfig(
corp_groups = True,
alliance_groups = True
)
autogroups_config.save()
for state in State.objects.all():
autogroups_config.states.add(state)
autogroups_config.update_corp_group_membership(self.user_1)
# column rendering
def test_user_profile_pic_u1(self):
expected = ('<img src="https://images.evetech.net/characters/1001/'
'portrait?size=32" class="img-circle">')
self.assertEqual(user_profile_pic(self.user_1), expected)
def test_user_profile_pic_u3(self):
self.assertIsNone(user_profile_pic(self.user_3))
def test_user_username_u1(self):
expected = (
'<strong><a href="/admin/authentication/user/{}/change/">'
'Bruce_Wayne</a></strong><br>Bruce Wayne'.format(self.user_1.pk)
)
self.assertEqual(user_username(self.user_1), expected)
def test_user_username_u3(self):
expected = (
'<strong><a href="/admin/authentication/user/{}/change/">'
'Lex_Luthor</a></strong>'.format(self.user_3.pk)
)
self.assertEqual(user_username(self.user_3), expected)
def test_user_main_organization_u1(self):
expected = 'Wayne Technologies<br>Wayne Enterprises'
self.assertEqual(user_main_organization(self.user_1), expected)
def test_user_main_organization_u2(self):
expected = 'Daily Planet'
self.assertEqual(user_main_organization(self.user_2), expected)
def test_user_main_organization_u3(self):
expected = None
self.assertEqual(user_main_organization(self.user_3), expected)
def test_characters_u1(self):
expected = 'Batman, Bruce Wayne'
result = self.modeladmin._characters(self.user_1)
self.assertEqual(result, expected)
def test_characters_u2(self):
expected = 'Clark Kent'
result = self.modeladmin._characters(self.user_2)
self.assertEqual(result, expected)
def test_characters_u3(self):
expected = 'Lex Luthor'
result = self.modeladmin._characters(self.user_3)
self.assertEqual(result, expected)
def test_groups_u1(self):
self._create_autogroups()
expected = 'Group 1'
result = self.modeladmin._groups(self.user_1)
self.assertEqual(result, expected)
def test_groups_u2(self):
self._create_autogroups()
expected = 'Group 2'
result = self.modeladmin._groups(self.user_2)
self.assertEqual(result, expected)
def test_groups_u3(self):
self._create_autogroups()
result = self.modeladmin._groups(self.user_3)
self.assertIsNone(result)
@patch(MODULE_PATH + '._has_auto_groups', False)
def test_groups_u1_no_autogroups(self):
expected = 'Group 1'
result = self.modeladmin._groups(self.user_1)
self.assertEqual(result, expected)
@patch(MODULE_PATH + '._has_auto_groups', False)
def test_groups_u2_no_autogroups(self):
expected = 'Group 2'
result = self.modeladmin._groups(self.user_2)
self.assertEqual(result, expected)
@patch(MODULE_PATH + '._has_auto_groups', False)
def test_groups_u3_no_autogroups(self):
result = self.modeladmin._groups(self.user_3)
self.assertIsNone(result)
def test_state(self):
expected = 'Guest'
result = self.modeladmin._state(self.user_1)
self.assertEqual(result, expected)
def test_role_u1(self):
expected = 'User'
result = self.modeladmin._role(self.user_1)
self.assertEqual(result, expected)
def test_role_u2(self):
expected = 'Staff'
result = self.modeladmin._role(self.user_2)
self.assertEqual(result, expected)
def test_role_u3(self):
expected = 'Superuser'
result = self.modeladmin._role(self.user_3)
self.assertEqual(result, expected)
def test_list_2_html_w_tooltips_no_cutoff(self):
items = ['one', 'two', 'three']
expected = 'one, two, three'
result = self.modeladmin._list_2_html_w_tooltips(items, 5)
self.assertEqual(expected, result)
def test_list_2_html_w_tooltips_w_cutoff(self):
items = ['one', 'two', 'three']
expected = ('<span data-tooltip="one, two, three" '
'class="tooltip">one, two, (...)</span>')
result = self.modeladmin._list_2_html_w_tooltips(items, 2)
self.assertEqual(expected, result)
def test_list_2_html_w_tooltips_empty_list(self):
items = []
expected = None
result = self.modeladmin._list_2_html_w_tooltips(items, 5)
self.assertEqual(expected, result)
# actions
@patch(MODULE_PATH + '.UserAdmin.message_user', auto_spec=True)
@patch(MODULE_PATH + '.update_character')
def test_action_update_main_character_model(
self, mock_task, mock_message_user
):
users_qs = User.objects.filter(pk__in=[self.user_1.pk, self.user_2.pk])
update_main_character_model(
self.modeladmin, MockRequest(self.user_1), users_qs
)
self.assertEqual(mock_task.delay.call_count, 2)
self.assertTrue(mock_message_user.called)
# filters
def test_filter_real_groups_with_autogroups(self):
class UserAdminTest(BaseUserAdmin):
list_filter = (UserAdmin.RealGroupsFilter,)
self._create_autogroups()
my_modeladmin = UserAdminTest(User, AdminSite())
# Make sure the lookups are correct
request = self.factory.get('/')
request.user = self.user_1
changelist = my_modeladmin.get_changelist_instance(request)
filters = changelist.get_filters(request)
filterspec = filters[0][0]
expected = [
(self.group_1.pk, self.group_1.name),
(self.group_2.pk, self.group_2.name),
]
self.assertEqual(filterspec.lookup_choices, expected)
# Make sure the correct queryset is returned
request = self.factory.get('/', {'group_id__exact': self.group_1.pk})
request.user = self.user_1
changelist = my_modeladmin.get_changelist_instance(request)
queryset = changelist.get_queryset(request)
expected = User.objects.filter(groups__in=[self.group_1])
self.assertSetEqual(set(queryset), set(expected))
@patch(MODULE_PATH + '._has_auto_groups', False)
def test_filter_real_groups_no_autogroups(self):
class UserAdminTest(BaseUserAdmin):
list_filter = (UserAdmin.RealGroupsFilter,)
my_modeladmin = UserAdminTest(User, AdminSite())
# Make sure the lookups are correct
request = self.factory.get('/')
request.user = self.user_1
changelist = my_modeladmin.get_changelist_instance(request)
filters = changelist.get_filters(request)
filterspec = filters[0][0]
expected = [
(self.group_1.pk, self.group_1.name),
(self.group_2.pk, self.group_2.name),
]
self.assertEqual(filterspec.lookup_choices, expected)
# Make sure the correct queryset is returned
request = self.factory.get('/', {'group_id__exact': self.group_1.pk})
request.user = self.user_1
changelist = my_modeladmin.get_changelist_instance(request)
queryset = changelist.get_queryset(request)
expected = User.objects.filter(groups__in=[self.group_1])
self.assertSetEqual(set(queryset), set(expected))
def test_filter_main_corporations(self):
class UserAdminTest(BaseUserAdmin):
list_filter = (MainCorporationsFilter,)
my_modeladmin = UserAdminTest(User, AdminSite())
# Make sure the lookups are correct
request = self.factory.get('/')
request.user = self.user_1
changelist = my_modeladmin.get_changelist_instance(request)
filters = changelist.get_filters(request)
filterspec = filters[0][0]
expected = [
(2002, 'Daily Planet'),
(2001, 'Wayne Technologies'),
]
self.assertEqual(filterspec.lookup_choices, expected)
# Make sure the correct queryset is returned
request = self.factory.get(
'/',
{'main_corporation_id__exact': self.character_1.corporation_id}
)
request.user = self.user_1
changelist = my_modeladmin.get_changelist_instance(request)
queryset = changelist.get_queryset(request)
expected = [self.user_1]
self.assertSetEqual(set(queryset), set(expected))
def test_filter_main_alliances(self):
class UserAdminTest(BaseUserAdmin):
list_filter = (MainAllianceFilter,)
my_modeladmin = UserAdminTest(User, AdminSite())
# Make sure the lookups are correct
request = self.factory.get('/')
request.user = self.user_1
changelist = my_modeladmin.get_changelist_instance(request)
filters = changelist.get_filters(request)
filterspec = filters[0][0]
expected = [
(3001, 'Wayne Enterprises'),
]
self.assertEqual(filterspec.lookup_choices, expected)
# Make sure the correct queryset is returned
request = self.factory.get(
'/',
{'main_alliance_id__exact': self.character_1.alliance_id}
)
request.user = self.user_1
changelist = my_modeladmin.get_changelist_instance(request)
queryset = changelist.get_queryset(request)
expected = [self.user_1]
self.assertSetEqual(set(queryset), set(expected))
def test_change_view_loads_normally(self):
User.objects.create_superuser(
username='superuser', password='secret', email='admin@example.com'
)
c = Client()
c.login(username='superuser', password='secret')
response = c.get(get_admin_change_view_url(self.user_1))
self.assertEqual(response.status_code, 200)
def test_search_works(self):
obj = User.objects.first()
response = make_generic_search_request(type(obj), obj.username)
expected = 200
self.assertEqual(response.status_code, expected)
class TestMakeServicesHooksActions(TestCaseWithTestData):
class MyServicesHookTypeA(ServicesHook):
def __init__(self):
super().__init__()
self.name = 'My Service A'
def update_groups(self, user):
pass
def sync_nicknames(self, user):
pass
class MyServicesHookTypeB(ServicesHook):
def __init__(self):
super().__init__()
self.name = 'My Service B'
def update_groups(self, user):
pass
def update_groups_bulk(self, user):
pass
def sync_nicknames(self, user):
pass
def sync_nicknames_bulk(self, user):
pass
def test_service_has_update_groups_only(self):
service = self.MyServicesHookTypeA()
mock_service = MagicMock(spec=service)
action = make_service_hooks_update_groups_action(mock_service)
action(MagicMock(), MagicMock(), [self.user_1])
self.assertTrue(mock_service.update_groups.called)
def test_service_has_update_groups_bulk(self):
service = self.MyServicesHookTypeB()
mock_service = MagicMock(spec=service)
action = make_service_hooks_update_groups_action(mock_service)
action(MagicMock(), MagicMock(), [self.user_1])
self.assertFalse(mock_service.update_groups.called)
self.assertTrue(mock_service.update_groups_bulk.called)
def test_service_has_sync_nickname_only(self):
service = self.MyServicesHookTypeA()
mock_service = MagicMock(spec=service)
action = make_service_hooks_sync_nickname_action(mock_service)
action(MagicMock(), MagicMock(), [self.user_1])
self.assertTrue(mock_service.sync_nickname.called)
def test_service_has_sync_nicknames_bulk(self):
service = self.MyServicesHookTypeB()
mock_service = MagicMock(spec=service)
action = make_service_hooks_sync_nickname_action(mock_service)
action(MagicMock(), MagicMock(), [self.user_1])
self.assertFalse(mock_service.sync_nickname.called)
self.assertTrue(mock_service.sync_nicknames_bulk.called)

View File

@@ -0,0 +1,102 @@
from unittest.mock import Mock, patch
from django.test import TestCase
from .. import app_settings
MODULE_PATH = 'allianceauth.authentication'
class TestSetAppSetting(TestCase):
@patch(MODULE_PATH + '.app_settings.settings')
def test_default_if_not_set(self, mock_settings):
mock_settings.TEST_SETTING_DUMMY = Mock(spec=None)
result = app_settings._clean_setting(
'TEST_SETTING_DUMMY',
False,
)
self.assertEqual(result, False)
@patch(MODULE_PATH + '.app_settings.settings')
def test_default_if_not_set_for_none(self, mock_settings):
mock_settings.TEST_SETTING_DUMMY = Mock(spec=None)
result = app_settings._clean_setting(
'TEST_SETTING_DUMMY',
None,
required_type=int
)
self.assertEqual(result, None)
@patch(MODULE_PATH + '.app_settings.settings')
def test_true_stays_true(self, mock_settings):
mock_settings.TEST_SETTING_DUMMY = True
result = app_settings._clean_setting(
'TEST_SETTING_DUMMY',
False,
)
self.assertEqual(result, True)
@patch(MODULE_PATH + '.app_settings.settings')
def test_false_stays_false(self, mock_settings):
mock_settings.TEST_SETTING_DUMMY = False
result = app_settings._clean_setting(
'TEST_SETTING_DUMMY',
False
)
self.assertEqual(result, False)
@patch(MODULE_PATH + '.app_settings.settings')
def test_default_for_invalid_type_bool(self, mock_settings):
mock_settings.TEST_SETTING_DUMMY = 'invalid type'
result = app_settings._clean_setting(
'TEST_SETTING_DUMMY',
False
)
self.assertEqual(result, False)
@patch(MODULE_PATH + '.app_settings.settings')
def test_default_for_invalid_type_int(self, mock_settings):
mock_settings.TEST_SETTING_DUMMY = 'invalid type'
result = app_settings._clean_setting(
'TEST_SETTING_DUMMY',
50
)
self.assertEqual(result, 50)
@patch(MODULE_PATH + '.app_settings.settings')
def test_default_if_below_minimum_1(self, mock_settings):
mock_settings.TEST_SETTING_DUMMY = -5
result = app_settings._clean_setting(
'TEST_SETTING_DUMMY',
default_value=50
)
self.assertEqual(result, 50)
@patch(MODULE_PATH + '.app_settings.settings')
def test_default_if_below_minimum_2(self, mock_settings):
mock_settings.TEST_SETTING_DUMMY = -50
result = app_settings._clean_setting(
'TEST_SETTING_DUMMY',
default_value=50,
min_value=-10
)
self.assertEqual(result, 50)
@patch(MODULE_PATH + '.app_settings.settings')
def test_default_for_invalid_type_int(self, mock_settings):
mock_settings.TEST_SETTING_DUMMY = 1000
result = app_settings._clean_setting(
'TEST_SETTING_DUMMY',
default_value=50,
max_value=100
)
self.assertEqual(result, 50)
@patch(MODULE_PATH + '.app_settings.settings')
def test_default_is_none_needs_required_type(self, mock_settings):
mock_settings.TEST_SETTING_DUMMY = 'invalid type'
with self.assertRaises(ValueError):
result = app_settings._clean_setting(
'TEST_SETTING_DUMMY',
default_value=None
)

View File

@@ -0,0 +1,149 @@
from django.contrib.auth.models import User, Group
from django.test import TestCase
from allianceauth.eveonline.models import EveCharacter
from allianceauth.tests.auth_utils import AuthUtils
from esi.models import Token
from ..backends import StateBackend
from ..models import CharacterOwnership, UserProfile, OwnershipRecord
MODULE_PATH = 'allianceauth.authentication'
PERMISSION_1 = "authentication.add_user"
PERMISSION_2 = "authentication.change_user"
class TestStatePermissions(TestCase):
def setUp(self):
# permissions
self.permission_1 = AuthUtils.get_permission_by_name(PERMISSION_1)
self.permission_2 = AuthUtils.get_permission_by_name(PERMISSION_2)
# group
self.group_1 = Group.objects.create(name="Group 1")
self.group_2 = Group.objects.create(name="Group 2")
# state
self.state_1 = AuthUtils.get_member_state()
self.state_2 = AuthUtils.create_state("Other State", 75)
# user
self.user = AuthUtils.create_user("Bruce Wayne")
self.main = AuthUtils.add_main_character_2(self.user, self.user.username, 123)
def test_user_has_user_permissions(self):
self.user.user_permissions.add(self.permission_1)
user = User.objects.get(pk=self.user.pk)
self.assertTrue(user.has_perm(PERMISSION_1))
def test_user_has_group_permissions(self):
self.group_1.permissions.add(self.permission_1)
self.user.groups.add(self.group_1)
user = User.objects.get(pk=self.user.pk)
self.assertTrue(user.has_perm(PERMISSION_1))
def test_user_has_state_permissions(self):
self.state_1.permissions.add(self.permission_1)
self.state_1.member_characters.add(self.main)
user = User.objects.get(pk=self.user.pk)
self.assertTrue(user.has_perm(PERMISSION_1))
def test_when_user_changes_state_perms_change_accordingly(self):
self.state_1.permissions.add(self.permission_1)
self.state_1.member_characters.add(self.main)
user = User.objects.get(pk=self.user.pk)
self.assertTrue(user.has_perm(PERMISSION_1))
self.state_2.permissions.add(self.permission_2)
self.state_2.member_characters.add(self.main)
self.state_1.member_characters.remove(self.main)
user = User.objects.get(pk=self.user.pk)
self.assertFalse(user.has_perm(PERMISSION_1))
self.assertTrue(user.has_perm(PERMISSION_2))
def test_state_permissions_are_returned_for_current_user_object(self):
# verify state permissions are returns for the current user object
# and not for it's instance in the database, which might be outdated
self.state_1.permissions.add(self.permission_1)
self.state_2.permissions.add(self.permission_2)
self.state_1.member_characters.add(self.main)
user = User.objects.get(pk=self.user.pk)
user.profile.state = self.state_2
self.assertFalse(user.has_perm(PERMISSION_1))
self.assertTrue(user.has_perm(PERMISSION_2))
class TestAuthenticate(TestCase):
@classmethod
def setUpTestData(cls):
cls.main_character = EveCharacter.objects.create(
character_id=1,
character_name='Main Character',
corporation_id=1,
corporation_name='Corp',
corporation_ticker='CORP',
)
cls.alt_character = EveCharacter.objects.create(
character_id=2,
character_name='Alt Character',
corporation_id=1,
corporation_name='Corp',
corporation_ticker='CORP',
)
cls.unclaimed_character = EveCharacter.objects.create(
character_id=3,
character_name='Unclaimed Character',
corporation_id=1,
corporation_name='Corp',
corporation_ticker='CORP',
)
cls.user = AuthUtils.create_user('test_user', disconnect_signals=True)
cls.old_user = AuthUtils.create_user('old_user', disconnect_signals=True)
AuthUtils.disconnect_signals()
CharacterOwnership.objects.create(user=cls.user, character=cls.main_character, owner_hash='1')
CharacterOwnership.objects.create(user=cls.user, character=cls.alt_character, owner_hash='2')
UserProfile.objects.update_or_create(user=cls.user, defaults={'main_character': cls.main_character})
AuthUtils.connect_signals()
def test_authenticate_main_character(self):
t = Token(character_id=self.main_character.character_id, character_owner_hash='1')
user = StateBackend().authenticate(token=t)
self.assertEquals(user, self.user)
def test_authenticate_alt_character(self):
t = Token(character_id=self.alt_character.character_id, character_owner_hash='2')
user = StateBackend().authenticate(token=t)
self.assertEquals(user, self.user)
def test_authenticate_unclaimed_character(self):
t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='3')
user = StateBackend().authenticate(token=t)
self.assertNotEqual(user, self.user)
self.assertEqual(user.username, 'Unclaimed_Character')
self.assertEqual(user.profile.main_character, self.unclaimed_character)
def test_authenticate_character_record(self):
t = Token(character_id=self.unclaimed_character.character_id, character_name=self.unclaimed_character.character_name, character_owner_hash='4')
OwnershipRecord.objects.create(user=self.old_user, character=self.unclaimed_character, owner_hash='4')
user = StateBackend().authenticate(token=t)
self.assertEqual(user, self.old_user)
self.assertTrue(CharacterOwnership.objects.filter(owner_hash='4', user=self.old_user).exists())
self.assertTrue(user.profile.main_character)
def test_iterate_username(self):
t = Token(character_id=self.unclaimed_character.character_id,
character_name=self.unclaimed_character.character_name, character_owner_hash='3')
username = StateBackend().authenticate(token=t).username
t.character_owner_hash = '4'
username_1 = StateBackend().authenticate(token=t).username
t.character_owner_hash = '5'
username_2 = StateBackend().authenticate(token=t).username
self.assertNotEqual(username, username_1, username_2)
self.assertTrue(username_1.endswith('_1'))
self.assertTrue(username_2.endswith('_2'))

View File

@@ -0,0 +1,35 @@
from io import StringIO
from django.core.management import call_command
from django.test import TestCase
from allianceauth.tests.auth_utils import AuthUtils
from ..models import CharacterOwnership, UserProfile
class ManagementCommandTestCase(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = AuthUtils.create_user('test user', disconnect_signals=True)
AuthUtils.add_main_character(cls.user, 'test character', '1', '2', 'test corp', 'test')
character = UserProfile.objects.get(user=cls.user).main_character
CharacterOwnership.objects.create(user=cls.user, character=character, owner_hash='test')
def setUp(self):
self.stdout = StringIO()
def test_ownership(self):
call_command('checkmains', stdout=self.stdout)
self.assertFalse(UserProfile.objects.filter(main_character__isnull=True).count())
self.assertNotIn(self.user.username, self.stdout.getvalue())
self.assertIn('All main characters', self.stdout.getvalue())
def test_no_ownership(self):
user = AuthUtils.create_user('v1 user', disconnect_signals=True)
AuthUtils.add_main_character(user, 'v1 character', '10', '20', 'test corp', 'test')
self.assertFalse(UserProfile.objects.filter(main_character__isnull=True).count())
call_command('checkmains', stdout=self.stdout)
self.assertEqual(UserProfile.objects.filter(main_character__isnull=True).count(), 1)
self.assertIn(user.username, self.stdout.getvalue())

View File

@@ -0,0 +1,68 @@
from unittest import mock
from urllib import parse
from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.http.response import HttpResponse
from django.shortcuts import reverse
from django.test import TestCase
from django.test.client import RequestFactory
from allianceauth.eveonline.models import EveCharacter
from allianceauth.tests.auth_utils import AuthUtils
from ..decorators import main_character_required
from ..models import CharacterOwnership
MODULE_PATH = 'allianceauth.authentication'
class DecoratorTestCase(TestCase):
@staticmethod
@main_character_required
def dummy_view(*args, **kwargs):
return HttpResponse(status=200)
@classmethod
def setUpTestData(cls):
cls.main_user = AuthUtils.create_user('main_user', disconnect_signals=True)
cls.no_main_user = AuthUtils.create_user(
'no_main_user', disconnect_signals=True
)
main_character = EveCharacter.objects.create(
character_id=1,
character_name='Main Character',
corporation_id=1,
corporation_name='Corp',
corporation_ticker='CORP',
)
CharacterOwnership.objects.create(
user=cls.main_user, character=main_character, owner_hash='1'
)
cls.main_user.profile.main_character = main_character
def setUp(self):
self.request = RequestFactory().get('/test/')
@mock.patch(MODULE_PATH + '.decorators.messages')
def test_login_redirect(self, m):
setattr(self.request, 'user', AnonymousUser())
response = self.dummy_view(self.request)
self.assertEqual(response.status_code, 302)
url = getattr(response, 'url', None)
self.assertEqual(parse.urlparse(url).path, reverse(settings.LOGIN_URL))
@mock.patch(MODULE_PATH + '.decorators.messages')
def test_main_character_redirect(self, m):
setattr(self.request, 'user', self.no_main_user)
response = self.dummy_view(self.request)
self.assertEqual(response.status_code, 302)
url = getattr(response, 'url', None)
self.assertEqual(url, reverse('authentication:dashboard'))
@mock.patch(MODULE_PATH + '.decorators.messages')
def test_successful_request(self, m):
setattr(self.request, 'user', self.main_user)
response = self.dummy_view(self.request)
self.assertEqual(response.status_code, 200)

View File

@@ -0,0 +1,253 @@
from unittest import mock
from django.contrib.auth.models import User
from django.test import TestCase
from allianceauth.eveonline.models import EveCharacter, EveCorporationInfo,\
EveAllianceInfo
from allianceauth.tests.auth_utils import AuthUtils
from esi.errors import IncompleteResponseError
from esi.models import Token
from ..models import CharacterOwnership, State, get_guest_state
from ..tasks import check_character_ownership
MODULE_PATH = 'allianceauth.authentication'
class CharacterOwnershipTestCase(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = AuthUtils.create_user('user', disconnect_signals=True)
cls.alt_user = AuthUtils.create_user('alt_user', disconnect_signals=True)
cls.character = EveCharacter.objects.create(
character_id=1,
character_name='Main Character',
corporation_id=1,
corporation_name='Corp',
corporation_ticker='CORP',
)
def test_create_ownership(self):
Token.objects.create(
user=self.user,
character_id=self.character.character_id,
character_name=self.character.character_name,
character_owner_hash='1',
)
co = CharacterOwnership.objects.get(character=self.character)
self.assertEquals(co.user, self.user)
self.assertEquals(co.owner_hash, '1')
def test_transfer_ownership(self):
Token.objects.create(
user=self.user,
character_id=self.character.character_id,
character_name=self.character.character_name,
character_owner_hash='1',
)
Token.objects.create(
user=self.alt_user,
character_id=self.character.character_id,
character_name=self.character.character_name,
character_owner_hash='2',
)
co = CharacterOwnership.objects.get(character=self.character)
self.assertNotEqual(self.user, co.user)
self.assertEquals(self.alt_user, co.user)
def test_clear_main_character(self):
Token.objects.create(
user=self.user,
character_id=self.character.character_id,
character_name=self.character.character_name,
character_owner_hash='1',
)
self.user.profile.main_character = self.character
self.user.profile.save()
Token.objects.create(
user=self.alt_user,
character_id=self.character.character_id,
character_name=self.character.character_name,
character_owner_hash='2',
)
self.user = User.objects.get(pk=self.user.pk)
self.assertIsNone(self.user.profile.main_character)
class StateTestCase(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = AuthUtils.create_user('test_user', disconnect_signals=True)
AuthUtils.add_main_character(cls.user, 'Test Character', '1', corp_id='1', alliance_id='1',
corp_name='Test Corp', alliance_name='Test Alliance')
cls.guest_state = get_guest_state()
cls.test_character = EveCharacter.objects.get(character_id='1')
cls.test_corporation = EveCorporationInfo.objects.create(corporation_id='1', corporation_name='Test Corp',
corporation_ticker='TEST', member_count=1)
cls.test_alliance = EveAllianceInfo.objects.create(alliance_id='1', alliance_name='Test Alliance',
alliance_ticker='TEST', executor_corp_id='1')
cls.member_state = State.objects.create(
name='Test Member',
priority=150,
)
def _refresh_user(self):
self.user = User.objects.get(pk=self.user.pk)
def test_state_assignment_on_character_change(self):
self.member_state.member_characters.add(self.test_character)
self._refresh_user()
self.assertEquals(self.user.profile.state, self.member_state)
self.member_state.member_characters.remove(self.test_character)
self._refresh_user()
self.assertEquals(self.user.profile.state, self.guest_state)
def test_state_assignment_on_corporation_change(self):
self.member_state.member_corporations.add(self.test_corporation)
self._refresh_user()
self.assertEquals(self.user.profile.state, self.member_state)
self.member_state.member_corporations.remove(self.test_corporation)
self._refresh_user()
self.assertEquals(self.user.profile.state, self.guest_state)
def test_state_assignment_on_alliance_addition(self):
self.member_state.member_alliances.add(self.test_alliance)
self._refresh_user()
self.assertEquals(self.user.profile.state, self.member_state)
self.member_state.member_alliances.remove(self.test_alliance)
self._refresh_user()
self.assertEquals(self.user.profile.state, self.guest_state)
def test_state_assignment_on_higher_priority_state_creation(self):
self.member_state.member_characters.add(self.test_character)
higher_state = State.objects.create(
name='Higher State',
priority=200,
)
higher_state.member_characters.add(self.test_character)
self._refresh_user()
self.assertEquals(higher_state, self.user.profile.state)
higher_state.member_characters.clear()
self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state)
self.member_state.member_characters.clear()
def test_state_assignment_on_lower_priority_state_creation(self):
self.member_state.member_characters.add(self.test_character)
lower_state = State.objects.create(
name='Lower State',
priority=125,
)
lower_state.member_characters.add(self.test_character)
self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state)
lower_state.member_characters.clear()
self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state)
self.member_state.member_characters.clear()
def test_state_assignment_on_priority_change(self):
self.member_state.member_characters.add(self.test_character)
lower_state = State.objects.create(
name='Lower State',
priority=125,
)
lower_state.member_characters.add(self.test_character)
self._refresh_user()
lower_state.priority = 500
lower_state.save()
self._refresh_user()
self.assertEquals(lower_state, self.user.profile.state)
lower_state.priority = 125
lower_state.save()
self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state)
def test_state_assignment_on_state_deletion(self):
self.member_state.member_characters.add(self.test_character)
higher_state = State.objects.create(
name='Higher State',
priority=200,
)
higher_state.member_characters.add(self.test_character)
self._refresh_user()
self.assertEquals(higher_state, self.user.profile.state)
higher_state.delete()
self.assertFalse(State.objects.filter(name='Higher State').count())
self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state)
def test_state_assignment_on_public_toggle(self):
self.member_state.member_characters.add(self.test_character)
higher_state = State.objects.create(
name='Higher State',
priority=200,
)
self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state)
higher_state.public = True
higher_state.save()
self._refresh_user()
self.assertEquals(higher_state, self.user.profile.state)
higher_state.public = False
higher_state.save()
self._refresh_user()
self.assertEquals(self.member_state, self.user.profile.state)
def test_state_assignment_on_active_changed(self):
self.member_state.member_characters.add(self.test_character)
self.user.is_active = False
self.user.save()
self._refresh_user()
self.assertEquals(self.user.profile.state, self.guest_state)
self.user.is_active = True
self.user.save()
self._refresh_user()
self.assertEquals(self.user.profile.state, self.member_state)
class CharacterOwnershipCheckTestCase(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = AuthUtils.create_user('test_user', disconnect_signals=True)
AuthUtils.add_main_character(cls.user, 'Test Character', '1', corp_id='1', alliance_id='1',
corp_name='Test Corp', alliance_name='Test Alliance')
cls.character = EveCharacter.objects.get(character_id=1)
cls.token = Token.objects.create(
user=cls.user,
character_id=1,
character_name='Test',
character_owner_hash='1',
)
cls.ownership = CharacterOwnership.objects.get(character=cls.character)
@mock.patch(MODULE_PATH + '.tasks.Token.update_token_data')
def test_no_change_owner_hash(self, update_token_data):
# makes sure the ownership isn't delete if owner hash hasn't changed
check_character_ownership(self.ownership)
self.assertTrue(CharacterOwnership.objects.filter(user=self.user).filter(character=self.character).exists())
@mock.patch(MODULE_PATH + '.tasks.Token.update_token_data')
def test_unable_to_update_token_data(self, update_token_data):
# makes sure ownerships and tokens aren't hellpurged when there's problems with the SSO servers
update_token_data.side_effect = IncompleteResponseError()
check_character_ownership(self.ownership)
self.assertTrue(CharacterOwnership.objects.filter(user=self.user).filter(character=self.character).exists())
update_token_data.side_effect = KeyError()
check_character_ownership(self.ownership)
self.assertTrue(CharacterOwnership.objects.filter(user=self.user).filter(character=self.character).exists())
@mock.patch(MODULE_PATH + '.tasks.Token.update_token_data')
@mock.patch(MODULE_PATH + '.tasks.Token.delete')
@mock.patch(MODULE_PATH + '.tasks.Token.objects.exists')
@mock.patch(MODULE_PATH + '.tasks.CharacterOwnership.objects.filter')
def test_owner_hash_changed(self, filter, exists, delete, update_token_data):
# makes sure the ownership is revoked when owner hash changes
filter.return_value.exists.return_value = False
check_character_ownership(self.ownership)
self.assertTrue(filter.return_value.delete.called)

View File

@@ -0,0 +1,290 @@
from math import ceil
from unittest.mock import patch
from requests import RequestException
import requests_mock
from packaging.version import Version as Pep440Version
from django.test import TestCase
from allianceauth.templatetags.admin_status import (
status_overview,
_fetch_list_from_gitlab,
_current_notifications,
_current_version_summary,
_fetch_notification_issues_from_gitlab,
_fetch_tags_from_gitlab,
_latests_versions
)
MODULE_PATH = 'allianceauth.templatetags'
def create_tags_list(tag_names: list):
return [{'name': str(tag_name)} for tag_name in tag_names]
GITHUB_TAGS = create_tags_list(['v2.4.6a1', 'v2.4.5', 'v2.4.0', 'v2.0.0', 'v1.1.1'])
GITHUB_NOTIFICATION_ISSUES = [
{
'id': 1,
'title': 'first issue'
},
{
'id': 2,
'title': 'second issue'
},
{
'id': 3,
'title': 'third issue'
},
{
'id': 4,
'title': 'forth issue'
},
{
'id': 5,
'title': 'fifth issue'
},
{
'id': 6,
'title': 'sixth issue'
},
]
TEST_VERSION = '2.6.5'
class TestStatusOverviewTag(TestCase):
@patch(MODULE_PATH + '.admin_status.__version__', TEST_VERSION)
@patch(MODULE_PATH + '.admin_status._fetch_celery_queue_length')
@patch(MODULE_PATH + '.admin_status._current_version_summary')
@patch(MODULE_PATH + '.admin_status._current_notifications')
def test_status_overview(
self,
mock_current_notifications,
mock_current_version_info,
mock_fetch_celery_queue_length
):
notifications = {
'notifications': GITHUB_NOTIFICATION_ISSUES[:5]
}
mock_current_notifications.return_value = notifications
version_info = {
'latest_major': True,
'latest_minor': True,
'latest_patch': True,
'latest_beta': False,
'current_version': TEST_VERSION,
'latest_major_version': '2.4.5',
'latest_minor_version': '2.4.0',
'latest_patch_version': '2.4.5',
'latest_beta_version': '2.4.4a1',
}
mock_current_version_info.return_value = version_info
mock_fetch_celery_queue_length.return_value = 3
result = status_overview()
expected = {
'notifications': GITHUB_NOTIFICATION_ISSUES[:5],
'latest_major': True,
'latest_minor': True,
'latest_patch': True,
'latest_beta': False,
'current_version': TEST_VERSION,
'latest_major_version': '2.4.5',
'latest_minor_version': '2.4.0',
'latest_patch_version': '2.4.5',
'latest_beta_version': '2.4.4a1',
'task_queue_length': 3,
}
self.assertEqual(result, expected)
class TestNotifications(TestCase):
@requests_mock.mock()
def test_fetch_notification_issues_from_gitlab(self, requests_mocker):
url = (
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth/issues'
'?labels=announcement'
)
requests_mocker.get(url, json=GITHUB_NOTIFICATION_ISSUES)
result = _fetch_notification_issues_from_gitlab()
self.assertEqual(result, GITHUB_NOTIFICATION_ISSUES)
@patch(MODULE_PATH + '.admin_status.cache')
def test_current_notifications_normal(self, mock_cache):
mock_cache.get_or_set.return_value = GITHUB_NOTIFICATION_ISSUES
result = _current_notifications()
self.assertEqual(result['notifications'], GITHUB_NOTIFICATION_ISSUES[:5])
@patch(MODULE_PATH + '.admin_status.cache')
def test_current_notifications_failed(self, mock_cache):
mock_cache.get_or_set.side_effect = RequestException
result = _current_notifications()
self.assertEqual(result['notifications'], list())
@patch(MODULE_PATH + '.admin_status.cache')
def test_current_notifications_is_none(self, mock_cache):
mock_cache.get_or_set.return_value = None
result = _current_notifications()
self.assertEqual(result['notifications'], list())
class TestCeleryQueueLength(TestCase):
def test_get_celery_queue_length(self):
pass
class TestVersionTags(TestCase):
@patch(MODULE_PATH + '.admin_status.__version__', TEST_VERSION)
@patch(MODULE_PATH + '.admin_status.cache')
def test_current_version_info_normal(self, mock_cache):
mock_cache.get_or_set.return_value = GITHUB_TAGS
result = _current_version_summary()
self.assertTrue(result['latest_major'])
self.assertTrue(result['latest_minor'])
self.assertTrue(result['latest_patch'])
self.assertEqual(result['latest_major_version'], '2.0.0')
self.assertEqual(result['latest_minor_version'], '2.4.0')
self.assertEqual(result['latest_patch_version'], '2.4.5')
self.assertEqual(result['latest_beta_version'], '2.4.6a1')
@patch(MODULE_PATH + '.admin_status.__version__', TEST_VERSION)
@patch(MODULE_PATH + '.admin_status.cache')
def test_current_version_info_failed(self, mock_cache):
mock_cache.get_or_set.side_effect = RequestException
expected = {}
result = _current_version_summary()
self.assertEqual(result, expected)
@requests_mock.mock()
def test_fetch_tags_from_gitlab(self, requests_mocker):
url = (
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth'
'/repository/tags'
)
requests_mocker.get(url, json=GITHUB_TAGS)
result = _fetch_tags_from_gitlab()
self.assertEqual(result, GITHUB_TAGS)
@patch(MODULE_PATH + '.admin_status.__version__', TEST_VERSION)
@patch(MODULE_PATH + '.admin_status.cache')
def test_current_version_info_return_no_data(self, mock_cache):
mock_cache.get_or_set.return_value = None
expected = {}
result = _current_version_summary()
self.assertEqual(result, expected)
class TestLatestsVersion(TestCase):
def test_all_version_types_defined(self):
tags = create_tags_list(
['2.1.1', '2.1.0', '2.0.0', '2.1.1a1', '1.1.1', '1.1.0', '1.0.0']
)
major, minor, patch, beta = _latests_versions(tags)
self.assertEqual(major, Pep440Version('2.0.0'))
self.assertEqual(minor, Pep440Version('2.1.0'))
self.assertEqual(patch, Pep440Version('2.1.1'))
self.assertEqual(beta, Pep440Version('2.1.1a1'))
def test_major_and_minor_not_defined_with_zero(self):
tags = create_tags_list(
['2.1.2', '2.1.1', '2.0.1', '2.1.1a1', '1.1.1', '1.1.0', '1.0.0']
)
major, minor, patch, beta = _latests_versions(tags)
self.assertEqual(major, Pep440Version('2.0.1'))
self.assertEqual(minor, Pep440Version('2.1.1'))
self.assertEqual(patch, Pep440Version('2.1.2'))
self.assertEqual(beta, Pep440Version('2.1.1a1'))
def test_can_ignore_invalid_versions(self):
tags = create_tags_list(
['2.1.1', '2.1.0', '2.0.0', '2.1.1a1', 'invalid']
)
major, minor, patch, beta = _latests_versions(tags)
self.assertEqual(major, Pep440Version('2.0.0'))
self.assertEqual(minor, Pep440Version('2.1.0'))
self.assertEqual(patch, Pep440Version('2.1.1'))
self.assertEqual(beta, Pep440Version('2.1.1a1'))
class TestFetchListFromGitlab(TestCase):
page_size = 2
def setUp(self):
self.url = (
'https://gitlab.com/api/v4/projects/allianceauth%2Fallianceauth'
'/repository/tags'
)
@classmethod
def my_callback(cls, request, context):
page = int(request.qs['page'][0])
start = (page - 1) * cls.page_size
end = start + cls.page_size
return GITHUB_TAGS[start:end]
@requests_mock.mock()
def test_can_fetch_one_page_with_header(self, requests_mocker):
headers = {
'x-total-pages': '1'
}
requests_mocker.get(self.url, json=GITHUB_TAGS, headers=headers)
result = _fetch_list_from_gitlab(self.url)
self.assertEqual(result, GITHUB_TAGS)
self.assertEqual(requests_mocker.call_count, 1)
@requests_mock.mock()
def test_can_fetch_one_page_wo_header(self, requests_mocker):
requests_mocker.get(self.url, json=GITHUB_TAGS)
result = _fetch_list_from_gitlab(self.url)
self.assertEqual(result, GITHUB_TAGS)
self.assertEqual(requests_mocker.call_count, 1)
@requests_mock.mock()
def test_can_fetch_one_page_and_ignore_invalid_header(self, requests_mocker):
headers = {
'x-total-pages': 'invalid'
}
requests_mocker.get(self.url, json=GITHUB_TAGS, headers=headers)
result = _fetch_list_from_gitlab(self.url)
self.assertEqual(result, GITHUB_TAGS)
self.assertEqual(requests_mocker.call_count, 1)
@requests_mock.mock()
def test_can_fetch_multiple_pages(self, requests_mocker):
total_pages = ceil(len(GITHUB_TAGS) / self.page_size)
headers = {
'x-total-pages': str(total_pages)
}
requests_mocker.get(self.url, json=self.my_callback, headers=headers)
result = _fetch_list_from_gitlab(self.url)
self.assertEqual(result, GITHUB_TAGS)
self.assertEqual(requests_mocker.call_count, total_pages)
@requests_mock.mock()
def test_can_fetch_given_number_of_pages_only(self, requests_mocker):
total_pages = ceil(len(GITHUB_TAGS) / self.page_size)
headers = {
'x-total-pages': str(total_pages)
}
requests_mocker.get(self.url, json=self.my_callback, headers=headers)
max_pages = 2
result = _fetch_list_from_gitlab(self.url, max_pages=max_pages)
self.assertEqual(result, GITHUB_TAGS[:4])
self.assertEqual(requests_mocker.call_count, max_pages)

View File

@@ -0,0 +1,27 @@
from django.conf.urls import url
from django.contrib.auth.decorators import login_required
from django.views.generic.base import TemplateView
from . import views
app_name = 'authentication'
urlpatterns = [
url(r'^$', views.index, name='index'),
url(
r'^account/login/$',
TemplateView.as_view(template_name='public/login.html'),
name='login'
),
url(
r'^account/characters/main/$',
views.main_character_change,
name='change_main_character'
),
url(
r'^account/characters/add/$',
views.add_character,
name='add_character'
),
url(r'^dashboard/$', views.dashboard, name='dashboard'),
]

View File

@@ -0,0 +1,218 @@
import logging
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import login, authenticate
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core import signing
from django.http import JsonResponse
from django.shortcuts import redirect, render
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _
from allianceauth.eveonline.models import EveCharacter
from esi.decorators import token_required
from esi.models import Token
from django_registration.backends.activation.views import (
RegistrationView as BaseRegistrationView,
ActivationView as BaseActivationView,
REGISTRATION_SALT
)
from django_registration.signals import user_registered
from .models import CharacterOwnership
from .forms import RegistrationForm
if 'allianceauth.eveonline.autogroups' in settings.INSTALLED_APPS:
_has_auto_groups = True
from allianceauth.eveonline.autogroups.models import *
else:
_has_auto_groups = False
logger = logging.getLogger(__name__)
@login_required
def index(request):
return redirect('authentication:dashboard')
@login_required
def dashboard(request):
groups = request.user.groups.all()
if _has_auto_groups:
groups = groups\
.filter(managedalliancegroup__isnull=True)\
.filter(managedcorpgroup__isnull=True)
groups = groups.order_by('name')
characters = EveCharacter.objects\
.filter(character_ownership__user=request.user)\
.select_related()\
.order_by('character_name')
context = {
'groups': groups,
'characters': characters
}
return render(request, 'authentication/dashboard.html', context)
@login_required
@token_required(scopes=settings.LOGIN_TOKEN_SCOPES)
def main_character_change(request, token):
logger.debug("main_character_change called by user %s for character %s" % (request.user, token.character_name))
try:
co = CharacterOwnership.objects.get(character__character_id=token.character_id, user=request.user)
except CharacterOwnership.DoesNotExist:
if not CharacterOwnership.objects.filter(character__character_id=token.character_id).exists():
co = CharacterOwnership.objects.create_by_token(token)
else:
messages.error(
request,
_('Cannot change main character to %(char)s: character owned by a different account.') % ({'char': token.character_name})
)
co = None
if co:
request.user.profile.main_character = co.character
request.user.profile.save(update_fields=['main_character'])
messages.success(request, _('Changed main character to %(char)s') % {"char": co.character})
logger.info('Changed user %(user)s main character to %(char)s' % ({'user': request.user, 'char': co.character}))
return redirect("authentication:dashboard")
@token_required(new=True, scopes=settings.LOGIN_TOKEN_SCOPES)
def add_character(request, token):
if CharacterOwnership.objects.filter(character__character_id=token.character_id).filter(
owner_hash=token.character_owner_hash).filter(user=request.user).exists():
messages.success(request, _('Added %(name)s to your account.'% ({'name': token.character_name})))
else:
messages.error(request, _('Failed to add %(name)s to your account: they already have an account.' % ({'name': token.character_name})))
return redirect('authentication:dashboard')
"""
Override the HMAC two-step registration view to accommodate the three-step registration required.
Step 1: OAuth token to create user and profile.
Step 2: Get email and send activation link (but do not save email).
Step 3: Get link, save email and activate.
Step 1 is necessary to automatically assign character ownership and a main character, both of which require a saved User
model - this means the ensuing registration form cannot create the user because it already exists.
Email is not saved to the user model in Step 2 as a way of differentiating users who have not yet completed registration
(is_active=False) and users who have been disabled by an admin (is_active=False, email present).
Because of this, the email address needs to be assigned in Step 3 after clicking the link, which means the link must
have the email address embedded much like the username. Key creation and decoding is overridden to support this action.
"""
# Step 1
@token_required(new=True, scopes=settings.LOGIN_TOKEN_SCOPES)
def sso_login(request, token):
user = authenticate(token=token)
if user:
token.user = user
if Token.objects.exclude(pk=token.pk).equivalent_to(token).require_valid().exists():
token.delete()
else:
token.save()
if user.is_active:
login(request, user)
return redirect(request.POST.get('next', request.GET.get('next', 'authentication:dashboard')))
elif not user.email:
# Store the new user PK in the session to enable us to identify the registering user in Step 2
request.session['registration_uid'] = user.pk
# Go to Step 2
return redirect('registration_register')
messages.error(request, _('Unable to authenticate as the selected character.'))
return redirect(settings.LOGIN_URL)
# Step 2
class RegistrationView(BaseRegistrationView):
form_class = RegistrationForm
template_name = "public/register.html"
email_body_template = "registration/activation_email.txt"
email_subject_template = "registration/activation_email_subject.txt"
success_url = reverse_lazy('registration_complete')
def get_success_url(self, user):
if not getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
return reverse_lazy('authentication:dashboard')
return super().get_success_url(user)
def dispatch(self, request, *args, **kwargs):
# We're storing a key in the session to pass user information from OAuth response. Make sure it's there.
if not self.request.session.get('registration_uid', None) or not User.objects.filter(
pk=self.request.session.get('registration_uid')).exists():
messages.error(self.request, _('Registration token has expired.'))
return redirect(settings.LOGIN_URL)
if not getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
# Keep the request so the user can be automagically logged in.
setattr(self, 'request', request)
return super(RegistrationView, self).dispatch(request, *args, **kwargs)
def register(self, form):
user = User.objects.get(pk=self.request.session.get('registration_uid'))
user.email = form.cleaned_data['email']
user_registered.send(self.__class__, user=user, request=self.request)
if getattr(settings, 'REGISTRATION_VERIFY_EMAIL', True):
# Go to Step 3
self.send_activation_email(user)
else:
user.is_active = True
user.save()
login(self.request, user, 'allianceauth.authentication.backends.StateBackend')
return user
def get_activation_key(self, user):
return signing.dumps(obj=[getattr(user, User.USERNAME_FIELD), user.email], salt=REGISTRATION_SALT)
def get_email_context(self, activation_key):
context = super(RegistrationView, self).get_email_context(activation_key)
context['url'] = context['site'].domain + reverse('registration_activate', args=[activation_key])
return context
# Step 3
class ActivationView(BaseActivationView):
template_name = "registration/activate.html"
success_url = reverse_lazy('registration_activation_complete')
def validate_key(self, activation_key):
try:
dump = signing.loads(activation_key, salt=REGISTRATION_SALT,
max_age=settings.ACCOUNT_ACTIVATION_DAYS * 86400)
return dump
except signing.BadSignature:
return None
def activate(self, *args, **kwargs):
dump = self.validate_key(kwargs.get('activation_key'))
if dump:
user = self.get_user(dump[0])
if user:
user.email = dump[1]
user.is_active = True
user.save()
return user
return False
def registration_complete(request):
messages.success(request, _('Sent confirmation email. Please follow the link to confirm your email address.'))
return redirect('authentication:login')
def activation_complete(request):
messages.success(request, _('Confirmed your email address. Please login to continue.'))
return redirect('authentication:dashboard')
def registration_closed(request):
messages.error(request, _('Registration of new accounts is not allowed at this time.'))
return redirect('authentication:login')

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env python
import os
import shutil
from optparse import OptionParser
from django.core.management import call_command
from django.core.management.commands.startproject import Command as BaseStartProject
class StartProject(BaseStartProject):
def add_arguments(self, parser):
super().add_arguments(parser)
parser.add_argument('--python', help='The path to the python executable.')
parser.add_argument('--celery', help='The path to the celery executable.')
parser.add_argument('--gunicorn', help='The path to the gunicorn executable.')
def create_project(parser, options, args):
# Validate args
if len(args) < 2:
parser.error("Please specify a name for your Alliance Auth installation.")
elif len(args) > 3:
parser.error("Too many arguments.")
# First find the path to Alliance Auth
import allianceauth
allianceauth_path = os.path.dirname(allianceauth.__file__)
template_path = os.path.join(allianceauth_path, 'project_template')
# Determine locations of commands to render supervisor cond
command_options = {
'template': template_path,
'python': shutil.which('python'),
'gunicorn': shutil.which('gunicorn'),
'celery': shutil.which('celery'),
'extensions': ['py', 'conf', 'json'],
}
# Strip 'start' out of the arguments, leaving project name (and optionally destination dir)
args = args[1:]
# Call the command with extra context
call_command(StartProject(), *args, **command_options)
print("Success! %(project_name)s has been created." % {'project_name': args[0]}) # noqa
def update_settings(parser, options, args):
if len(args) < 2:
parser.error("Please specify the path to your Alliance Auth installation.")
elif len(args) > 2:
parser.error("Too many arguments.")
project_path = args[1]
project_name = os.path.split(project_path)[-1]
# find the target settings/base.py file, handing both the project and app as valid paths
# first check if given path is to the app
settings_path = os.path.join(project_path, 'settings/base.py')
if not os.path.exists(settings_path):
# next check if given path is to the project, so the app is within it
settings_path = os.path.join(project_path, project_name, 'settings/base.py')
if not os.path.exists(settings_path):
parser.error("Unable to locate the Alliance Auth project at %s" % project_path)
# first find the path to the Alliance Auth template settings
import allianceauth
allianceauth_path = os.path.dirname(allianceauth.__file__)
template_path = os.path.join(allianceauth_path, 'project_template')
template_settings_path = os.path.join(template_path, 'project_name/settings/base.py')
# overwrite the local project's base settings
with open(template_settings_path, 'r') as template, open(settings_path, 'w') as target:
target.write(template.read())
print("Successfully updated %(project_name)s settings." % {'project_name': project_name})
COMMANDS = {
'start': create_project,
'update': update_settings,
}
def main():
# Parse options
parser = OptionParser(usage="Usage: %prog [start|update] project_name [directory]")
(options, args) = parser.parse_args()
# Find command
try:
command = args[0]
except IndexError:
parser.print_help()
return
if command in COMMANDS:
COMMANDS[command](parser, options, args)
else:
parser.error("Unrecognised command: " + command)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,9 @@
from django.conf import settings
from .views import NightModeRedirectView
def auth_settings(request):
return {
'SITE_NAME': settings.SITE_NAME,
'NIGHT_MODE': NightModeRedirectView.night_mode_state(request),
}

View File

@@ -0,0 +1 @@
default_app_config = 'allianceauth.corputils.apps.CorpUtilsConfig'

View File

@@ -0,0 +1,6 @@
from django.contrib import admin
from .models import CorpStats, CorpMember
admin.site.register(CorpStats)
admin.site.register(CorpMember)

View File

@@ -1,7 +1,6 @@
from __future__ import unicode_literals
from django.apps import AppConfig
class CorpUtilsConfig(AppConfig):
name = 'corputils'
name = 'allianceauth.corputils'
label = 'corputils'

View File

@@ -0,0 +1,30 @@
from allianceauth.services.hooks import MenuItemHook, UrlHook
from django.utils.translation import ugettext_lazy as _
from allianceauth import hooks
from allianceauth.corputils import urls
class CorpStats(MenuItemHook):
def __init__(self):
MenuItemHook.__init__(self,
_('Corporation Stats'),
'fas fa-share-alt fa-fw',
'corputils:view',
navactive=['corputils:'])
def render(self, request):
if request.user.has_perm('corputils.view_corp_corpstats') or request.user.has_perm(
'corputils.view_alliance_corpstats') or request.user.has_perm(
'corputils.add_corpstats') or request.user.has_perm('corputils.view_state_corpstats'):
return MenuItemHook.render(self, request)
return ''
@hooks.register('menu_item_hook')
def register_menu():
return CorpStats()
@hooks.register('url_hook')
def register_url():
return UrlHook(urls, 'corputils', r'^corpstats/')

View File

@@ -0,0 +1,48 @@
from django.db import models
import logging
logger = logging.getLogger(__name__)
class CorpStatsQuerySet(models.QuerySet):
def visible_to(self, user):
# superusers get all visible
if user.is_superuser:
logger.debug('Returning all corpstats for superuser %s.' % user)
return self
try:
char = user.profile.main_character
assert char
# build all accepted queries
queries = [models.Q(token__user=user)]
if user.has_perm('corputils.view_alliance_corpstats'):
if char.alliance_id is not None:
queries.append(models.Q(corp__alliance__alliance_id=char.alliance_id))
else:
queries.append(models.Q(corp__corporation_id=char.corporation_id))
if user.has_perm('corputils.view_corp_corpstats'):
if user.has_perm('corputils.view_alliance_corpstats'):
pass
else:
queries.append(models.Q(corp__corporation_id=char.corporation_id))
if user.has_perm('corputils.view_state_corpstats'):
queries.append(models.Q(corp__in=user.profile.state.member_corporations.all()))
queries.append(models.Q(corp__alliance__in=user.profile.state.member_alliances.all()))
logger.debug('%s queries for user %s visible corpstats.' % (len(queries), user))
# filter based on queries
query = queries.pop()
for q in queries:
query |= q
return self.filter(query)
except AssertionError:
logger.debug('User %s has no main character. No corpstats visible.' % user)
return self.none()
class CorpStatsManager(models.Manager):
def get_queryset(self):
return CorpStatsQuerySet(self.model, using=self._db)
def visible_to(self, user):
return self.get_queryset().visible_to(user)

View File

@@ -19,6 +19,7 @@ PERMISSIONS = {
}
}
def user_permissions_dict(apps):
Permission = apps.get_model('auth', 'Permission')
ContentType = apps.get_model('contenttypes', 'ContentType')
@@ -33,14 +34,17 @@ def user_permissions_dict(apps):
'corpstats': {x: Permission.objects.get_or_create(codename=x, name=y, content_type=corpstats_ct)[0] for x, y in PERMISSIONS['corpstats'].items()},
}
def users_with_permission(apps, perm):
User = apps.get_model('auth', 'User')
return User.objects.filter(user_permissions=perm.pk)
def groups_with_permission(apps, perm):
Group = apps.get_model('auth', 'Group')
return Group.objects.filter(permissions=perm.pk)
def forward(apps, schema_editor):
perm_dict = user_permissions_dict(apps)
@@ -66,8 +70,9 @@ def forward(apps, schema_editor):
for name, perm in perm_dict['user'].items():
perm.delete()
def reverse(apps, schema_editor):
def reverse(apps, schema_editor):
perm_dict = user_permissions_dict(apps)
corp_users = users_with_permission(apps, perm_dict['corpstats']['view_corp_corpstats'])
@@ -79,7 +84,6 @@ def reverse(apps, schema_editor):
u.user_permissions.remove(perm_dict['corpstats']['view_corp_corpstats'].pk)
for u in corp_api_users:
u.user_permissions.remove(perm_dict['corpstats']['corp_apis'].pk)
alliance_users = users_with_permission(apps, perm_dict['corpstats']['view_alliance_corpstats'])
alliance_api_users = users_with_permission(apps, perm_dict['corpstats']['alliance_apis'])
@@ -93,7 +97,6 @@ def reverse(apps, schema_editor):
corp_groups = groups_with_permission(apps, perm_dict['corpstats']['view_corp_corpstats'])
corp_api_groups = groups_with_permission(apps, perm_dict['corpstats']['corp_apis'])
corp_gs = corp_groups | corp_api_groups
for g in corp_groups.distinct():
g.permissions.add(perm_dict['user']['corp_apis'].pk)
for g in corp_groups:

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-22 23:35
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('corputils', '0002_migrate_permissions'),
]
operations = [
migrations.AlterModelOptions(
name='corpstats',
options={'permissions': (('corp_apis', 'Can view API keys of members of their corporation.'), ('alliance_apis', 'Can view API keys of members of their alliance.'), ('blue_apis', 'Can view API keys of members of blue corporations.'), ('view_corp_corpstats', 'Can view corp stats of their corporation.'), ('view_alliance_corpstats', 'Can view corp stats of members of their alliance.'), ('view_blue_corpstats', 'Can view corp stats of blue corporations.')), 'verbose_name': 'corp stats', 'verbose_name_plural': 'corp stats'},
),
]

View File

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-26 20:13
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import json
def convert_json_to_members(apps, schema_editor):
CorpStats = apps.get_model('corputils', 'CorpStats')
CorpMember = apps.get_model('corputils', 'CorpMember')
for cs in CorpStats.objects.all():
members = json.loads(cs._members)
CorpMember.objects.bulk_create(
[CorpMember(corpstats=cs, character_id=member_id, character_name=member_name) for member_id, member_name in
members.items()]
)
def convert_members_to_json(apps, schema_editor):
CorpStats = apps.get_model('corputils', 'CorpStats')
for cs in CorpStats.objects.all():
cs._members = json.dumps({m.character_id: m.character_name for m in cs.members.all()})
cs.save()
class Migration(migrations.Migration):
dependencies = [
('corputils', '0003_granular_permissions'),
]
operations = [
migrations.CreateModel(
name='CorpMember',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('character_id', models.PositiveIntegerField()),
('character_name', models.CharField(max_length=37)),
],
options={
'ordering': ['character_name'],
},
),
migrations.AddField(
model_name='corpmember',
name='corpstats',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='members',
to='corputils.CorpStats'),
),
migrations.AlterUniqueTogether(
name='corpmember',
unique_together=set([('corpstats', 'character_id')]),
),
migrations.RunPython(convert_json_to_members, convert_members_to_json),
migrations.RemoveField(
model_name='corpstats',
name='_members',
),
]

View File

@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-06-10 15:34
from __future__ import unicode_literals
from django.db import migrations
def delete_permissions(apps, schema_editor):
CorpStats = apps.get_model('corputils', 'CorpStats')
ContentType = apps.get_model('contenttypes', 'ContentType')
Permission = apps.get_model('auth', 'Permission')
ct = ContentType.objects.get_for_model(CorpStats)
perms = Permission.objects.filter(content_type=ct)
perms.filter(codename__contains='api').delete()
perms.filter(codename='view_corpstats').delete()
perms.filter(codename__contains='blue').delete()
perms.filter(codename__contains='remove').delete()
g = perms.get(codename='view_corp_corpstats')
g.name = 'Can view corp stats of their corporation.'
g.save()
g = perms.get(codename='view_alliance_corpstats')
g.name = 'Can view corp stats of members of their alliance.'
g.save()
class Migration(migrations.Migration):
dependencies = [
('corputils', '0004_member_models'),
]
operations = [
migrations.AlterModelOptions(
name='corpstats',
options={'permissions': (('view_corp_corpstats', 'Can view corp stats of their corporation.'), ('view_alliance_corpstats', 'Can view corp stats of members of their alliance.'), ('view_state_corpstats', 'Can view corp stats of members of their auth state.')), 'verbose_name': 'corp stats', 'verbose_name_plural': 'corp stats'},
),
migrations.RunPython(delete_permissions, migrations.RunPython.noop),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.2 on 2021-04-07 16:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('corputils', '0005_cleanup_permissions'),
]
operations = [
migrations.AlterField(
model_name='corpmember',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.AlterField(
model_name='corpstats',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View File

@@ -0,0 +1,201 @@
import logging
import os
from allianceauth.authentication.models import CharacterOwnership, UserProfile
from bravado.exception import HTTPForbidden
from django.db import models
from esi.errors import TokenError
from esi.models import Token
from allianceauth.eveonline.models import EveCorporationInfo, EveCharacter,\
EveAllianceInfo
from allianceauth.notifications import notify
from allianceauth.corputils.managers import CorpStatsManager
SWAGGER_SPEC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'swagger.json')
"""
Swagger spec operations:
Character
get_characters_character_id
get_corporations_corporation_id_members
Universe
post_universe_names
"""
logger = logging.getLogger(__name__)
class CorpStats(models.Model):
token = models.ForeignKey(Token, on_delete=models.CASCADE)
corp = models.OneToOneField(EveCorporationInfo, on_delete=models.CASCADE)
last_update = models.DateTimeField(auto_now=True)
class Meta:
permissions = (
('view_corp_corpstats', 'Can view corp stats of their corporation.'),
('view_alliance_corpstats', 'Can view corp stats of members of their alliance.'),
('view_state_corpstats', 'Can view corp stats of members of their auth state.'),
)
verbose_name = "corp stats"
verbose_name_plural = "corp stats"
objects = CorpStatsManager()
def __str__(self):
return "%s for %s" % (self.__class__.__name__, self.corp)
def update(self):
try:
c = self.token.get_esi_client(spec_file=SWAGGER_SPEC_PATH)
assert c.Character.get_characters_character_id(character_id=self.token.character_id).result()[
'corporation_id'] == int(self.corp.corporation_id)
member_ids = c.Corporation.get_corporations_corporation_id_members(
corporation_id=self.corp.corporation_id).result()
# requesting too many ids per call results in a HTTP400
# the swagger spec doesn't have a maxItems count
# manual testing says we can do over 350, but let's not risk it
member_id_chunks = [member_ids[i:i + 255] for i in range(0, len(member_ids), 255)]
member_name_chunks = [c.Universe.post_universe_names(ids=id_chunk).result() for id_chunk in
member_id_chunks]
member_list = {}
for name_chunk in member_name_chunks:
member_list.update({m['id']: m['name'] for m in name_chunk})
# bulk create new member models
missing_members = [m_id for m_id in member_ids if
not CorpMember.objects.filter(corpstats=self, character_id=m_id).exists()]
CorpMember.objects.bulk_create(
[CorpMember(character_id=m_id, character_name=member_list[m_id], corpstats=self) for m_id in
missing_members])
# purge old members
self.members.exclude(character_id__in=member_ids).delete()
# update the timer
self.save()
except TokenError as e:
logger.warning("%s failed to update: %s" % (self, e))
if self.token.user:
notify(self.token.user, "%s failed to update with your ESI token." % self,
message="Your token has expired or is no longer valid. Please add a new one to create a new CorpStats.",
level="error")
self.delete()
except HTTPForbidden as e:
logger.warning("%s failed to update: %s" % (self, e))
if self.token.user:
notify(self.token.user, "%s failed to update with your ESI token." % self,
message="%s: %s" % (e.status_code, e.message), level="error")
self.delete()
except AssertionError:
logger.warning("%s token character no longer in corp." % self)
if self.token.user:
notify(self.token.user, "%s cannot update with your ESI token." % self,
message="%s cannot update with your ESI token as you have left corp." % self, level="error")
self.delete()
@property
def member_count(self):
return self.members.count()
@property
def user_count(self):
return len(set([m.main_character for m in self.members.all() if m.main_character]))
@property
def registered_member_count(self):
return len(self.registered_members)
@property
def registered_members(self):
return self.members.filter(pk__in=[m.pk for m in self.members.all() if m.registered])
@property
def unregistered_member_count(self):
return self.member_count - self.registered_member_count
@property
def unregistered_members(self):
return self.members.filter(pk__in=[m.pk for m in self.members.all() if not m.registered])
@property
def main_count(self):
return len(self.mains)
@property
def mains(self):
return self.members.filter(pk__in=[m.pk for m in self.members.all() if
m.main_character and int(m.main_character.character_id) == int(
m.character_id)])
def visible_to(self, user):
return CorpStats.objects.filter(pk=self.pk).visible_to(user).exists()
def can_update(self, user):
return self.token.user == user or self.visible_to(user)
def corp_logo(self, size=128):
return self.corp.logo_url(size)
def alliance_logo(self, size=128):
if self.corp.alliance:
return self.corp.alliance.logo_url(size)
else:
return EveAllianceInfo.generic_logo_url(1, size)
class CorpMember(models.Model):
character_id = models.PositiveIntegerField()
character_name = models.CharField(max_length=37)
corpstats = models.ForeignKey(CorpStats, on_delete=models.CASCADE, related_name='members')
class Meta:
# not making character_id unique in case a character moves between two corps while only one updates
unique_together = ('corpstats', 'character_id')
ordering = ['character_name']
def __str__(self):
return self.character_name
@property
def character(self):
try:
return EveCharacter.objects.get(character_id=self.character_id)
except EveCharacter.DoesNotExist:
return None
@property
def main_character(self):
try:
return self.character.character_ownership.user.profile.main_character
except (CharacterOwnership.DoesNotExist, UserProfile.DoesNotExist, AttributeError):
return None
@property
def alts(self):
if self.main_character:
return [co.character for co in self.main_character.character_ownership.user.character_ownerships.all()]
else:
return []
@property
def registered(self):
return CharacterOwnership.objects.filter(character__character_id=self.character_id).exists()
def portrait_url(self, size=32):
return EveCharacter.generic_portrait_url(self.character_id, size)
@property
def portrait_url_32(self):
return self.portrait_url(32)
@property
def portrait_url_64(self):
return self.portrait_url(64)
@property
def portrait_url_128(self):
return self.portrait_url(128)

File diff suppressed because one or more lines are too long

View File

@@ -1,14 +1,14 @@
from corputils.models import CorpStats
from alliance_auth.celeryapp import app
from celery import shared_task
from allianceauth.corputils.models import CorpStats
@app.task
@shared_task
def update_corpstats(pk):
cs = CorpStats.objects.get(pk=pk)
cs.update()
@app.task
@shared_task
def update_all_corpstats():
for cs in CorpStats.objects.all():
update_corpstats.delay(cs.pk)

View File

@@ -1,6 +1,5 @@
{% extends 'public/base.html' %}
{% extends 'allianceauth/base.html' %}
{% load i18n %}
{% block title %}{% trans "Corporation Member Data" %}{% endblock %}
{% block page_title %}{% trans "Corporation Member Data" %}{% endblock %}
{% block content %}
<div class="col-lg-12">
@@ -10,8 +9,8 @@
<div class="container-fluid">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{% trans "Corporations" %}<span class="caret"></span></a>
<ul class="dropdown-menu">
<a href="#" id="dLabel" class="dropdown-toggle" role="button" data-toggle="dropdown" aria-haspopup="false" aria-expanded="false">{% trans "Corporations" %}<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
{% for corpstat in available %}
<li>
<a href="{% url 'corputils:view_corp' corpstat.corp.corporation_id %}">{{ corpstat.corp.corporation_name }}</a>
@@ -27,7 +26,7 @@
</ul>
<form class="navbar-form navbar-right" role="search" action="{% url 'corputils:search' %}" method="GET">
<div class="form-group">
<input type="text" class="form-control" name="search_string" placeholder="{% if search_string %}{{ search_string }}{% else %}{% trans "Search characters..." %}{% endif %}">
<input type="text" class="form-control" name="search_string" placeholder="{% if search_string %}{{ search_string }}{% else %}{% trans "Search all corporations..." %}{% endif %}">
</div>
</form>
</div>
@@ -35,5 +34,4 @@
{% block member_data %}{% endblock %}
</div>
</div>
{% endblock %}
{% endblock %}

View File

@@ -0,0 +1,219 @@
{% extends 'corputils/base.html' %}
{% load i18n %}
{% load humanize %}
{% block member_data %}
{% if corpstats %}
<div class="row">
<div class="col-lg-12 text-center">
<table class="table">
<tr>
<td class="text-center col-lg-6{% if corpstats.corp.alliance %}{% else %}col-lg-offset-3{% endif %}">
<img class="ra-avatar" src="{{ corpstats.corp.logo_url_64 }}">
</td>
{% if corpstats.corp.alliance %}
<td class="text-center col-lg-6">
<img class="ra-avatar" src="{{ corpstats.corp.alliance.logo_url_64 }}">
</td>
{% endif %}
</tr>
<tr>
<td class="text-center"><h4>{{ corpstats.corp.corporation_name }}</h4></td>
{% if corpstats.corp.alliance %}
<td class="text-center"><h4>{{ corpstats.corp.alliance.alliance_name }}</h4></td>
{% endif %}
</tr>
</table>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">
<ul class="nav nav-pills pull-left">
<li class="active"><a href="#mains" data-toggle="pill">{% trans 'Mains' %} ({{ total_mains }})</a></li>
<li><a href="#members" data-toggle="pill">{% trans 'Members' %} ({{ corpstats.member_count }})</a></li>
<li><a href="#unregistered" data-toggle="pill">{% trans 'Unregistered' %} ({{ unregistered.count }})</a></li>
</ul>
<div class="pull-right hidden-xs">
{% trans "Last update:" %} {{ corpstats.last_update|naturaltime }}&nbsp;
<a class="btn btn-success" type="button" href="{% url 'corputils:update' corpstats.corp.corporation_id %}" title="Update Now">
<span class="glyphicon glyphicon-refresh"></span>
</a>
</div>
<div class="clearfix"></div>
</div>
<div class="panel-body">
<div class="tab-content">
<div class="tab-pane fade in active" id="mains">
{% if mains %}
<div class="table-responsive">
<table class="table table-hover" id="table-mains">
<thead>
<tr>
<th style="height:1em;"><!-- Must have text or height to prevent clipping --></th>
<th></th>
</tr>
</thead>
<tbody>
{% for id, main in mains.items %}
<tr>
<td class="text-center" style="vertical-align:middle">
<div class="thumbnail"
style="border: 0 none; box-shadow: none; background: transparent;">
<img src="{{ main.main.portrait_url_64 }}" class="img-circle">
<div class="caption text-center">
{{ main.main }}
</div>
</div>
</td>
<td>
<table class="table table-hover">
{% for alt in main.alts %}
{% if forloop.first %}
<tr>
<th></th>
<th class="text-center">{% trans "Character" %}</th>
<th class="text-center">{% trans "Corporation" %}</th>
<th class="text-center">{% trans "Alliance" %}</th>
<th class="text-center"></th>
</tr>
{% endif %}
<tr>
<td class="text-center" style="width:5%">
<div class="thumbnail" style="border: 0 none; box-shadow: none; background: transparent;">
<img src="{{ alt.portrait_url_32 }}" class="img-circle">
</div>
</td>
<td class="text-center" style="width:30%">{{ alt.character_name }}</td>
<td class="text-center" style="width:30%">{{ alt.corporation_name }}</td>
<td class="text-center" style="width:30%">{{ alt.alliance_name }}</td>
<td class="text-center" style="width:5%">
<a href="https://zkillboard.com/character/{{ alt.character_id }}/"
class="label label-danger" target="_blank">
{% trans "Killboard" %}
</a>
</td>
</tr>
{% endfor %}
</table>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
<div class="tab-pane fade" id="members">
{% if members %}
<div class="table-responsive">
<table class="table table-hover" id="table-members">
<thead>
<tr>
<th></th>
<th class="text-center">{% trans "Character" %}</th>
<th class="text-center"></th>
<th class="text-center">{% trans "Main Character" %}</th>
<th class="text-center">{% trans "Main Corporation" %}</th>
<th class="text-center">{% trans "Main Alliance" %}</th>
</tr>
</thead>
<tbody>
{% for member in members %}
<tr>
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
<td class="text-center">{{ member }}</td>
<td class="text-center"><a
href="https://zkillboard.com/character/{{ member.character_id }}/"
class="label label-danger"
target="_blank">{% trans "Killboard" %}</a></td>
<td class="text-center">{{ member.character_ownership.user.profile.main_character.character_name }}</td>
<td class="text-center">{{ member.character_ownership.user.profile.main_character.corporation_name }}</td>
<td class="text-center">{{ member.character_ownership.user.profile.main_character.alliance_name }}</td>
</tr>
{% endfor %}
{% for member in unregistered %}
<tr class="danger">
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
<td class="text-center">{{ member.character_name }}</td>
<td class="text-center"><a
href="https://zkillboard.com/character/{{ member.character_id }}/"
class="label label-danger"
target="_blank">{% trans "Killboard" %}</a></td>
<td class="text-center"></td>
<td class="text-center"></td>
<td class="text-center"></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
<div class="tab-pane fade" id="unregistered">
{% if unregistered %}
<div class="table-responsive">
<table class="table table-hover" id="table-unregistered">
<thead>
<tr>
<th></th>
<th class="text-center">{% trans "Character" %}</th>
<th class="text-center"></th>
</tr>
</thead>
<tbody>
{% for member in unregistered %}
<tr class="danger">
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
<td class="text-center">{{ member.character_name }}</td>
<td class="text-center">
<a href="https://zkillboard.com/character/{{ member.character_id }}/"
class="label label-danger"
target="_blank">
{% trans "Killboard" %}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% endblock %}
{% block extra_javascript %}
{% include 'bundles/datatables-js.html' %}
{% endblock %}
{% block extra_css %}
{% include 'bundles/datatables-css.html' %}
{% endblock %}
{% block extra_script %}
$(document).ready(function(){
$('#table-mains').DataTable({
"columnDefs": [
{ "sortable": false, "targets": [1] },
],
});
$('#table-members').DataTable({
"columnDefs": [
{ "searchable": false, "targets": [0, 2] },
{ "sortable": false, "targets": [0, 2] },
],
"order": [[ 1, "asc" ]],
});
$('#table-unregistered').DataTable({
"columnDefs": [
{ "searchable": false, "targets": [0, 2] },
{ "sortable": false, "targets": [0, 2] },
],
"order": [[ 1, "asc" ]],
});
});
{% endblock %}

Some files were not shown because too many files have changed in this diff Show More