mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-04 06:06:19 +01:00
Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
026b4c272b | ||
|
|
f5cb6a3fb7 | ||
|
|
0b57e027f7 | ||
|
|
3d50f977d9 | ||
|
|
0abb160886 | ||
|
|
ff3bb9743f | ||
|
|
cf81f59fa6 | ||
|
|
d186088a8f | ||
|
|
2106e492e3 | ||
|
|
badc5a1f7f | ||
|
|
cced434a99 | ||
|
|
6c4e8ec2d1 | ||
|
|
5bbaef4476 | ||
|
|
6208071537 | ||
|
|
df02982983 | ||
|
|
eaa9d1930c | ||
|
|
24bc9d4b7f | ||
|
|
73641a7a1e | ||
|
|
485c0fc373 | ||
|
|
55cbbadc2b | ||
|
|
b1dafeda8d | ||
|
|
380069627a | ||
|
|
c3390b089e | ||
|
|
c9d9b0bf07 | ||
|
|
d4064fd059 | ||
|
|
dd63ff7884 | ||
|
|
5e9a782c99 | ||
|
|
9865726d2d | ||
|
|
e8915b84e5 | ||
|
|
8360371ab7 | ||
|
|
33c2ba9bca | ||
|
|
ab3c671d53 | ||
|
|
4c72352737 | ||
|
|
c4a6492ab5 | ||
|
|
e1ccd972e4 | ||
|
|
9ad61c1f4c | ||
|
|
8e64fe145e | ||
|
|
395c2c1bec | ||
|
|
3d25f58fbb | ||
|
|
1887d612e6 | ||
|
|
a561862911 | ||
|
|
a309a733d6 | ||
|
|
d964197b27 | ||
|
|
653bb97c55 | ||
|
|
02aaa7ff78 | ||
|
|
c205bff99f | ||
|
|
56082848a7 | ||
|
|
2816a5fa46 | ||
|
|
4f26fa03e6 | ||
|
|
4ae450f963 | ||
|
|
959e167987 | ||
|
|
f96959d854 | ||
|
|
2f5529b582 | ||
|
|
04f2731517 | ||
|
|
71c1054328 | ||
|
|
917707027e | ||
|
|
03aab76ab9 | ||
|
|
1e523d762e | ||
|
|
33e481fb12 | ||
|
|
16a4ed9bf2 | ||
|
|
286cb9e1d1 | ||
|
|
2f2c2bd0e5 | ||
|
|
3cdbff6b36 | ||
|
|
54464c23cf | ||
|
|
2a87eed059 | ||
|
|
55f349c35b | ||
|
|
604808b195 | ||
|
|
ce35e72e44 | ||
|
|
8582584fd5 | ||
|
|
8379fdd7d5 | ||
|
|
ec9b43b083 | ||
|
|
66240ad296 | ||
|
|
0fe5a1c5e3 | ||
|
|
b514f8cbcc | ||
|
|
4ee10e0c31 | ||
|
|
5f88e7e1a5 | ||
|
|
5a9418d792 | ||
|
|
02bd4570b0 | ||
|
|
6ba084c710 | ||
|
|
6fd3c32ba0 | ||
|
|
3d92e4c5c5 | ||
|
|
27fc8a373f | ||
|
|
32009fd3ff | ||
|
|
b4b739ee61 | ||
|
|
a630b5b397 | ||
|
|
721708ee16 | ||
|
|
4dabb3198d | ||
|
|
c0ca73f9ea | ||
|
|
83b62525eb | ||
|
|
9eba5607d2 | ||
|
|
5fb64d8c06 | ||
|
|
ed461d9e7a | ||
|
|
bf345361b2 | ||
|
|
648753a68a | ||
|
|
42def30c91 | ||
|
|
25dadf81f6 | ||
|
|
2dd75b5100 | ||
|
|
eb6d7e9d9a | ||
|
|
1c4d2533b1 |
91
README.md
91
README.md
@@ -2,74 +2,26 @@ Alliance Auth
|
||||
============
|
||||
|
||||
[](https://gitter.im/R4stl1n/allianceauth?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](http://allianceauth.readthedocs.io/en/latest/?badge=latest)
|
||||
|
||||
Alliance service auth to help large scale alliances manage services.
|
||||
Built for "The 99 Percent" open for anyone to use
|
||||
|
||||
[Documentation and Setup Guides](https://github.com/R4stl1n/allianceauth/wiki)
|
||||
|
||||
[Project Website](http://r4stl1n.github.io/allianceauth/)
|
||||
|
||||
[Old Dev Setup Guide] (http://r4stl1n.github.io/allianceauth/quicksetup.html)
|
||||
|
||||
[Old Production Setup Guide] (http://r4stl1n.github.io/allianceauth/fullsetup.html)
|
||||
|
||||
Join us in-game in the channel allianceauth for help and feature requests.
|
||||
|
||||
Special Thanks:
|
||||
|
||||
Thanks to Nikdoof, without his old auth implementation this project wouldn't be as far as it is now.
|
||||
|
||||
Note:
|
||||
|
||||
Please keep your admin account and normal accounts separate. If you are the admin only use
|
||||
the admin account for admin stuff do not attempt to use it for your personal services.
|
||||
Create a new normal account for this or things will break.
|
||||
|
||||
Requirements:
|
||||
|
||||
# Django Stuff #
|
||||
django 1.10.1
|
||||
django-bootstrap-form
|
||||
django-celery
|
||||
|
||||
# Python Stuff #
|
||||
python-mysql-connector
|
||||
python-mysqld
|
||||
python-passlib
|
||||
python-evelink
|
||||
python-openfire
|
||||
python-xmpp
|
||||
python-dnspython
|
||||
|
||||
# Needed Apps #
|
||||
Rabbitmq server
|
||||
|
||||
Startup Instructions:
|
||||
|
||||
./bootstrap.sh (Sudo if needed)
|
||||
./startup.sh
|
||||
./shutdown.sh
|
||||
|
||||
Vagrant Instructions:
|
||||
|
||||
Copy the scripts to the root directory before running
|
||||
EVE service auth to help corps, alliances, and coalitions manage services.
|
||||
Built for "The 99 Percent" open for anyone to use.
|
||||
|
||||
Special Permissions In Admin:
|
||||
|
||||
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 | 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)
|
||||
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 )
|
||||
|
||||
auth | user | human_resources ( View applications to user's corp )
|
||||
hrapplications | application | delete_application ( Can delete applications )
|
||||
@@ -78,14 +30,21 @@ Special Permissions In Admin:
|
||||
hrapplications | application | view_apis ( Can see applicant's API keys )
|
||||
hrapplications | applicationcomment | add_applicationcomment ( Can comment on applications )
|
||||
|
||||
Active Developers
|
||||
Vagrant Instructions:
|
||||
|
||||
Copy the scripts to the root directory before running
|
||||
|
||||
Active Developers:
|
||||
|
||||
Adarnof
|
||||
Kaezon Rio
|
||||
Mr McClain
|
||||
basraah
|
||||
|
||||
Beta Testers/ Bug Fixers:
|
||||
|
||||
TrentBartlem ( Testing and Bug Fixes)
|
||||
TrentBartlem ( Testing and Bug Fixes )
|
||||
IskFiend ( Bug Fixes and Server Configuration )
|
||||
Mr McClain (Bug Fixes and server configuration)
|
||||
Mr McClain (Bug Fixes and server configuration )
|
||||
|
||||
Special Thanks:
|
||||
|
||||
Thanks to Nikdoof, without his old auth implementation this project wouldn't be as far as it is now.
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import unicode_literals
|
||||
|
||||
__version__ = '1.14.2'
|
||||
NAME = 'Alliance Auth v%s' % __version__
|
||||
|
||||
@@ -61,8 +61,9 @@ INSTALLED_APPS = [
|
||||
'corputils',
|
||||
'fleetactivitytracking',
|
||||
'notifications',
|
||||
'eve_sso',
|
||||
'esi',
|
||||
'geelweb.django.navhelper',
|
||||
'bootstrap_pagination',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
@@ -110,7 +111,7 @@ TEMPLATES = [
|
||||
'notifications.context_processors.user_notification_count',
|
||||
'authentication.context_processors.states',
|
||||
'authentication.context_processors.membership_state',
|
||||
'authentication.context_processors.sso',
|
||||
'groupmanagement.context_processors.can_manage_groups',
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -184,21 +185,22 @@ MESSAGE_TAGS = {
|
||||
messages.ERROR: 'danger'
|
||||
}
|
||||
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django_redis.cache.RedisCache",
|
||||
"LOCATION": "redis://127.0.0.1:6379/1",
|
||||
"OPTIONS": {
|
||||
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#####################################################
|
||||
##
|
||||
## 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
|
||||
#################
|
||||
@@ -222,24 +224,23 @@ EMAIL_USE_TLS = 'True' == os.environ.get('AA_EMAIL_USE_TLS', 'True')
|
||||
# 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
|
||||
# SMF_URL - URL for your SMF forums.
|
||||
# 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
|
||||
###################
|
||||
# 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
|
||||
# Callback URL should be https://mydomain.com/sso/callback
|
||||
###################
|
||||
EVE_SSO_CLIENT_ID = os.environ.get('AA_EVE_SSO_CLIENT_ID', '')
|
||||
EVE_SSO_CLIENT_SECRET = os.environ.get('AA_EVE_SSO_CLIENT_SECRET', '')
|
||||
EVE_SSO_CALLBACK_URL = os.environ.get('AA_EVE_SSO_CALLBACK_URL', '')
|
||||
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
|
||||
@@ -268,7 +269,6 @@ BLUE_ALLIANCE_GROUPS = 'True' == os.environ.get('AA_BLUE_ALLIANCE_GROUPS', 'Fals
|
||||
# ENABLE_AUTH_IPS4 - Enable IPS4 support in the auth for auth'd members
|
||||
# ENABLE_AUTH_SMF - Enable SMF forum support in the auth for auth'd members
|
||||
# ENABLE_AUTH_MARKET = Enable Alliance Market support in auth for auth'd members
|
||||
# ENABLE_AUTH_PATHFINDER = Enable Alliance Pathfinder suppor in auth for auth'd members
|
||||
# ENABLE_AUTH_XENFORO = Enable XenForo forums support in the auth for auth'd members
|
||||
#########################
|
||||
ENABLE_AUTH_FORUM = 'True' == os.environ.get('AA_ENABLE_AUTH_FORUM', 'False')
|
||||
@@ -286,7 +286,6 @@ ENABLE_AUTH_XENFORO = 'True' == os.environ.get('AA_ENABLE_AUTH_XENFORO', 'False'
|
||||
#####################
|
||||
# Blue service Setup
|
||||
#####################
|
||||
# BLUE_STANDING - The default lowest standings setting to consider blue
|
||||
# ENABLE_BLUE_FORUM - Enable forum support in the auth for blues
|
||||
# ENABLE_BLUE_JABBER - Enable jabber support in the auth for blues
|
||||
# ENABLE_BLUE_MUMBLE - Enable mumble support in the auth for blues
|
||||
@@ -296,10 +295,8 @@ ENABLE_AUTH_XENFORO = 'True' == os.environ.get('AA_ENABLE_AUTH_XENFORO', 'False'
|
||||
# ENABLE_BLUE_IPS4 - Enable IPS4 forum support in the auth for blues
|
||||
# ENABLE_BLUE_SMF - Enable SMF forum support in the auth for blues
|
||||
# ENABLE_BLUE_MARKET - Enable Alliance Market in the auth for blues
|
||||
# ENABLE_BLUE_PATHFINDER = Enable Pathfinder support in the auth for blues
|
||||
# ENABLE_BLUE_XENFORO = Enable XenForo forum support in the auth for blue
|
||||
#####################
|
||||
BLUE_STANDING = float(os.environ.get('AA_BLUE_STANDING', '5.0'))
|
||||
ENABLE_BLUE_FORUM = 'True' == os.environ.get('AA_ENABLE_BLUE_FORUM', 'False')
|
||||
ENABLE_BLUE_JABBER = 'True' == os.environ.get('AA_ENABLE_BLUE_JABBER', 'False')
|
||||
ENABLE_BLUE_MUMBLE = 'True' == os.environ.get('AA_ENABLE_BLUE_MUMBLE', 'False')
|
||||
@@ -313,27 +310,28 @@ ENABLE_BLUE_MARKET = 'True' == os.environ.get('AA_ENABLE_BLUE_MARKET', 'False')
|
||||
ENABLE_BLUE_XENFORO = 'True' == os.environ.get('AA_ENABLE_BLUE_XENFORO', 'False')
|
||||
|
||||
#########################
|
||||
# Corp Configuration
|
||||
# Tenant 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', '')
|
||||
CORP_NAME = os.environ.get('AA_CORP_NAME', '')
|
||||
CORP_API_ID = os.environ.get('AA_CORP_API_ID', '')
|
||||
CORP_API_VCODE = os.environ.get('AA_CORP_API_VCODE', '')
|
||||
# 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 = []
|
||||
|
||||
#########################
|
||||
# Alliance Configuration
|
||||
# Standings Configuration
|
||||
#########################
|
||||
# ALLIANCE_ID - Set this to your Alliance ID (get this from https://zkillboard.com/alliance/#######)
|
||||
# ALLIANCE_NAME - Set this to your Alliance Name
|
||||
# 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'.
|
||||
########################
|
||||
ALLIANCE_ID = os.environ.get('AA_ALLIANCE_ID', '')
|
||||
ALLIANCE_NAME = os.environ.get('AA_ALLIANCE_NAME', '')
|
||||
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')
|
||||
|
||||
########################
|
||||
# API Configuration
|
||||
@@ -355,6 +353,21 @@ 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
|
||||
#####################
|
||||
@@ -653,3 +666,7 @@ if ENABLE_AUTH_MARKET or ENABLE_BLUE_MARKET:
|
||||
DATABASES['market'] = MARKET_DB
|
||||
if ENABLE_AUTH_IPS4 or ENABLE_BLUE_IPS4:
|
||||
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]
|
||||
|
||||
@@ -10,13 +10,16 @@ import services.views
|
||||
import groupmanagement.views
|
||||
import optimer.views
|
||||
import timerboard.views
|
||||
import corputils.views
|
||||
import fleetactivitytracking.views
|
||||
import fleetup.views
|
||||
import srp.views
|
||||
import notifications.views
|
||||
import hrapplications.views
|
||||
import eve_sso.urls
|
||||
import corputils.urls
|
||||
import esi.urls
|
||||
from alliance_auth import NAME
|
||||
|
||||
admin.site.site_header = NAME
|
||||
|
||||
# Functional/Untranslated URL's
|
||||
urlpatterns = [
|
||||
@@ -27,8 +30,11 @@ urlpatterns = [
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
|
||||
# SSO
|
||||
url (r'^sso/', include(eve_sso.urls, namespace='eve_sso')),
|
||||
url (r'^sso/login$', authentication.views.sso_login, name='auth_sso_login'),
|
||||
url(r'^sso/', include(esi.urls, namespace='esi')),
|
||||
url(r'^sso/login$', authentication.views.sso_login, name='auth_sso_login'),
|
||||
|
||||
# Corputils
|
||||
url(r'^corpstats/', include(corputils.urls, namespace='corputils')),
|
||||
|
||||
# Index
|
||||
url(_(r'^$'), authentication.views.index_view, name='auth_index'),
|
||||
@@ -144,14 +150,6 @@ urlpatterns = [
|
||||
# User viewed/translated URLS
|
||||
urlpatterns += i18n_patterns(
|
||||
|
||||
# corputils
|
||||
url(r'^corputils/$', corputils.views.corp_member_view, name='auth_corputils'),
|
||||
url(r'^corputils/(?P<corpid>[0-9]+)/$', corputils.views.corp_member_view, name='auth_corputils_corp_view'),
|
||||
url(r'^corputils/(?P<corpid>[0-9]+)/(?P<year>[0-9]+)/(?P<month>[0-9]+)/$', corputils.views.corp_member_view,
|
||||
name='auth_corputils_month'),
|
||||
url(r'^corputils/search/$', corputils.views.corputils_search, name="auth_corputils_search"),
|
||||
url(r'^corputils/search/(?P<corpid>[0-9]+)/$', corputils.views.corputils_search, name='auth_corputils_search_corp'),
|
||||
|
||||
# Fleetup
|
||||
url(r'^fleetup/$', fleetup.views.fleetup_view, name='auth_fleetup_view'),
|
||||
url(r'^fleetup/fittings/$', fleetup.views.fleetup_fittings, name='auth_fleetup_fittings'),
|
||||
@@ -177,13 +175,11 @@ urlpatterns += i18n_patterns(
|
||||
django.contrib.auth.views.password_reset_confirm, name='password_reset_confirm'),
|
||||
|
||||
# Portal Urls
|
||||
url(_(r'^dashboard/$'), authentication.views.dashboard_view, name='auth_dashboard'),
|
||||
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'^api_key_management/'), eveonline.views.api_key_management_view,
|
||||
name='auth_api_key_management'),
|
||||
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'),
|
||||
@@ -192,6 +188,12 @@ urlpatterns += i18n_patterns(
|
||||
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,
|
||||
@@ -245,12 +247,6 @@ urlpatterns += i18n_patterns(
|
||||
# Teamspeak Urls
|
||||
url(r'verify_teamspeak3/$', services.views.verify_teamspeak3, name='auth_verify_teamspeak3'),
|
||||
|
||||
# corputils
|
||||
url(_(r'^corputils/$'), corputils.views.corp_member_view, name='auth_corputils'),
|
||||
url(_(r'^corputils/(?P<corpid>[0-9]+)/$'), corputils.views.corp_member_view, name='auth_corputils_corp_view'),
|
||||
url(_(r'^corputils/search/$'), corputils.views.corputils_search, name="auth_corputils_search"),
|
||||
url(_(r'^corputils/search/(?P<corpid>[0-9]+)/$'), corputils.views.corputils_search, name='auth_corputils_search_corp'),
|
||||
|
||||
# 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'),
|
||||
|
||||
@@ -26,6 +26,14 @@ class AuthServicesInfoManager(admin.ModelAdmin):
|
||||
pass
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def has_delete_permission(request, obj=None):
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def has_add_permission(request, obj=None):
|
||||
return False
|
||||
|
||||
def sync_jabber(self, request, queryset):
|
||||
count = 0
|
||||
for a in queryset: # queryset filtering doesn't work here?
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
from __future__ import unicode_literals
|
||||
from authentication.models import AuthServicesInfo
|
||||
from authentication.states import NONE_STATE, BLUE_STATE, MEMBER_STATE
|
||||
from authentication.managers import UserState
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def membership_state(request):
|
||||
if request.user.is_authenticated:
|
||||
auth = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
return {'STATE': auth.state}
|
||||
return {'STATE': NONE_STATE}
|
||||
return UserState.get_membership_state(request)
|
||||
|
||||
|
||||
def states(request):
|
||||
@@ -18,8 +15,3 @@ def states(request):
|
||||
'NONE_STATE': NONE_STATE,
|
||||
'MEMBER_BLUE_STATE': [MEMBER_STATE, BLUE_STATE],
|
||||
}
|
||||
|
||||
def sso(request):
|
||||
return {
|
||||
'EVE_SSO_CALLBACK_URL': settings.EVE_SSO_CALLBACK_URL,
|
||||
}
|
||||
|
||||
@@ -1,33 +1,23 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.contrib.auth.decorators import user_passes_test
|
||||
from authentication.models import AuthServicesInfo
|
||||
from authentication.states import MEMBER_STATE, BLUE_STATE, NONE_STATE
|
||||
from django.conf import settings
|
||||
from authentication.managers import UserState
|
||||
|
||||
|
||||
def _state_required(states, *args, **kwargs):
|
||||
def test_func(user):
|
||||
if user.is_superuser and settings.SUPERUSER_STATE_BYPASS:
|
||||
return True
|
||||
if user.is_authenticated:
|
||||
auth = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
return auth.state in states
|
||||
return False
|
||||
|
||||
return user_passes_test(test_func, *args, **kwargs)
|
||||
def _state_required(state_test, *args, **kwargs):
|
||||
return user_passes_test(state_test, *args, **kwargs)
|
||||
|
||||
|
||||
def members(*args, **kwargs):
|
||||
return _state_required([MEMBER_STATE], *args, **kwargs)
|
||||
return _state_required(UserState.member_state, *args, **kwargs)
|
||||
|
||||
|
||||
def blues(*args, **kwargs):
|
||||
return _state_required([BLUE_STATE], *args, **kwargs)
|
||||
return _state_required(UserState.blue_state, *args, **kwargs)
|
||||
|
||||
|
||||
def members_and_blues(*args, **kwargs):
|
||||
return _state_required([MEMBER_STATE, BLUE_STATE], *args, **kwargs)
|
||||
return _state_required(UserState.member_or_blue_state, *args, **kwargs)
|
||||
|
||||
|
||||
def none_state(*args, **kwargs):
|
||||
return _state_required([NONE_STATE], *args, **kwargs)
|
||||
return _state_required(UserState.none_state, *args, **kwargs)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from django.conf import settings
|
||||
from authentication.states import NONE_STATE, BLUE_STATE, MEMBER_STATE
|
||||
from authentication.models import AuthServicesInfo
|
||||
|
||||
import logging
|
||||
@@ -16,7 +17,7 @@ class AuthServicesInfoManager:
|
||||
def update_main_char_id(char_id, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s main character to id %s" % (user, char_id))
|
||||
authserviceinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.main_char_id = char_id
|
||||
authserviceinfo.save(update_fields=['main_char_id'])
|
||||
logger.info("Updated user %s main character to id %s" % (user, char_id))
|
||||
@@ -27,7 +28,7 @@ class AuthServicesInfoManager:
|
||||
def update_user_forum_info(username, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s forum info: username %s" % (user, username))
|
||||
authserviceinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.forum_username = username
|
||||
authserviceinfo.save(update_fields=['forum_username'])
|
||||
logger.info("Updated user %s forum info in authservicesinfo model." % user)
|
||||
@@ -38,7 +39,7 @@ class AuthServicesInfoManager:
|
||||
def update_user_jabber_info(username, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s jabber info: username %s" % (user, username))
|
||||
authserviceinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.jabber_username = username
|
||||
authserviceinfo.save(update_fields=['jabber_username'])
|
||||
logger.info("Updated user %s jabber info in authservicesinfo model." % user)
|
||||
@@ -49,7 +50,7 @@ class AuthServicesInfoManager:
|
||||
def update_user_mumble_info(username, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s mumble info: username %s" % (user, username))
|
||||
authserviceinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.mumble_username = username
|
||||
authserviceinfo.save(update_fields=['mumble_username'])
|
||||
logger.info("Updated user %s mumble info in authservicesinfo model." % user)
|
||||
@@ -60,7 +61,7 @@ class AuthServicesInfoManager:
|
||||
def update_user_ipboard_info(username, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s ipboard info: uername %s" % (user, username))
|
||||
authserviceinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.ipboard_username = username
|
||||
authserviceinfo.save(update_fields=['ipboard_username'])
|
||||
logger.info("Updated user %s ipboard info in authservicesinfo model." % user)
|
||||
@@ -71,7 +72,7 @@ class AuthServicesInfoManager:
|
||||
def update_user_xenforo_info(username, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s xenforo info: uername %s" % (user, username))
|
||||
authserviceinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.xenforo_username = username
|
||||
authserviceinfo.save(update_fields=['xenforo_username'])
|
||||
logger.info("Updated user %s xenforo info in authservicesinfo model." % user)
|
||||
@@ -82,7 +83,7 @@ class AuthServicesInfoManager:
|
||||
def update_user_teamspeak3_info(uid, perm_key, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s teamspeak3 info: uid %s" % (user, uid))
|
||||
authserviceinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.teamspeak3_uid = uid
|
||||
authserviceinfo.teamspeak3_perm_key = perm_key
|
||||
authserviceinfo.save(update_fields=['teamspeak3_uid', 'teamspeak3_perm_key'])
|
||||
@@ -94,7 +95,7 @@ class AuthServicesInfoManager:
|
||||
def update_is_blue(is_blue, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s blue status: %s" % (user, is_blue))
|
||||
authserviceinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.is_blue = is_blue
|
||||
authserviceinfo.save(update_fields=['is_blue'])
|
||||
logger.info("Updated user %s blue status to %s in authservicesinfo model." % (user, is_blue))
|
||||
@@ -103,7 +104,7 @@ class AuthServicesInfoManager:
|
||||
def update_user_discord_info(user_id, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s discord info: user_id %s" % (user, user_id))
|
||||
authserviceinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.discord_uid = user_id
|
||||
authserviceinfo.save(update_fields=['discord_uid'])
|
||||
logger.info("Updated user %s discord info in authservicesinfo model." % user)
|
||||
@@ -114,7 +115,7 @@ class AuthServicesInfoManager:
|
||||
def update_user_ips4_info(username, id, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s IPS4 info: username %s" % (user, username))
|
||||
authserviceinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.ips4_username = username
|
||||
authserviceinfo.ips4_id = id
|
||||
authserviceinfo.save(update_fields=['ips4_username', 'ips4_id'])
|
||||
@@ -126,7 +127,7 @@ class AuthServicesInfoManager:
|
||||
def update_user_smf_info(username, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s forum info: username %s" % (user, username))
|
||||
authserviceinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.smf_username = username
|
||||
authserviceinfo.save(update_fields=['smf_username'])
|
||||
logger.info("Updated user %s smf info in authservicesinfo model." % user)
|
||||
@@ -137,9 +138,50 @@ class AuthServicesInfoManager:
|
||||
def update_user_market_info(username, user):
|
||||
if User.objects.filter(username=user.username).exists():
|
||||
logger.debug("Updating user %s market info: username %s" % (user, username))
|
||||
authserviceinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authserviceinfo = AuthServicesInfo.objects.get(user=user)
|
||||
authserviceinfo.market_username = username
|
||||
authserviceinfo.save(update_fields=['market_username'])
|
||||
logger.info("Updated user %s market info in authservicesinfo model." % user)
|
||||
else:
|
||||
logger.error("Failed to update user %s market info: user does not exist." % user)
|
||||
|
||||
|
||||
class UserState:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
MEMBER_STATE = MEMBER_STATE
|
||||
BLUE_STATE = BLUE_STATE
|
||||
NONE_STATE = NONE_STATE
|
||||
|
||||
@classmethod
|
||||
def member_state(cls, user):
|
||||
return cls.state_required(user, [cls.MEMBER_STATE])
|
||||
|
||||
@classmethod
|
||||
def member_or_blue_state(cls, user):
|
||||
return cls.state_required(user, [cls.MEMBER_STATE, cls.BLUE_STATE])
|
||||
|
||||
@classmethod
|
||||
def blue_state(cls, user):
|
||||
return cls.state_required(user, [cls.BLUE_STATE])
|
||||
|
||||
@classmethod
|
||||
def none_state(cls, user):
|
||||
return cls.state_required(user, [cls.NONE_STATE])
|
||||
|
||||
@classmethod
|
||||
def get_membership_state(cls, request):
|
||||
if request.user.is_authenticated:
|
||||
auth = AuthServicesInfo.objects.get(user=request.user)
|
||||
return {'STATE': auth.state}
|
||||
return {'STATE': cls.NONE_STATE}
|
||||
|
||||
@staticmethod
|
||||
def state_required(user, states):
|
||||
if user.is_superuser and settings.SUPERUSER_STATE_BYPASS:
|
||||
return True
|
||||
if user.is_authenticated:
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
return auth.state in states
|
||||
return False
|
||||
|
||||
@@ -8,12 +8,10 @@ from authentication.states import MEMBER_STATE, BLUE_STATE, NONE_STATE
|
||||
from django.conf import settings
|
||||
|
||||
def determine_membership_by_character(char, apps):
|
||||
if settings.IS_CORP:
|
||||
if int(char.corporation_id) == int(settings.CORP_ID):
|
||||
return MEMBER_STATE
|
||||
else:
|
||||
if int(char.alliance_id) == int(settings.ALLIANCE_ID):
|
||||
return MEMBER_STATE
|
||||
if str(char.corporation_id) in settings.STR_CORP_IDS:
|
||||
return MEMBER_STATE
|
||||
elif str(char.alliance_id) in settings.STR_ALLIANCE_IDS:
|
||||
return MEMBER_STATE
|
||||
EveCorporationInfo = apps.get_model('eveonline', 'EveCorporationInfo')
|
||||
if EveCorporationInfo.objects.filter(corporation_id=char.corporation_id).exists() is False:
|
||||
return NONE_STATE
|
||||
@@ -26,7 +24,7 @@ def determine_membership_by_character(char, apps):
|
||||
|
||||
def determine_membership_by_user(user, apps):
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
auth, c = AuthServicesInfo.objects.get_or_create(user=user)
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
if auth.main_char_id:
|
||||
EveCharacter = apps.get_model('eveonline', 'EveCharacter')
|
||||
if EveCharacter.objects.filter(character_id=auth.main_char_id).exists():
|
||||
@@ -43,7 +41,7 @@ def set_state(user, apps):
|
||||
else:
|
||||
state = NONE_STATE
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
auth = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
if auth.state != state:
|
||||
auth.state = state
|
||||
auth.save()
|
||||
|
||||
40
authentication/migrations/0010_only_one_authservicesinfo.py
Normal file
40
authentication/migrations/0010_only_one_authservicesinfo.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2017-01-07 06:47
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def count_completed_fields(model):
|
||||
return len([True for key, value in model.__dict__.items() if bool(value)])
|
||||
|
||||
def forward(apps, schema_editor):
|
||||
# this ensures only one model exists per user
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
users = set([a.user for a in AuthServicesInfo.objects.all()])
|
||||
for u in users:
|
||||
auths = AuthServicesInfo.objects.filter(user=u)
|
||||
if auths.count() > 1:
|
||||
pk = auths[0].pk
|
||||
largest = 0
|
||||
for auth in auths:
|
||||
completed = count_completed_fields(auth)
|
||||
if completed > largest:
|
||||
largest = completed
|
||||
pk = auth.pk
|
||||
auths.exclude(pk=pk).delete()
|
||||
|
||||
# ensure all users have a model
|
||||
User = apps.get_model('auth', 'User')
|
||||
for u in User.objects.exclude(pk__in=[user.pk for user in users]):
|
||||
AuthServicesInfo.objects.create(user=u)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0009_auto_20161021_0228'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(forward, migrations.RunPython.noop)
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2017-01-07 07:11
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0010_only_one_authservicesinfo'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='authservicesinfo',
|
||||
name='user',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-01-12 00:59
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
def remove_permissions(apps, schema_editor):
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
|
||||
# delete the add and remove permissions for AuthServicesInfo
|
||||
ct = ContentType.objects.get_for_model(AuthServicesInfo)
|
||||
Permission.objects.filter(content_type=ct).filter(codename__in=['add_authservicesinfo', 'delete_authservicesinfo']).delete()
|
||||
|
||||
|
||||
def add_permissions(apps, schema_editor):
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
|
||||
# recreate the add and remove permissions for AuthServicesInfo
|
||||
ct = ContentType.objects.get_for_model(AuthServicesInfo)
|
||||
Permission.objects.create(content_type=ct, codename='add_authservicesinfo', name='Can add auth services info')
|
||||
Permission.objects.create(content_type=ct, codename='delete_authservicesinfo', name='Can delete auth services info')
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0011_authservicesinfo_user_onetoonefield'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='authservicesinfo',
|
||||
options={'default_permissions': ('change',)},
|
||||
),
|
||||
migrations.RunPython(remove_permissions, add_permissions),
|
||||
]
|
||||
@@ -7,6 +7,9 @@ from authentication.states import MEMBER_STATE, BLUE_STATE, NONE_STATE
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class AuthServicesInfo(models.Model):
|
||||
class Meta:
|
||||
default_permissions = ('change',)
|
||||
|
||||
STATE_CHOICES = (
|
||||
(NONE_STATE, 'None'),
|
||||
(BLUE_STATE, 'Blue'),
|
||||
@@ -27,7 +30,7 @@ class AuthServicesInfo(models.Model):
|
||||
smf_username = models.CharField(max_length=254, blank=True, default="")
|
||||
market_username = models.CharField(max_length=254, blank=True, default="")
|
||||
main_char_id = models.CharField(max_length=64, blank=True, default="")
|
||||
user = models.ForeignKey(User)
|
||||
user = models.OneToOneField(User)
|
||||
state = models.CharField(blank=True, null=True, choices=STATE_CHOICES, default=NONE_STATE, max_length=10)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.db.models.signals import pre_save
|
||||
from django.db.models.signals import pre_save, post_save
|
||||
from django.dispatch import receiver
|
||||
from django.contrib.auth.models import User
|
||||
from authentication.models import AuthServicesInfo
|
||||
from authentication.states import MEMBER_STATE, BLUE_STATE
|
||||
from authentication.tasks import make_member, make_blue, disable_member
|
||||
@@ -23,3 +24,11 @@ def pre_save_auth_state(sender, instance, *args, **kwargs):
|
||||
else:
|
||||
disable_member(instance.user)
|
||||
validate_services(instance.user, instance.state)
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def post_save_user(sender, instance, created, *args, **kwargs):
|
||||
# ensure all users have a model
|
||||
if created:
|
||||
AuthServicesInfo.objects.get_or_create(user=instance)
|
||||
|
||||
|
||||
@@ -63,15 +63,13 @@ def make_blue(auth):
|
||||
|
||||
|
||||
def determine_membership_by_character(char):
|
||||
if settings.IS_CORP:
|
||||
if int(char.corporation_id) == int(settings.CORP_ID):
|
||||
logger.debug("Character %s in owning corp id %s" % (char, char.corporation_id))
|
||||
return MEMBER_STATE
|
||||
else:
|
||||
if int(char.alliance_id) == int(settings.ALLIANCE_ID):
|
||||
logger.debug("Character %s in owning alliance id %s" % (char, char.alliance_id))
|
||||
return MEMBER_STATE
|
||||
if EveCorporationInfo.objects.filter(corporation_id=char.corporation_id).exists() is False:
|
||||
if str(char.corporation_id) in settings.STR_CORP_IDS:
|
||||
logger.debug("Character %s in member corp id %s" % (char, char.corporation_id))
|
||||
return MEMBER_STATE
|
||||
elif str(char.alliance_id) in settings.STR_ALLIANCE_IDS:
|
||||
logger.debug("Character %s in member alliance id %s" % (char, char.alliance_id))
|
||||
return MEMBER_STATE
|
||||
elif not EveCorporationInfo.objects.filter(corporation_id=char.corporation_id).exists():
|
||||
logger.debug("No corp model for character %s corp id %s. Unable to check standings. Non-member." % (
|
||||
char, char.corporation_id))
|
||||
return NONE_STATE
|
||||
@@ -87,7 +85,7 @@ def determine_membership_by_character(char):
|
||||
|
||||
def determine_membership_by_user(user):
|
||||
logger.debug("Determining membership of user %s" % user)
|
||||
auth, c = AuthServicesInfo.objects.get_or_create(user=user)
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
if auth.main_char_id:
|
||||
if EveCharacter.objects.filter(character_id=auth.main_char_id).exists():
|
||||
char = EveCharacter.objects.get(character_id=auth.main_char_id)
|
||||
@@ -107,11 +105,13 @@ def set_state(user):
|
||||
else:
|
||||
state = NONE_STATE
|
||||
logger.debug("Assigning user %s to state %s" % (user, state))
|
||||
auth = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
if auth.state != state:
|
||||
auth.state = state
|
||||
auth.save()
|
||||
notify(user, "Membership State Change", message="You membership state has been changed to %s" % state)
|
||||
assign_corp_group(auth)
|
||||
assign_alliance_group(auth)
|
||||
|
||||
|
||||
def assign_corp_group(auth):
|
||||
|
||||
@@ -10,7 +10,7 @@ from authentication.models import AuthServicesInfo
|
||||
from authentication.forms import LoginForm, RegistrationForm
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib import messages
|
||||
from eve_sso.decorators import token_required
|
||||
from esi.decorators import token_required
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -67,6 +67,7 @@ def register_user_view(request):
|
||||
|
||||
user.save()
|
||||
logger.info("Created new user %s" % user)
|
||||
login(request, user)
|
||||
messages.warning(request, 'Add an API key to set up your account.')
|
||||
return redirect("auth_dashboard")
|
||||
|
||||
@@ -88,32 +89,27 @@ def index_view(request):
|
||||
return render(request, 'public/index.html')
|
||||
|
||||
|
||||
@login_required
|
||||
def dashboard_view(request):
|
||||
logger.debug("dashboard_view called by user %s" % request.user)
|
||||
render_items = {'characters': EveManager.get_characters_by_owner_id(request.user.id),
|
||||
'authinfo': AuthServicesInfo.objects.get_or_create(user=request.user)[0]}
|
||||
return render(request, 'registered/dashboard.html', context=render_items)
|
||||
|
||||
|
||||
@login_required
|
||||
def help_view(request):
|
||||
logger.debug("help_view called by user %s" % request.user)
|
||||
return render(request, 'registered/help.html')
|
||||
|
||||
|
||||
@token_required(new=True)
|
||||
def sso_login(request, tokens=[]):
|
||||
token = tokens[0]
|
||||
def sso_login(request, token):
|
||||
try:
|
||||
char = EveCharacter.objects.get(character_id=token.character_id)
|
||||
if char.user:
|
||||
if char.user.is_active:
|
||||
login(request, char.user)
|
||||
return redirect(dashboard_view)
|
||||
token.user = char.user
|
||||
token.save()
|
||||
return redirect('auth_dashboard')
|
||||
else:
|
||||
messages.error(request, 'Your account has been disabled.')
|
||||
else:
|
||||
messages.warning(request, 'Authenticated character has no owning account. Please log in with username and password.')
|
||||
messages.warning(request,
|
||||
'Authenticated character has no owning account. Please log in with username and password.')
|
||||
except EveCharacter.DoesNotExist:
|
||||
messages.error(request, 'No account exists with the authenticated character. Please create an account first.')
|
||||
return redirect(login_user)
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
from __future__ import unicode_literals
|
||||
from corputils.models import CorpStats
|
||||
from django.contrib import admin
|
||||
|
||||
admin.site.register(CorpStats)
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class CorputilsSearchForm(forms.Form):
|
||||
search_string = forms.CharField(max_length=254, required=True, label="",
|
||||
widget=forms.TextInput(attrs={'placeholder': _('Search characters...')}))
|
||||
42
corputils/managers.py
Normal file
42
corputils/managers.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from django.db import models
|
||||
from authentication.models import AuthServicesInfo
|
||||
from eveonline.models import EveCharacter
|
||||
|
||||
|
||||
class CorpStatsQuerySet(models.QuerySet):
|
||||
def visible_to(self, user):
|
||||
# superusers get all visible
|
||||
if user.is_superuser:
|
||||
return self
|
||||
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
try:
|
||||
char = EveCharacter.objects.get(character_id=auth.main_char_id)
|
||||
# build all accepted queries
|
||||
queries = []
|
||||
if user.has_perm('corputils.view_corp_corpstats'):
|
||||
queries.append(models.Q(corp__corporation_id=char.corporation_id))
|
||||
if user.has_perm('corputils.view_alliance_corpstats'):
|
||||
queries.append(models.Q(corp__alliance__alliance_id=char.alliance_id))
|
||||
if user.has_perm('corputils.view_blue_corpstats'):
|
||||
queries.append(models.Q(corp__is_blue=True))
|
||||
|
||||
# filter based on queries
|
||||
if queries:
|
||||
query = queries.pop()
|
||||
for q in queries:
|
||||
query |= q
|
||||
return self.filter(query)
|
||||
else:
|
||||
# not allowed to see any
|
||||
return self.none()
|
||||
except EveCharacter.DoesNotExist:
|
||||
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)
|
||||
35
corputils/migrations/0001_initial.py
Normal file
35
corputils/migrations/0001_initial.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-12-14 21:36
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('esi', '0002_scopes_20161208'),
|
||||
('eveonline', '0004_eveapikeypair_sso_verified'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CorpStats',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('last_update', models.DateTimeField(auto_now=True)),
|
||||
('_members', models.TextField(default='{}')),
|
||||
('corp', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='eveonline.EveCorporationInfo')),
|
||||
('token', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='esi.Token')),
|
||||
],
|
||||
options={
|
||||
'default_permissions': ('add', 'change', 'remove', 'view_corp', 'view_alliance', 'view_blue'),
|
||||
'verbose_name': 'corp stats',
|
||||
'verbose_name_plural': 'corp stats',
|
||||
'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.')),
|
||||
},
|
||||
),
|
||||
]
|
||||
125
corputils/migrations/0002_migrate_permissions.py
Normal file
125
corputils/migrations/0002_migrate_permissions.py
Normal file
@@ -0,0 +1,125 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-12-14 21:48
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
PERMISSIONS = {
|
||||
'user': [
|
||||
'corp_apis',
|
||||
'alliance_apis',
|
||||
],
|
||||
'corpstats': {
|
||||
'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.',
|
||||
}
|
||||
}
|
||||
|
||||
def user_permissions_dict(apps):
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
User = apps.get_model('auth', 'User')
|
||||
CorpStats = apps.get_model('corputils', 'CorpStats')
|
||||
|
||||
user_ct = ContentType.objects.get_for_model(User)
|
||||
corpstats_ct = ContentType.objects.get_for_model(CorpStats)
|
||||
|
||||
return {
|
||||
'user': {x: Permission.objects.get_or_create(name=x, codename=x, content_type=user_ct)[0] for x in PERMISSIONS['user']},
|
||||
'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)
|
||||
|
||||
corp_users = users_with_permission(apps, perm_dict['user']['corp_apis'])
|
||||
for u in corp_users:
|
||||
u.user_permissions.add(perm_dict['corpstats']['corp_apis'].pk)
|
||||
u.user_permissions.add(perm_dict['corpstats']['view_corp_corpstats'].pk)
|
||||
|
||||
alliance_users = users_with_permission(apps, perm_dict['user']['alliance_apis'])
|
||||
for u in alliance_users:
|
||||
u.user_permissions.add(perm_dict['corpstats']['alliance_apis'].pk)
|
||||
u.user_permissions.add(perm_dict['corpstats']['view_alliance_corpstats'].pk)
|
||||
|
||||
corp_groups = groups_with_permission(apps, perm_dict['user']['corp_apis'])
|
||||
for g in corp_groups:
|
||||
g.permissions.add(perm_dict['corpstats']['corp_apis'].pk)
|
||||
g.permissions.add(perm_dict['corpstats']['view_corp_corpstats'].pk)
|
||||
|
||||
alliance_groups = groups_with_permission(apps, perm_dict['user']['alliance_apis'])
|
||||
for g in alliance_groups:
|
||||
g.permissions.add(perm_dict['corpstats']['alliance_apis'].pk)
|
||||
g.permissions.add(perm_dict['corpstats']['view_alliance_corpstats'].pk)
|
||||
|
||||
for name, perm in perm_dict['user'].items():
|
||||
perm.delete()
|
||||
|
||||
def reverse(apps, schema_editor):
|
||||
perm_dict = user_permissions_dict(apps)
|
||||
|
||||
corp_users = users_with_permission(apps, perm_dict['corpstats']['view_corp_corpstats'])
|
||||
corp_api_users = users_with_permission(apps, perm_dict['corpstats']['corp_apis'])
|
||||
corp_us = corp_users | corp_api_users
|
||||
for u in corp_us.distinct():
|
||||
u.user_permissions.add(perm_dict['user']['corp_apis'].pk)
|
||||
for u in corp_users:
|
||||
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'])
|
||||
alliance_us = alliance_users | alliance_api_users
|
||||
for u in alliance_us.distinct():
|
||||
u.user_permissions.add(perm_dict['user']['alliance_apis'].pk)
|
||||
for u in alliance_users:
|
||||
u.user_permissions.remove(perm_dict['corpstats']['view_alliance_corpstats'].pk)
|
||||
for u in alliance_api_users:
|
||||
u.user_permissions.remove(perm_dict['corpstats']['alliance_apis'].pk)
|
||||
|
||||
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:
|
||||
g.permissions.remove(perm_dict['corpstats']['view_corp_corpstats'].pk)
|
||||
for g in corp_api_groups:
|
||||
g.permissions.remove(perm_dict['corpstats']['corp_apis'].pk)
|
||||
|
||||
alliance_groups = groups_with_permission(apps, perm_dict['corpstats']['view_alliance_corpstats'])
|
||||
alliance_api_groups = groups_with_permission(apps, perm_dict['corpstats']['alliance_apis'])
|
||||
alliance_gs = alliance_groups | alliance_api_groups
|
||||
for g in alliance_gs.distinct():
|
||||
g.permissions.add(perm_dict['user']['alliance_apis'].pk)
|
||||
for g in alliance_groups:
|
||||
g.permissions.remove(perm_dict['corpstats']['view_alliance_corpstats'].pk)
|
||||
for g in alliance_api_groups:
|
||||
g.permissions.remove(perm_dict['corpstats']['alliance_apis'].pk)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('corputils', '0001_initial'),
|
||||
('authentication', '0005_delete_perms'),
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(forward, reverse),
|
||||
]
|
||||
0
corputils/migrations/__init__.py
Normal file
0
corputils/migrations/__init__.py
Normal file
@@ -1 +1,185 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.db import models
|
||||
from eveonline.models import EveCorporationInfo, EveCharacter, EveApiKeyPair
|
||||
from esi.models import Token
|
||||
from esi.errors import TokenError
|
||||
from notifications import notify
|
||||
from authentication.models import AuthServicesInfo
|
||||
from bravado.exception import HTTPForbidden
|
||||
from corputils.managers import CorpStatsManager
|
||||
from operator import attrgetter
|
||||
import json
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CorpStats(models.Model):
|
||||
token = models.ForeignKey(Token, on_delete=models.CASCADE)
|
||||
corp = models.OneToOneField(EveCorporationInfo)
|
||||
last_update = models.DateTimeField(auto_now=True)
|
||||
_members = models.TextField(default='{}')
|
||||
|
||||
class Meta:
|
||||
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"
|
||||
|
||||
objects = CorpStatsManager()
|
||||
|
||||
def __str__(self):
|
||||
return "%s for %s" % (self.__class__.__name__, self.corp)
|
||||
|
||||
def update(self):
|
||||
try:
|
||||
c = self.token.get_esi_client()
|
||||
assert c.Character.get_characters_character_id(character_id=self.token.character_id).result()[
|
||||
'corporation_id'] == int(self.corp.corporation_id)
|
||||
members = c.Corporation.get_corporations_corporation_id_members(
|
||||
corporation_id=self.corp.corporation_id).result()
|
||||
member_ids = [m['character_id'] for m in members]
|
||||
|
||||
# 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.Character.get_characters_names(character_ids=id_chunk).result() for id_chunk in
|
||||
member_id_chunks]
|
||||
member_list = {}
|
||||
for name_chunk in member_name_chunks:
|
||||
member_list.update({m['character_id']: m['character_name'] for m in name_chunk})
|
||||
|
||||
self.members = member_list
|
||||
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 members(self):
|
||||
return json.loads(self._members)
|
||||
|
||||
@members.setter
|
||||
def members(self, dict):
|
||||
self._members = json.dumps(dict)
|
||||
|
||||
@property
|
||||
def member_ids(self):
|
||||
return [id for id, name in self.members.items()]
|
||||
|
||||
@property
|
||||
def member_names(self):
|
||||
return [name for id, name in self.members.items()]
|
||||
|
||||
def show_apis(self, user):
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
if auth.main_char_id:
|
||||
try:
|
||||
char = EveCharacter.objects.get(character_id=auth.main_char_id)
|
||||
if char.corporation_id == self.corp.corporation_id and user.has_perm('corputils.corp_apis'):
|
||||
return True
|
||||
if self.corp.alliance and char.alliance_id == self.corp.alliance.alliance_id and user.has_perm(
|
||||
'corputils.alliance_apis'):
|
||||
return True
|
||||
if user.has_perm('corputils.blue_apis') and self.corp.is_blue:
|
||||
return True
|
||||
except EveCharacter.DoesNotExist:
|
||||
pass
|
||||
return user.is_superuser
|
||||
|
||||
def entered_apis(self):
|
||||
return EveCharacter.objects.filter(character_id__in=self.member_ids).exclude(api_id__isnull=True).count()
|
||||
|
||||
def member_count(self):
|
||||
return len(self.members)
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class MemberObject(object):
|
||||
def __init__(self, character_id, character_name, show_apis=False):
|
||||
self.character_id = character_id
|
||||
self.character_name = character_name
|
||||
try:
|
||||
char = EveCharacter.objects.get(character_id=character_id)
|
||||
auth = AuthServicesInfo.objects.get(user=char.user)
|
||||
try:
|
||||
self.main = EveCharacter.objects.get(character_id=auth.main_char_id)
|
||||
except EveCharacter.DoesNotExist:
|
||||
self.main = None
|
||||
api = EveApiKeyPair.objects.get(api_id=char.api_id)
|
||||
self.registered = True
|
||||
if show_apis:
|
||||
self.api = api
|
||||
else:
|
||||
self.api = None
|
||||
except (EveCharacter.DoesNotExist, AuthServicesInfo.DoesNotExist):
|
||||
self.main = None
|
||||
self.api = None
|
||||
self.registered = False
|
||||
except EveApiKeyPair.DoesNotExist:
|
||||
self.api = None
|
||||
self.registered = False
|
||||
|
||||
def __str__(self):
|
||||
return self.character_name
|
||||
|
||||
def portrait_url(self, size=32):
|
||||
return "https://image.eveonline.com/Character/%s_%s.jpg" % (self.character_id, size)
|
||||
|
||||
def get_member_objects(self, user):
|
||||
show_apis = self.show_apis(user)
|
||||
return sorted([CorpStats.MemberObject(id, name, show_apis=show_apis) for id, name in self.members.items()],
|
||||
key=attrgetter('character_name'))
|
||||
|
||||
def can_update(self, user):
|
||||
return user.is_superuser or user == self.token.user
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ViewModel(object):
|
||||
def __init__(self, corpstats, user):
|
||||
self.corp = corpstats.corp
|
||||
self.members = corpstats.get_member_objects(user)
|
||||
self.can_update = corpstats.can_update(user)
|
||||
self.total_members = len(self.members)
|
||||
self.registered_members = corpstats.entered_apis()
|
||||
self.show_apis = corpstats.show_apis(user)
|
||||
self.last_updated = corpstats.last_update
|
||||
|
||||
def __str__(self):
|
||||
return str(self.corp)
|
||||
|
||||
def corp_logo(self, size=128):
|
||||
return "https://image.eveonline.com/Corporation/%s_%s.png" % (self.corp.corporation_id, size)
|
||||
|
||||
def alliance_logo(self, size=128):
|
||||
if self.corp.alliance:
|
||||
return "https://image.eveonline.com/Alliance/%s_%s.png" % (self.corp.alliance.alliance_id, size)
|
||||
else:
|
||||
return "https://image.eveonline.com/Alliance/1_%s.png" % size
|
||||
|
||||
def get_view_model(self, user):
|
||||
return CorpStats.ViewModel(self, user)
|
||||
|
||||
15
corputils/tasks.py
Normal file
15
corputils/tasks.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from corputils.models import CorpStats
|
||||
from celery.task import task, periodic_task
|
||||
from celery.task.schedules import crontab
|
||||
|
||||
|
||||
@task
|
||||
def update_corpstats(pk):
|
||||
cs = CorpStats.objects.get(pk=pk)
|
||||
cs.update()
|
||||
|
||||
|
||||
@periodic_task(run_every=crontab(minute=0, hour="*/6"))
|
||||
def update_all_corpstats():
|
||||
for cs in CorpStats.objects.all():
|
||||
update_corpstats.delay(cs.pk)
|
||||
39
corputils/templates/corputils/base.html
Normal file
39
corputils/templates/corputils/base.html
Normal file
@@ -0,0 +1,39 @@
|
||||
{% extends 'public/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">
|
||||
<h1 class="page-header text-center">{% trans "Corporation Member Data" %}</h1>
|
||||
<div class="col-lg-10 col-lg-offset-1 container">
|
||||
<nav class="navbar navbar-default">
|
||||
<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">Corporations<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{% for corpstat in available %}
|
||||
<li>
|
||||
<a href="{% url 'corputils:view_corp' corpstat.corp.corporation_id %}">{{ corpstat.corp.corporation_name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% if perms.corputils.add_corpstats %}
|
||||
<li>
|
||||
<a href="{% url 'corputils:add' %}">Add</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</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 %}Search characters...{% endif %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
{% block member_data %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
92
corputils/templates/corputils/corpstats.html
Normal file
92
corputils/templates/corputils/corpstats.html
Normal file
@@ -0,0 +1,92 @@
|
||||
{% extends 'corputils/base.html' %}
|
||||
{% load i18n %}
|
||||
{% load humanize %}
|
||||
{% load bootstrap_pagination %}
|
||||
{% 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 }}"></td>
|
||||
{% if corpstats.corp.alliance %}
|
||||
<td class="text-center col-lg-6"><img class="ra-avatar" src="{{ corpstats.alliance_logo }}"></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">
|
||||
<b>{% trans "API Index:" %}</b>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="{{ corpstats.registered_members }}" aria-valuemin="0" aria-valuemax="{{ corpstats.total_members }}" style="width: {% widthratio corpstats.registered_members corpstats.total_members 100 %}%;">
|
||||
{{ corpstats.registered_members }}/{{ corpstats.total_members }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading clearfix">
|
||||
<div class="panel-title pull-left">
|
||||
<h4>Members</h4>
|
||||
</div>
|
||||
<div class="panel-title pull-right">
|
||||
Last update: {{ corpstats.last_updated|naturaltime }}
|
||||
{% if corpstats.can_update %}
|
||||
<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>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="text-center">
|
||||
{% bootstrap_paginate members range=10 %}
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="text-center">Character</th>
|
||||
{% if corpstats.show_apis %}
|
||||
<th class="text-center">API</th>
|
||||
{% endif %}
|
||||
<th class="text-center">zKillboard</th>
|
||||
<th class="text-center">Main Character</th>
|
||||
<th class="text-center">Main Corporation</th>
|
||||
<th class="text-center">Main Alliance</th>
|
||||
</tr>
|
||||
{% for member in members %}
|
||||
<tr {% if not member.registered %}class="danger"{% endif %}>
|
||||
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
|
||||
<td class="text-center">{{ member.character_name }}</td>
|
||||
{% if corpstats.show_apis %}
|
||||
{% if member.api %}
|
||||
<td class="text-center"><a href="{{ JACK_KNIFE_URL }}?usid={{ member.api.api_id }}&apik={{ member.api.api_key }}" target="_blank" class="label label-primary">{{ member.api.api_id }}</td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<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.main.character_name }}</td>
|
||||
<td class="text-center">{{ member.main.corporation_name }}</td>
|
||||
<td class="text-center">{{ member.main.alliance_name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
43
corputils/templates/corputils/search.html
Normal file
43
corputils/templates/corputils/search.html
Normal file
@@ -0,0 +1,43 @@
|
||||
{% extends "corputils/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap_pagination %}
|
||||
{% block member_data %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading clearfix">
|
||||
<div class="panel-title pull-left">Search Results</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="text-center">
|
||||
{% bootstrap_paginate results range=10 %}
|
||||
</div>
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th class="text-center"></th>
|
||||
<th class="text-center">Character</th>
|
||||
<th class="text-center">Corporation</th>
|
||||
<th class="text-center">API</th>
|
||||
<th class="text-center">zKillboard</th>
|
||||
<th class="text-center">Main Character</th>
|
||||
<th class="text-center">Main Corporation</th>
|
||||
<th class="text-center">Main Alliance</th>
|
||||
</tr>
|
||||
{% for result in results %}
|
||||
<tr {% if not result.1.registered %}class="danger"{% endif %}>
|
||||
<td class="text-center"><img src="{{ result.1.portrait_url }}" class="img-circle"></td>
|
||||
<td class="text-center">{{ result.1.character_name }}</td>
|
||||
<td class="text-center">{{ result.0.corp.corporation_name }}</td>
|
||||
{% if result.1.api %}
|
||||
<td class="text-center"><a href="{{ JACK_KNIFE_URL }}?usid={{ result.1.api.api_id }}&apik={{ result.1.api.api_key }}" target="_blank" class="label label-primary">{{ result.1.api.api_id }}</td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
<td class="text-center"><a href="https://zkillboard.com/character/{{ result.1.character_id }}/" class="label label-danger" target="_blank">{% trans "Killboard" %}</a></td>
|
||||
<td class="text-center">{{ result.1.main.character_name }}</td>
|
||||
<td class="text-center">{{ result.1.main.corporation_name }}</td>
|
||||
<td class="text-center">{{ result.1.main.alliance_name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
11
corputils/urls.py
Normal file
11
corputils/urls.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.conf.urls import url
|
||||
import corputils.views
|
||||
|
||||
app_name = 'corputils'
|
||||
urlpatterns = [
|
||||
url(r'^$', corputils.views.corpstats_view, name='view'),
|
||||
url(r'^add/$', corputils.views.corpstats_add, name='add'),
|
||||
url(r'^(?P<corp_id>(\d)*)/$', corputils.views.corpstats_view, name='view_corp'),
|
||||
url(r'^(?P<corp_id>(\d)+)/update/$', corputils.views.corpstats_update, name='update'),
|
||||
url(r'^search/$', corputils.views.corpstats_search, name='search'),
|
||||
]
|
||||
@@ -1,325 +1,142 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib import messages
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db import IntegrityError
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.conf import settings
|
||||
from eveonline.models import EveCharacter, EveCorporationInfo
|
||||
from corputils.models import CorpStats
|
||||
from esi.decorators import token_required
|
||||
from bravado.exception import HTTPError
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
from authentication.models import AuthServicesInfo
|
||||
from services.managers.eve_api_manager import EveApiManager
|
||||
from services.managers.evewho_manager import EveWhoManager
|
||||
from eveonline.models import EveCorporationInfo
|
||||
from eveonline.models import EveAllianceInfo
|
||||
from eveonline.models import EveCharacter
|
||||
from eveonline.models import EveApiKeyPair
|
||||
from fleetactivitytracking.models import Fat
|
||||
from corputils.forms import CorputilsSearchForm
|
||||
from evelink.api import APIError
|
||||
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
MEMBERS_PER_PAGE = int(getattr(settings, 'CORPSTATS_MEMBERS_PER_PAGE', 20))
|
||||
|
||||
|
||||
class Player(object):
|
||||
def __init__(self, main, user, maincorp, maincorpid, altlist, apilist, n_fats):
|
||||
self.main = main
|
||||
self.user = user
|
||||
self.maincorp = maincorp
|
||||
self.maincorpid = maincorpid
|
||||
self.altlist = altlist
|
||||
self.apilist = apilist
|
||||
self.n_fats = n_fats
|
||||
def get_page(model_list, page_num):
|
||||
p = Paginator(model_list, MEMBERS_PER_PAGE)
|
||||
try:
|
||||
members = p.page(page_num)
|
||||
except PageNotAnInteger:
|
||||
members = p.page(1)
|
||||
except EmptyPage:
|
||||
members = p.page(p.num_pages)
|
||||
return members
|
||||
|
||||
|
||||
def first_day_of_next_month(year, month):
|
||||
if month == 12:
|
||||
return datetime.datetime(year + 1, 1, 1)
|
||||
else:
|
||||
return datetime.datetime(year, month + 1, 1)
|
||||
|
||||
|
||||
def first_day_of_previous_month(year, month):
|
||||
if month == 1:
|
||||
return datetime.datetime(year - 1, 12, 1)
|
||||
else:
|
||||
return datetime.datetime(year, month - 1, 1)
|
||||
def access_corpstats_test(user):
|
||||
return user.has_perm('corputils.view_corp_corpstats') or user.has_perm(
|
||||
'corputils.view_alliance_corpstats') or user.has_perm('corputils.view_blue_corpstats')
|
||||
|
||||
|
||||
@login_required
|
||||
def corp_member_view(request, corpid=None, year=datetime.date.today().year, month=datetime.date.today().month):
|
||||
year = int(year)
|
||||
month = int(month)
|
||||
start_of_month = datetime.datetime(year, month, 1)
|
||||
start_of_next_month = first_day_of_next_month(year, month)
|
||||
start_of_previous_month = first_day_of_previous_month(year, month)
|
||||
logger.debug("corp_member_view called by user %s" % request.user)
|
||||
|
||||
@user_passes_test(access_corpstats_test)
|
||||
@permission_required('corputils.add_corpstats')
|
||||
@token_required(scopes='esi-corporations.read_corporation_membership.v1')
|
||||
def corpstats_add(request, token):
|
||||
try:
|
||||
user_main = EveCharacter.objects.get(
|
||||
character_id=AuthServicesInfo.objects.get_or_create(user=request.user)[0].main_char_id)
|
||||
user_corp_id = user_main.corporation_id
|
||||
except (ValueError, EveCharacter.DoesNotExist):
|
||||
user_corp_id = settings.CORP_ID
|
||||
|
||||
if not settings.IS_CORP:
|
||||
alliance = EveAllianceInfo.objects.get(alliance_id=settings.ALLIANCE_ID)
|
||||
alliancecorps = EveCorporationInfo.objects.filter(alliance=alliance)
|
||||
membercorplist = [(int(membercorp.corporation_id), str(membercorp.corporation_name)) for membercorp in
|
||||
alliancecorps]
|
||||
membercorplist.sort(key=lambda tup: tup[1])
|
||||
membercorp_id_list = [int(membercorp.corporation_id) for membercorp in alliancecorps]
|
||||
|
||||
bluecorps = EveCorporationInfo.objects.filter(is_blue=True)
|
||||
bluecorplist = [(int(bluecorp.corporation_id), str(bluecorp.corporation_name)) for bluecorp in bluecorps]
|
||||
bluecorplist.sort(key=lambda tup: tup[1])
|
||||
bluecorp_id_list = [int(bluecorp.corporation_id) for bluecorp in bluecorps]
|
||||
|
||||
if not (user_corp_id in membercorp_id_list or user_corp_id not in bluecorp_id_list):
|
||||
user_corp_id = None
|
||||
|
||||
if not corpid:
|
||||
if settings.IS_CORP:
|
||||
corpid = settings.CORP_ID
|
||||
elif user_corp_id:
|
||||
corpid = user_corp_id
|
||||
if EveCharacter.objects.filter(character_id=token.character_id).exists():
|
||||
corp_id = EveCharacter.objects.get(character_id=token.character_id).corporation_id
|
||||
else:
|
||||
corpid = membercorplist[0][0]
|
||||
|
||||
corp = EveCorporationInfo.objects.get(corporation_id=corpid)
|
||||
if request.user.has_perm('auth.alliance_apis') or (request.user.has_perm('auth.corp_apis') and user_corp_id == corpid):
|
||||
logger.debug("Retreiving and sending API-information")
|
||||
|
||||
if settings.IS_CORP:
|
||||
try:
|
||||
member_list = EveApiManager.get_corp_membertracking(settings.CORP_API_ID, settings.CORP_API_VCODE)
|
||||
except APIError:
|
||||
logger.debug("Corp API does not have membertracking scope, using EveWho data instead.")
|
||||
member_list = EveWhoManager.get_corporation_members(corpid)
|
||||
else:
|
||||
member_list = EveWhoManager.get_corporation_members(corpid)
|
||||
|
||||
characters_with_api = {}
|
||||
characters_without_api = {}
|
||||
|
||||
num_registered_characters = 0
|
||||
for char_id, member_data in member_list.items():
|
||||
try:
|
||||
char = EveCharacter.objects.get(character_id=char_id)
|
||||
char_owner = char.user
|
||||
try:
|
||||
if not char_owner:
|
||||
raise AttributeError("Character has no assigned user.")
|
||||
mainid = int(AuthServicesInfo.objects.get_or_create(user=char_owner)[0].main_char_id)
|
||||
mainchar = EveCharacter.objects.get(character_id=mainid)
|
||||
mainname = mainchar.character_name
|
||||
maincorp = mainchar.corporation_name
|
||||
maincorpid = mainchar.corporation_id
|
||||
api_pair = EveApiKeyPair.objects.get(api_id=char.api_id)
|
||||
except (ValueError, EveCharacter.DoesNotExist, EveApiKeyPair.DoesNotExist):
|
||||
logger.debug("No main character seem to be set for character %s" % char.character_name)
|
||||
mainname = "User: " + char_owner.username
|
||||
mainchar = char
|
||||
maincorp = "Not set."
|
||||
maincorpid = None
|
||||
api_pair = None
|
||||
except AttributeError:
|
||||
logger.debug("No associated user for character %s" % char.character_name)
|
||||
mainname = None
|
||||
mainchar = char
|
||||
maincorp = None
|
||||
maincorpid = None
|
||||
try:
|
||||
api_pair = EveApiKeyPair.objects.get(api_id=char.api_id)
|
||||
except EveApiKeyPair.DoesNotExist:
|
||||
api_pair = None
|
||||
num_registered_characters += 1
|
||||
characters_with_api.setdefault(mainname, Player(main=mainchar,
|
||||
user=char_owner,
|
||||
maincorp=maincorp,
|
||||
maincorpid=maincorpid,
|
||||
altlist=[],
|
||||
apilist=[],
|
||||
n_fats=0)
|
||||
).altlist.append(char)
|
||||
if api_pair:
|
||||
characters_with_api[mainname].apilist.append(api_pair)
|
||||
|
||||
except EveCharacter.DoesNotExist:
|
||||
characters_without_api.update({member_data["name"]: member_data["id"]})
|
||||
|
||||
for char in EveCharacter.objects.filter(corporation_id=corpid):
|
||||
if not int(char.character_id) in member_list:
|
||||
logger.debug("Character '%s' does not exist in EveWho dump." % char.character_name)
|
||||
char_owner = char.user
|
||||
try:
|
||||
if not char_owner:
|
||||
raise AttributeError("Character has no assigned user.")
|
||||
mainid = int(AuthServicesInfo.objects.get_or_create(user=char_owner)[0].main_char_id)
|
||||
mainchar = EveCharacter.objects.get(character_id=mainid)
|
||||
mainname = mainchar.character_name
|
||||
maincorp = mainchar.corporation_name
|
||||
maincorpid = mainchar.corporation_id
|
||||
api_pair = EveApiKeyPair.objects.get(api_id=char.api_id)
|
||||
except (ValueError, EveCharacter.DoesNotExist, EveApiKeyPair.DoesNotExist):
|
||||
logger.debug("No main character seem to be set for character %s" % char.character_name)
|
||||
mainname = "User: " + char_owner.username
|
||||
mainchar = char
|
||||
maincorp = "Not set."
|
||||
maincorpid = None
|
||||
api_pair = None
|
||||
except AttributeError:
|
||||
logger.debug("No associated user for character %s" % char.character_name)
|
||||
mainname = None
|
||||
mainchar = char
|
||||
maincorp = None
|
||||
maincorpid = None
|
||||
try:
|
||||
api_pair = EveApiKeyPair.objects.get(api_id=char.api_id)
|
||||
except EveApiKeyPair.DoesNotExist:
|
||||
api_pair = None
|
||||
num_registered_characters += 1
|
||||
characters_with_api.setdefault(mainname, Player(main=mainchar,
|
||||
user=char_owner,
|
||||
maincorp=maincorp,
|
||||
maincorpid=maincorpid,
|
||||
altlist=[],
|
||||
apilist=[],
|
||||
n_fats=0)
|
||||
).altlist.append(char)
|
||||
if api_pair:
|
||||
characters_with_api[mainname].apilist.append(api_pair)
|
||||
|
||||
n_unacounted = corp.member_count - (num_registered_characters + len(characters_without_api))
|
||||
|
||||
for mainname, player in characters_with_api.items():
|
||||
fats_this_month = Fat.objects.filter(user=player.user).filter(
|
||||
fatlink__fatdatetime__gte=start_of_month).filter(fatlink__fatdatetime__lt=start_of_next_month)
|
||||
characters_with_api[mainname].n_fats = len(fats_this_month)
|
||||
|
||||
if start_of_next_month > datetime.datetime.now():
|
||||
start_of_next_month = None
|
||||
|
||||
if not settings.IS_CORP:
|
||||
context = {"membercorplist": membercorplist,
|
||||
"corp": corp,
|
||||
"characters_with_api": sorted(characters_with_api.items()),
|
||||
'n_registered': num_registered_characters,
|
||||
'n_unacounted': n_unacounted,
|
||||
"characters_without_api": sorted(characters_without_api.items()),
|
||||
"search_form": CorputilsSearchForm()}
|
||||
else:
|
||||
logger.debug("corp_member_view running in corportation mode")
|
||||
context = {"corp": corp,
|
||||
"characters_with_api": sorted(characters_with_api.items()),
|
||||
'n_registered': num_registered_characters,
|
||||
'n_unacounted': n_unacounted,
|
||||
"characters_without_api": sorted(characters_without_api.items()),
|
||||
"search_form": CorputilsSearchForm()}
|
||||
|
||||
context["next_month"] = start_of_next_month
|
||||
context["previous_month"] = start_of_previous_month
|
||||
context["this_month"] = start_of_month
|
||||
|
||||
return render(request, 'registered/corputils.html', context=context)
|
||||
else:
|
||||
logger.warn('User %s (%s) not authorized to view corp stats for corp id %s' % (request.user, user_corp_id, corpid))
|
||||
return redirect("auth_dashboard")
|
||||
|
||||
|
||||
def can_see_api(user, character):
|
||||
if user.has_perm('auth.alliance_apis'):
|
||||
return True
|
||||
try:
|
||||
user_main = EveCharacter.objects.get(
|
||||
character_id=AuthServicesInfo.objects.get_or_create(user=user)[0].main_char_id)
|
||||
if user.has_perm('auth.corp_apis') and user_main.corporation_id == character.corporation_id:
|
||||
return True
|
||||
except EveCharacter.DoesNotExist:
|
||||
return False
|
||||
return False
|
||||
corp_id = \
|
||||
token.get_esi_client().Character.get_characters_character_id(character_id=token.character_id).result()[
|
||||
'corporation_id']
|
||||
corp = EveCorporationInfo.objects.get(corporation_id=corp_id)
|
||||
cs = CorpStats.objects.create(token=token, corp=corp)
|
||||
cs.update()
|
||||
assert cs.pk # ensure update was succesful
|
||||
if CorpStats.objects.filter(pk=cs.pk).visible_to(request.user).exists():
|
||||
return redirect('corputils:view_corp', corp_id=corp.corporation_id)
|
||||
except EveCorporationInfo.DoesNotExist:
|
||||
messages.error(request, 'Unrecognized corporation. Please ensure it is a member of the alliance or a blue.')
|
||||
except IntegrityError:
|
||||
messages.error(request, 'Selected corp already has a statistics module.')
|
||||
except AssertionError:
|
||||
messages.error(request, 'Failed to gather corporation statistics with selected token.')
|
||||
return redirect('corputils:view')
|
||||
|
||||
|
||||
@login_required
|
||||
def corputils_search(request, corpid=settings.CORP_ID):
|
||||
logger.debug("corputils_search called by user %s" % request.user)
|
||||
@user_passes_test(access_corpstats_test)
|
||||
def corpstats_view(request, corp_id=None):
|
||||
corpstats = None
|
||||
|
||||
corp = EveCorporationInfo.objects.get(corporation_id=corpid)
|
||||
# get requested model
|
||||
if corp_id:
|
||||
corp = get_object_or_404(EveCorporationInfo, corporation_id=corp_id)
|
||||
corpstats = get_object_or_404(CorpStats, corp=corp)
|
||||
|
||||
authorized = False
|
||||
try:
|
||||
user_main = EveCharacter.objects.get(
|
||||
character_id=AuthServicesInfo.objects.get_or_create(user=request.user)[0].main_char_id)
|
||||
if request.user.has_perm('auth.alliance_apis') or (
|
||||
request.user.has_perm('auth.corp_apis') and (user_main.corporation_id == corpid)):
|
||||
logger.debug("Retreiving and sending API-information")
|
||||
authorized = True
|
||||
except (ValueError, EveCharacter.DoesNotExist):
|
||||
if request.user.has_perm('auth.alliance_apis'):
|
||||
logger.debug("Retrieving and sending API-information")
|
||||
authorized = True
|
||||
# get available models
|
||||
available = CorpStats.objects.visible_to(request.user)
|
||||
|
||||
if authorized:
|
||||
if request.method == 'POST':
|
||||
form = CorputilsSearchForm(request.POST)
|
||||
logger.debug("Request type POST contains form valid: %s" % form.is_valid())
|
||||
if form.is_valid():
|
||||
# Really dumb search and only checks character name
|
||||
# This can be improved but it does the job for now
|
||||
searchstring = form.cleaned_data['search_string']
|
||||
logger.debug("Searching for player with character name %s for user %s" % (searchstring, request.user))
|
||||
# ensure we can see the requested model
|
||||
if corpstats and corpstats not in available:
|
||||
raise PermissionDenied('You do not have permission to view the selected corporation statistics module.')
|
||||
|
||||
member_list = {}
|
||||
if settings.IS_CORP:
|
||||
member_list = EveApiManager.get_corp_membertracking(settings.CORP_API_ID, settings.CORP_API_VCODE)
|
||||
if not member_list:
|
||||
logger.debug('Unable to fetch members from API. Pulling from EveWho')
|
||||
member_list = EveWhoManager.get_corporation_members(corpid)
|
||||
# get default model if none requested
|
||||
if not corp_id and available.count() == 1:
|
||||
corpstats = available[0]
|
||||
|
||||
SearchResult = namedtuple('SearchResult',
|
||||
['name', 'id', 'main', 'api_registered', 'character', 'apiinfo'])
|
||||
context = {
|
||||
'available': available,
|
||||
}
|
||||
|
||||
searchresults = []
|
||||
for memberid, member_data in member_list.items():
|
||||
if searchstring.lower() in member_data["name"].lower():
|
||||
try:
|
||||
char = EveCharacter.objects.get(character_name=member_data["name"])
|
||||
user = char.user
|
||||
mainid = int(AuthServicesInfo.objects.get_or_create(user=user)[0].main_char_id)
|
||||
main = EveCharacter.objects.get(character_id=mainid)
|
||||
if can_see_api(request.user, char):
|
||||
api_registered = True
|
||||
apiinfo = EveApiKeyPair.objects.get(api_id=char.api_id)
|
||||
else:
|
||||
api_registered = False
|
||||
apiinfo = None
|
||||
except EveCharacter.DoesNotExist:
|
||||
api_registered = False
|
||||
char = None
|
||||
main = ""
|
||||
apiinfo = None
|
||||
# paginate
|
||||
members = []
|
||||
if corpstats:
|
||||
page = request.GET.get('page', 1)
|
||||
members = get_page(corpstats.get_member_objects(request.user), page)
|
||||
|
||||
searchresults.append(SearchResult(name=member_data["name"], id=memberid, main=main,
|
||||
api_registered=api_registered,
|
||||
character=char, apiinfo=apiinfo))
|
||||
if corpstats:
|
||||
context.update({
|
||||
'corpstats': corpstats.get_view_model(request.user),
|
||||
'members': members,
|
||||
})
|
||||
|
||||
logger.info("Found %s members for user %s matching search string %s" % (
|
||||
len(searchresults), request.user, searchstring))
|
||||
return render(request, 'corputils/corpstats.html', context=context)
|
||||
|
||||
context = {'corp': corp, 'results': searchresults, 'search_form': CorputilsSearchForm(),
|
||||
"year": datetime.datetime.now().year, "month": datetime.datetime.now().month}
|
||||
|
||||
return render(request, 'registered/corputilssearchview.html',
|
||||
context=context)
|
||||
else:
|
||||
logger.debug("Form invalid - returning for user %s to retry." % request.user)
|
||||
context = {'corp': corp, 'members': None, 'search_form': CorputilsSearchForm()}
|
||||
return render(request, 'registered/corputilssearchview.html', context=context)
|
||||
|
||||
else:
|
||||
logger.debug("Returning empty search form for user %s" % request.user)
|
||||
return redirect("auth_corputils")
|
||||
@login_required
|
||||
@user_passes_test(access_corpstats_test)
|
||||
def corpstats_update(request, corp_id):
|
||||
corp = get_object_or_404(EveCorporationInfo, corporation_id=corp_id)
|
||||
corpstats = get_object_or_404(CorpStats, corp=corp)
|
||||
if corpstats.can_update(request.user):
|
||||
try:
|
||||
corpstats.update()
|
||||
except HTTPError as e:
|
||||
messages.error(request, str(e))
|
||||
else:
|
||||
logger.warn('User %s not authorized to view corp stats for corp ID %s' % (request.user, corpid))
|
||||
return redirect("auth_dashboard")
|
||||
raise PermissionDenied(
|
||||
'You do not have permission to update member data for the selected corporation statistics module.')
|
||||
if corpstats.pk:
|
||||
return redirect('corputils:view_corp', corp_id=corp.corporation_id)
|
||||
else:
|
||||
return redirect('corputils:view')
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(access_corpstats_test)
|
||||
def corpstats_search(request):
|
||||
results = []
|
||||
search_string = request.GET.get('search_string', None)
|
||||
if search_string:
|
||||
has_similar = CorpStats.objects.filter(_members__icontains=search_string).visible_to(request.user)
|
||||
for corpstats in has_similar:
|
||||
similar = [(member_id, corpstats.members[member_id]) for member_id in corpstats.members if
|
||||
search_string.lower() in corpstats.members[member_id].lower()]
|
||||
for s in similar:
|
||||
results.append(
|
||||
(corpstats, CorpStats.MemberObject(s[0], s[1], show_apis=corpstats.show_apis(request.user))))
|
||||
page = request.GET.get('page', 1)
|
||||
results = sorted(results, key=lambda x: x[1].character_name)
|
||||
results_page = get_page(results, page)
|
||||
context = {
|
||||
'available': CorpStats.objects.visible_to(request.user),
|
||||
'results': results_page,
|
||||
'search_string': search_string,
|
||||
}
|
||||
return render(request, 'corputils/search.html', context=context)
|
||||
return redirect('corputils:view')
|
||||
|
||||
1
docs/.gitignore
vendored
Normal file
1
docs/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
_build/
|
||||
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXPROJ = AllianceAuth
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
173
docs/conf.py
Normal file
173
docs/conf.py
Normal file
@@ -0,0 +1,173 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Alliance Auth documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue Jan 3 12:56:59 2017.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org
|
||||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||
|
||||
# Support for recommonmark module
|
||||
import recommonmark
|
||||
|
||||
from recommonmark.parser import CommonMarkParser
|
||||
from recommonmark.transform import AutoStructify
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = ['.md', '.rst']
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Alliance Auth'
|
||||
copyright = u'2017, Alliance Auth'
|
||||
author = u'R4stl1n'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'1.14'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
# release = u'1.14.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'AllianceAuthdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'AllianceAuth.tex', u'Alliance Auth Documentation',
|
||||
u'R4stl1n', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'allianceauth', u'Alliance Auth Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'AllianceAuth', u'Alliance Auth Documentation',
|
||||
author, 'AllianceAuth', 'Alliance service auth to help large scale alliances manage services.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Markdown support
|
||||
source_parsers = {
|
||||
'.md': CommonMarkParser,
|
||||
}
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_config_value('recommonmark_config', {
|
||||
'auto_toc_tree_section': 'Contents',
|
||||
}, True)
|
||||
app.add_transform(AutoStructify)
|
||||
38
docs/development/documentation.md
Normal file
38
docs/development/documentation.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Documentation
|
||||
|
||||
The documentation for Alliance Auth uses [Sphinx](http://www.sphinx-doc.org/) to build documentation. When a new commit
|
||||
to specific branches is made (master, primarily), the repository is automatically pulled, docs built and deployed on
|
||||
[readthedocs.org](https://readthedocs.org/).
|
||||
|
||||
|
||||
Documentation was migrated from the Github wiki pages and into the repository to allow documentation changes to be
|
||||
included with pull requests. This means that documentation can be guaranteed to be updated when a pull request is
|
||||
accepted rather than hoping documentation is updated afterwards or relying on maintainers to do the work. It also
|
||||
allows for documentation to be maintained at different versions more easily.
|
||||
|
||||
## Building Documentation
|
||||
If you're developing new documentation, its likely you'll want or need to test build it before committing to your
|
||||
branch. To achieve this you can use Sphinx to build the documentation locally as it appears on Read the Docs.
|
||||
|
||||
Activate your virtual environment (if you're using one) and install the documentation requirements found in
|
||||
`docs/requirements.txt` using pip, e.g. `pip install -r docs/requirements.txt`.
|
||||
|
||||
You can then build the docs by changing to the `docs/` directory and running `make html` or `make dirhtml`, depending
|
||||
on how the Read the Docs project is configured. Either should work fine for testing. You can now find the output of the
|
||||
build in the `/docs/_build/` directory.
|
||||
|
||||
Occasionally you may need to fully rebuild the documents by running `make clean` first, usually when you add or
|
||||
rearrange toctrees.
|
||||
|
||||
|
||||
## Documentation Format
|
||||
|
||||
CommonMark Markdown is the current preferred format, via [recommonmark](https://github.com/rtfd/recommonmark).
|
||||
reStructuredText is supported if required, or you can execute snippets of reST inside Markdown by using a code block:
|
||||
|
||||
```eval_rst
|
||||
reStructuredText here
|
||||
```
|
||||
|
||||
Markdown is used elsewhere on Github so it provides the most portability of documentation from Issues and Pull Requests
|
||||
as well as providing an easier initial migration path from the Github wiki.
|
||||
7
docs/development/index.md
Normal file
7
docs/development/index.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Development
|
||||
|
||||
```eval_rst
|
||||
.. toctree::
|
||||
|
||||
documentation
|
||||
```
|
||||
133
docs/features/corpstats.md
Normal file
133
docs/features/corpstats.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Corp Stats
|
||||
|
||||
This module is used to check the registration status of corp members and to determine character relationships, being mains or alts.
|
||||
|
||||
## Creating a Corp Stats
|
||||
|
||||
Upon initial install, nothing will be visible. For every corp, a model will have to be created before data can be viewed.
|
||||
|
||||

|
||||
|
||||
If you are a superuser, the add button will be immediate visible to you. If not, your user account requires the `add_corpstats` permission.
|
||||
|
||||
Corp Stats requires an EVE SSO token to access data from the EVE Swagger Interface. Upon pressing the Add button, you will be prompted to authenticated. Please select the character who is in the corp you want data for.
|
||||
|
||||

|
||||
|
||||
You will return to auth where you are asked to select a token with the green arrow button. If you want to use a different character, press the `LOG IN with EVE Online` button.
|
||||
|
||||

|
||||
|
||||
If this works (and you have permission to view the Corp Stats you just created) you'll be returned to a view of the Corp Stats.
|
||||
If it fails an error message will be displayed.
|
||||
|
||||
## Corp Stats View
|
||||
|
||||
### Navigation Bar
|
||||
|
||||

|
||||
|
||||
This bar contains a dropdown menu of all available corps. If the user has the `add_corpstats` permission, a button to add a Corp Stats will be shown.
|
||||
|
||||
On the right of this bar is a search field. Press enter to search. It checks all characters in all Corp Stats you have view permission to and returns search results. Generic searches (such as 'a') will be slow.
|
||||
|
||||
### API Index
|
||||
|
||||

|
||||
|
||||
This is a visual indication of the number of registered characters.
|
||||
|
||||
### Last Update
|
||||
|
||||

|
||||
|
||||
Corp Stats do not automatically update. They update once upon creation for initial data, and whenever someone presses the update button.
|
||||
|
||||
Only superusers and the creator of the Corp Stat can update it.
|
||||
|
||||
### Member List
|
||||
|
||||

|
||||
|
||||
The list contains all characters in the corp. Red backgrounds means they are not registered in auth. If registered, and the user has the required permission to view APIs, a link to JackKnife will be present.
|
||||
A link to zKillboard is present for all characters.
|
||||
If registered, the character will also have a main character, main corporation, and main alliance field.
|
||||
|
||||
This view is paginated: use the navigation arrows to view more pages (sorted alphabetically by character name), or search for a specific character.
|
||||
|
||||

|
||||
|
||||
## Search View
|
||||
|
||||

|
||||
|
||||
This view is essentially the same as the Corp Stats page, but not specific to a single corp.
|
||||
The search query is visible in the search box.
|
||||
Characters from all Corp Stats to which the user has view access will be displayed. APIs respect permissions.
|
||||
|
||||
This view is paginated: use the navigation arrows to view more pages (sorted alphabetically by character name).
|
||||
|
||||
## Permissions
|
||||
|
||||
To use this feature, users will require some of the following:
|
||||
|
||||
```eval_rst
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| Permission | Admin Site | Auth Site |
|
||||
+=======================================+==================+====================================================+
|
||||
| corpstats.corp_apis | None | Can view API keys of members of their corporation. |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| corpstats.alliance_apis | None | Can view API keys of members of their alliance. |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| corpstats.blue_apis | None | Can view API keys of members of blue corporations. |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| corpstats.view_corp_corpstats | None | Can view corp stats of their corporation. |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| corpstats.view_alliance_corpstats | None | Can view corp stats of members of their alliance. |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| corpstats.view_blue_corpstats | None | Can view corp stats of blue corporations. |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| corpstats.add_corpstats | Can create model | Can add new corpstats using an SSO token. |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| corpstats.change_corpstats | Can edit model | None. |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| corpstats.remove_corpstats | Can delete model | None. |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
|
||||
```
|
||||
|
||||
Typical use-cases would see the bundling of `corp_apis` and `view_corp_corpstats`, same for alliances and blues.
|
||||
Alliance permissions supersede corp permissions. Note that these evaluate against the user's main character.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Failure to create Corp Stats
|
||||
|
||||
>Unrecognized corporation. Please ensure it is a member of the alliance or a blue.
|
||||
|
||||
Corp Stats can only be created for corporations who have a model in the database. These only exist for tenant corps,
|
||||
corps of tenant alliances, blue corps, and members of blue alliances.
|
||||
|
||||
>Selected corp already has a statistics module.
|
||||
|
||||
Only one Corp Stats may exist at a time for a given corporation.
|
||||
|
||||
>Failed to gather corporation statistics with selected token.
|
||||
|
||||
During initial population, the EVE Swagger Interface did not return any member data. This aborts the creation process. Please wait for the API to start working before attempting to create again.
|
||||
|
||||
### Failure to update Corp Stats
|
||||
|
||||
Any of the following errors will result in a notification to the owning user, and deletion of the Corp Stats model.
|
||||
|
||||
>Your token has expired or is no longer valid. Please add a new one to create a new CorpStats.
|
||||
|
||||
This occurs when the SSO token is invalid, which can occur when deleted by the user, the character is transferred between accounts, or the API is having a bad day.
|
||||
|
||||
>CorpStats for corp_name cannot update with your ESI token as you have left corp.
|
||||
|
||||
The SSO token's character is no longer in the corp which the Corp Stats is for, and therefore membership data cannot be retrieved.
|
||||
|
||||
>HTTPForbidden
|
||||
|
||||
The SSO token lacks the required scopes to update membership data.
|
||||
84
docs/features/hrapplications.md
Normal file
84
docs/features/hrapplications.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# HR Applications
|
||||
|
||||
## Management
|
||||
|
||||
### Creating Forms
|
||||
|
||||
The most common task is creating ApplicationForm models for corps. Only when such models exist will a corp be listed as a choice for applicants. This occurs in the django admin site, so only staff have access.
|
||||
|
||||
The first step is to create questions. This is achieved by creating ApplicationQuestion models, one for each question. Titles are not unique.
|
||||
|
||||
Next step is to create the actual ApplicationForm model. It requires an existing EveCorporationInfo model to which it will belong. It also requires the selection of questions. ApplicationForm models are unique per corp: only one may exist for any given corp concurrently.
|
||||
|
||||
You can adjust these questions at any time. This is the preferred method of modifying the form: deleting and recreating will cascade the deletion to all received applications from this form which is usually not intended.
|
||||
|
||||
Once completed the corp will be available to receive applications.
|
||||
|
||||
### Reviewing Applications
|
||||
|
||||
Superusers can see all applications, while normal members with the required permission can view only those to their corp.
|
||||
|
||||
Selecting an application from the management screen will provide all the answers to the questions in the form at the time the user applied.
|
||||
|
||||
When a reviewer assigns themselves an application, they mark it as in progress. This notifies the applicant and permanently attached the reviewer to the application.
|
||||
|
||||
Only the assigned reviewer can approve/reject/delete the application if they possess the appropriate permission.
|
||||
|
||||
Any reviewer who can see the application can view the applicant's APIs if they possess the appropriate permission.
|
||||
|
||||
## Permissions
|
||||
|
||||
The following permissions have an effect on the website above and beyond their usual admin site functions.
|
||||
|
||||
```eval_rst
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| Permission | Admin Site | Auth Site |
|
||||
+=======================================+==================+====================================================+
|
||||
| auth.human_resources | None | Can view applications and mark in progress |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| hrapplications.approve_application | None | Can approve applications |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| hrapplications.delete_application | Can delete model | Can delete applications |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| hrapplications.reject_applications | None | Can reject applications |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| hrapplications.view_apis | None | Can view applicant API keys, and audit in Jacknife |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
| hrapplications.add_applicationcomment | Can create model | Can comment on applications |
|
||||
+---------------------------------------+------------------+----------------------------------------------------+
|
||||
|
||||
```
|
||||
|
||||
Best practice is to bundle the `auth.human_resources` permission alongside the `hrapplications.approve_application` and `hrapplications.reject_application` permissions, as in isolation these don't make much sense.
|
||||
|
||||
## Models
|
||||
|
||||
### ApplicationQuestion
|
||||
|
||||
This is the model representation of a question. It contains a title, and a field for optional "helper" text. It is referenced by ApplicationForm models but acts independently. Modifying the question after it has been created will not void responses, so it's not advisable to edit the title or the answers may not make sense to reviewers.
|
||||
|
||||
### ApplicationForm
|
||||
|
||||
This is the template for an application. It points at a corp, with only one form allowed per corp. It also points at ApplicationQuestion models. When a user creates an application, they will be prompted with each question the form includes at the given time. Modifying questions in a form after it has been created will not be reflected in existing applications, so it's perfectly fine to adjust them as you see fit. Changing corps however is not advisable, as existing applications will point at the wrong corp after they've been submitted, confusing reviewers.
|
||||
|
||||
### Application
|
||||
|
||||
This is the model representation of a completed application. It references an ApplicationForm from which it was spawned which is where the corp specificity comes from. It points at a user, contains info regarding its reviewer, and has a status. Shortcut properties also provide the applicant's main character, the applicant's APIs, and a string representation of the reviewer (for cases when the reviewer doesn't have a main character or the model gets deleted).
|
||||
|
||||
### ApplicationResponse
|
||||
|
||||
This is an answer to a question. It points at the Application to which it belongs, to the ApplicationQuestion which it is answering, and contains the answer text. Modifying any of these fields in dangerous.
|
||||
|
||||
### ApplicationComment
|
||||
|
||||
This is a reviewer's comment on an application. Points at the application, points to the user, and contains the comment text. Modifying any of these fields is dangerous.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No corps accepting applications
|
||||
|
||||
Ensure there are ApplicationForm models in the admin site. Ensure the user does not already have an application to these corps. If the users wishes to re-apply they must first delete their completed application
|
||||
|
||||
### Reviewer unable to complete application
|
||||
|
||||
Reviewers require a permission for each of the three possible outcomes of an application, Approve Reject or Delete. Any user with the human resources permission can mark an application as in-progress, but if they lack these permissions then the application will get stuck. Either grant the user the required permissions or change the assigned reviewer in the admin site. Best practice is to bundle the `auth.human_resources` permission alongside the `hrapplications.approve_application` and `hrapplications.reject_application` permissions, as in isolation these don't serve much purpose.
|
||||
10
docs/features/index.md
Normal file
10
docs/features/index.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Features
|
||||
|
||||
```eval_rst
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents
|
||||
|
||||
hrapplications
|
||||
corpstats
|
||||
```
|
||||
62
docs/index.md
Normal file
62
docs/index.md
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
# Alliance Auth
|
||||
|
||||
Alliance service auth to help large scale alliances manage services. Built for "The 99 Percent" open for anyone to use
|
||||
|
||||
# Installing
|
||||
|
||||
[Ubuntu Setup Guide](installation/auth/ubuntu.md)
|
||||
|
||||
For other distros, adapt the procedure and find distro-specific alternatives for the [dependencies](installation/auth/dependencies.md)
|
||||
|
||||
# Using
|
||||
|
||||
See the [Quick Start Guide](installation/auth/quickstart.md)
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
Read the [list of common problems.](maintenance/troubleshooting.md)
|
||||
|
||||
# Upgrading
|
||||
|
||||
As AllianceAuth is developed, new features are added while old bugs are repaired. It’s good practice to keep your instance of AllianceAuth up to date.
|
||||
|
||||
Some updates require specific instructions. Refer to their entry in the [changelog](maintenance/changelog.md)
|
||||
|
||||
In general, the update process has 4 steps:
|
||||
- download the latest code
|
||||
- generate new models in the database
|
||||
- update current models in the database
|
||||
- rebuild web cache
|
||||
|
||||
To perform each of these steps, you’ll need to be working from the console in the AllianceAuth directory. Usually `cd ~/allianceauth`
|
||||
|
||||
Start by pulling the code changes:
|
||||
|
||||
git pull
|
||||
|
||||
Modify settings.py according to the changelog.
|
||||
|
||||
For an automated upgrade, run the script:
|
||||
|
||||
bash update.sh
|
||||
|
||||
For a manual upgrade, execute the commands in this order:
|
||||
|
||||
sudo pip install -r requirements.txt
|
||||
|
||||
python manage.py migrate
|
||||
|
||||
python manage.py collectstatic
|
||||
|
||||
|
||||
```eval_rst
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:caption: Contents
|
||||
|
||||
features/index
|
||||
installation/index
|
||||
maintenance/index
|
||||
development/index
|
||||
```
|
||||
81
docs/installation/auth/apache.md
Normal file
81
docs/installation/auth/apache.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# Apache Setup
|
||||
### Overview
|
||||
|
||||
AllianceAuth gets served using a Web Server Gateway Interface (WSGI) script. This script passes web requests to AllianceAuth which generates the content to be displayed and returns it. This means very little has to be configured in Apache to host AllianceAuth.
|
||||
|
||||
In the interest of ~~laziness~~ time-efficiency, scroll down for example configs. Use these, changing the ServerName to your domain name.
|
||||
|
||||
### Required Parameters for AllianceAuth Core
|
||||
|
||||
The AllianceAuth core requires the following parameters to be set:
|
||||
|
||||
WSGIDaemonProcess
|
||||
WSGIProcessGroup
|
||||
WSGIScriptAlias
|
||||
|
||||
The following aliases are required:
|
||||
|
||||
Alias /static/ to point at the static folder
|
||||
Alias /templates/ to point at the templates folder
|
||||
|
||||
## Description of Parameters
|
||||
|
||||
- `WSGIDaemonProcess` is the name of the process/application. It also needs to be passed the python-path parameter directing python to search the AllianceAuth directory for modules to load.
|
||||
- `WSGIProcessGroup` is the group to run the process under. Typically the same as the name of the process/application.
|
||||
- `WSGIScriptAlias` points to the WSGI script.
|
||||
|
||||
## Additional Parameters for Full Setup
|
||||
|
||||
To pass additional services the following aliases and directories are required:
|
||||
|
||||
- `Alias /forums` to point at the forums folder
|
||||
- `Alias /killboard` to point at the killboard
|
||||
|
||||
Each of these require directory permissions allowing all connections.
|
||||
|
||||
For Apache 2.4 or greater:
|
||||
|
||||
<Directory "/path/to/alias/folder">
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
For Apache 2.3 or older:
|
||||
|
||||
<Directory "/path/to/alias/folder">
|
||||
Order Deny,Allow
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
## SSL
|
||||
|
||||
You can supply your own SSL certificates if you so desire. The alternative is running behind cloudflare for free SSL.
|
||||
|
||||
## Sample Config Files
|
||||
|
||||
### Own SSL Cert
|
||||
- Apache 2.4 or newer:
|
||||
- [000-default.conf](http://pastebin.com/3LLzyNmV)
|
||||
- [default-ssl.conf](http://pastebin.com/HUPPEp0R)
|
||||
- Apache 2.3 or older:
|
||||
- [000-default](http://pastebin.com/HfyKpQNu)
|
||||
- [default-ssl](http://pastebin.com/2WCS5jnb)
|
||||
|
||||
### No SSL Cloudflare, or LetsEncrypt
|
||||
- Apache 2.4 or newer:
|
||||
- [000-default.conf](http://pastebin.com/j1Ps3ZK6)
|
||||
- Apache 2.3 or older:
|
||||
- [000-default](http://pastebin.com/BHQzf2pj)
|
||||
|
||||
To have LetsEncrypt automatically install SSL certs, comment out the three lines starting with `WSGI`, install certificates, then uncomment them in `000-default-ls-ssl.conf`
|
||||
|
||||
## Enabling and Disabling Sites
|
||||
|
||||
To instruct apache to serve traffic from a virtual host, enable it:
|
||||
|
||||
sudo a2ensite NAME
|
||||
where NAME is the name of the configuration file (eg 000-default.conf)
|
||||
|
||||
To disable traffic from a site, disable the virtual host:
|
||||
|
||||
sudo a2dissite NAME
|
||||
where NAME is the name of the configuration file (eg 000-default.conf)
|
||||
84
docs/installation/auth/centos.md
Normal file
84
docs/installation/auth/centos.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# CentOS Installation
|
||||
|
||||
It's recommended to update all packages before proceeding.
|
||||
`sudo yum update`
|
||||
`sudo yum upgrade`
|
||||
`sudo reboot`
|
||||
|
||||
Now install all [dependencies](dependencies.md).
|
||||
|
||||
sudo yum install xxxxxxx
|
||||
|
||||
replacing the x's with the list of packages.
|
||||
|
||||
Make sure redis is running before continuing:
|
||||
|
||||
systemctl enable redis.service
|
||||
systemctl start redis.service
|
||||
|
||||
For security and permissions, it's highly recommended you create a user to install under who is not the root account.
|
||||
|
||||
sudo adduser allianceserver
|
||||
sudo passwd allianceserver
|
||||
|
||||
This user needs sudo powers. Add them by editing the sudoers file:
|
||||
|
||||
sudo nano /etc/sudoers
|
||||
|
||||
Find the line which says `root ALL=(ALL) ALL` - beneath it add another line `allianceserver ALL=(ALL) ALL` - now reboot.
|
||||
|
||||
**From this point on you need to be logged in as the allianceserver user**
|
||||
|
||||
Start your mariadb server `sudo systemctl start mariadb`
|
||||
|
||||
Secure your MYSQL / Maria-db server by typing `mysql_secure_installation `
|
||||
|
||||
AllianceAuth needs a MySQL user account. Create one as follows, replacing `PASSWORD` with an actual secure password:
|
||||
|
||||
mysql -u root -p
|
||||
CREATE USER 'allianceserver'@'localhost' IDENTIFIED BY 'PASSWORD';
|
||||
GRANT ALL PRIVILEGES ON * . * TO 'allianceserver'@'localhost';
|
||||
|
||||
Now we need to make the requisite databases.
|
||||
|
||||
create database alliance_auth;
|
||||
create database alliance_forum;
|
||||
create database alliance_jabber;
|
||||
create database alliance_mumble;
|
||||
create database alliance_killboard;
|
||||
|
||||
Ensure you are in the allianceserver home directory by issuing `cd`
|
||||
|
||||
Now we clone the source code:
|
||||
|
||||
git clone https://github.com/allianceauth/allianceauth.git
|
||||
|
||||
Enter the folder by issuing `cd allianceauth`
|
||||
|
||||
Ensure you're on the latest version with the following:
|
||||
|
||||
git tag | sort -n | tail -1 | xargs git checkout
|
||||
|
||||
Python package dependencies can be installed from the requirements file:
|
||||
|
||||
sudo pip install -r requirements.txt
|
||||
|
||||
The settings file needs configuring. See this lengthy guide for specifics.
|
||||
|
||||
Django needs to install models to the database before it can start.
|
||||
|
||||
python manage.py migrate
|
||||
|
||||
AllianceAuth needs to generate corp and alliance models before it can assign users to them.
|
||||
|
||||
python manage.py shell < run_alliance_corp_update.py
|
||||
|
||||
Now we need to round up all the static files required to render templates. Answer ‘yes’ when prompted.
|
||||
|
||||
python manage.py collectstatic
|
||||
|
||||
Test the server by starting it manually.
|
||||
|
||||
python manage.py runserver 0.0.0.0:8000
|
||||
|
||||
If you see an error, stop, read it, and resolve it. If the server comes up and you can access it at `yourip:8000`, you're golden. It's ok to stop the server if you're going to be installing apache.
|
||||
35
docs/installation/auth/cloudflare.md
Normal file
35
docs/installation/auth/cloudflare.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Cloudflare
|
||||
|
||||
CloudFlare offers free SSL and DDOS mitigation services. Why not take advantage of it?
|
||||
|
||||
## Setup
|
||||
|
||||
You’ll need to register an account on [CloudFlare’s site.](https://www.cloudflare.com/)
|
||||
|
||||
Along the top bar, select `Add Site`
|
||||
|
||||
Enter your domain name. It will scan records and let you know you can add the site. Continue setup.
|
||||
|
||||
On the next page you should see an A record for yourdomain.com pointing at your server IP. If not, manually add one:
|
||||
|
||||
A yourdomain.com my.server.ip.address Automatic TTL
|
||||
|
||||
Add the record and ensure the cloud under Status is orange. If not, click it. This ensures traffic gets screened by CloudFlare.
|
||||
|
||||
If you want forums or kb on a subdomain, and want these to be protected by CloudFlare, add an additional record for for each subdomain in the following format, ensuring the cloud is orange:
|
||||
|
||||
CNAME subdomain yourdomain.com Automatic TTL
|
||||
|
||||
CloudFlare blocks ports outside 80 and 443 on hosts it protects. This means, if the cloud is orange, only web traffic will get through. We need to reconfigure AllianceAuth to provide services under a subdomain. Configure these subdomains as above, but ensure the cloud is not orange (arrow should go around a grey cloud).
|
||||
|
||||
## Redirect to HTTPS
|
||||
|
||||
Now we need to configure the https redirect to force all traffic to https. Along the top bar of CloudFlare, select `Page Rules`. Add a new rule, Pattern is yourdomain.com, toggle the `Always use https` to ON, and save. It’ll take a few minutes to propagate.
|
||||
|
||||

|
||||
|
||||
## Update Auth URLs
|
||||
|
||||
Edit settings.py and replace everything that has a HTTP with HTTPS (except anything with a port on the end, like `OPENFIRE_ADDRESS`)
|
||||
|
||||
And there we have it. You’re DDOS-protected with free SSL.
|
||||
72
docs/installation/auth/dependencies.md
Normal file
72
docs/installation/auth/dependencies.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Dependencies
|
||||
|
||||
## Ubuntu
|
||||
|
||||
Tested on Ubuntu 12, 14, 15, and 16. Package names and repositories may vary.
|
||||
|
||||
### Core
|
||||
Required for base auth site
|
||||
|
||||
#### Python
|
||||
|
||||
python python-dev python-mysqldb python-setuptools python-mysql.connector python-pip
|
||||
|
||||
#### MySQL
|
||||
|
||||
mysql-server mysql-client libmysqlclient-dev
|
||||
|
||||
#### Utilities
|
||||
|
||||
screen unzip git redis-server curl libssl-dev libbz2-dev libffi-dev
|
||||
|
||||
### Apache
|
||||
Required for displaying web content
|
||||
|
||||
apache2 libapache2-mod-php5 libapache2-mod-wsgi
|
||||
|
||||
### PHP
|
||||
Required for phpBB, smf, evernus alliance market, etc
|
||||
|
||||
php5 php5-gd php5-mysqlnd php5-curl php5-gd php5-intl php-pear php5-imagick php5-imap php5-mcrypt php5-memcache php5-ming php5-ps php5-pspell php5-recode php5-snmp php5-sqlite php5-tidy php5-xmlrpc php5-xsl
|
||||
|
||||
### Java
|
||||
Required for hosting jabber server
|
||||
|
||||
oracle-java8-installer
|
||||
|
||||
## CentOS 7
|
||||
|
||||
### Add The EPEL Repository
|
||||
|
||||
yum --enablerepo=extras install epel-release
|
||||
yum update
|
||||
|
||||
### Core
|
||||
Required for base auth site
|
||||
|
||||
#### Python
|
||||
|
||||
python python-devel MySQL-python python-setuptools mysql-connector-python python-pip bzip2-devel
|
||||
|
||||
#### MySQL
|
||||
|
||||
mariadb-server mariadb-devel mariadb
|
||||
|
||||
#### Utilities
|
||||
|
||||
screen gcc gcc-c++ unzip git redis curl nano
|
||||
|
||||
### Apache
|
||||
Required for displaying web content
|
||||
|
||||
httpd mod_wsgi
|
||||
|
||||
### PHP
|
||||
Required for phpBB, smf, evernus alliance market, etc
|
||||
|
||||
php php-gd php-mysqlnd php-intl php-pear ImageMagick php-imap php-mcrypt php-memcache php-pspell php-recode php-snmp php-pdo php-tidy php-xmlrpc
|
||||
|
||||
### Java
|
||||
Required for hosting jabber server
|
||||
|
||||
java libstdc++.i686
|
||||
14
docs/installation/auth/index.md
Normal file
14
docs/installation/auth/index.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Auth
|
||||
|
||||
```eval_rst
|
||||
.. toctree::
|
||||
|
||||
dependencies
|
||||
ubuntu
|
||||
centos
|
||||
settings
|
||||
apache
|
||||
cloudflare
|
||||
supervisor
|
||||
quickstart
|
||||
```
|
||||
14
docs/installation/auth/quickstart.md
Normal file
14
docs/installation/auth/quickstart.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Quick Start
|
||||
|
||||
Once you’ve installed AllianceAuth, perform these steps to get yourself up and running.
|
||||
|
||||
First you need a superuser account. You can use this as a personal account. From the command line, `python manage.py createsuperuser` and follow the prompts.
|
||||
|
||||
The big goal of AllianceAuth is the automation of group membership, so we’ll need some groups. In the admin interface, select `Groups`, then at the top-right select `Add Group`. Give it a name and select permissions. Special characters (including spaces) are removing before syncing to services, so try not to have group names which will be the same upon cleaning. A description of permissions can be found in the [readme file](https://github.com/allianceauth/allianceauth/blob/master/README.md). Repeat for all the groups you see fit, whenever you need a new one.
|
||||
|
||||
### Background Processes
|
||||
|
||||
To start the background processes to sync groups and check api keys, issue these commands:
|
||||
|
||||
screen -dm bash -c 'python manage.py celeryd'
|
||||
screen -dm bash -c 'python manage.py celerybeat'
|
||||
422
docs/installation/auth/settings.md
Normal file
422
docs/installation/auth/settings.md
Normal file
@@ -0,0 +1,422 @@
|
||||
# Settings Overview
|
||||
|
||||
The `alliance_auth/settings.py` file is used to pass settings to the django app needed to run.
|
||||
|
||||
### Words of Warning
|
||||
|
||||
Certain fields are quite sensitive to leading `http://` and trailing `/` - if you see these present in the default text, be sure to include them in your values.
|
||||
|
||||
Every variable value is opened and closed with a single apostrophe `'` - please do not include these in your values or it will break things. If you absolutely must, replace them at the opening and closing of the value with double quotes `"`.
|
||||
|
||||
Certain variables are booleans, and come in a form that looks like this:
|
||||
|
||||
MEMBER_CORP_GROUPS = 'True' == os.environ.get('AA_MEMBER_CORP_GROUPS', 'True')
|
||||
|
||||
They're handled as strings because when settings are exported from shell commands (eg `export AA_MEMBER_CORP_GROUPS False`) they're interpreted as strings, so a string comparison is done.
|
||||
|
||||
When changing these booleans, edit the setting within the brackets (eg `('AA_MEMBER_CORP_GROUPS', 'True')` vs `('AA_MEMBER_CORP_GROUPS', 'False')`) and not the `True` earlier in the statement. Otherwise these will have unexpected behaviours.
|
||||
|
||||
# Fields to Modify
|
||||
|
||||
## Required
|
||||
- [SECRET_KEY](#secret_key)
|
||||
- Use [this tool](http://www.miniwebtool.com/django-secret-key-generator/) to generate a key on initial install
|
||||
- [DEBUG](#debug)
|
||||
- If issues are encountered, set this to `True` to view a more detailed error report, otherwise set `False`
|
||||
- [ALLOWED_HOSTS](#allowed_hosts)
|
||||
- This restricts web addresses auth will answer to. Separate with commas.
|
||||
- Should include localhost `127.0.0.1` and `yourdomain.com`
|
||||
- To allow from all, include `'*'`
|
||||
- [DATABASES](#databases)
|
||||
- Fill out the database name and user credentials to manage the auth database.
|
||||
- [DOMAIN](#domain)
|
||||
- Set to the domain name AllianceAuth will be accessible under
|
||||
- [EMAIL_HOST_USER](#email_host_user)
|
||||
- Username to send emails from. If gmail account, the full gmail address.
|
||||
- [EMAIL_HOST_PASSWORD](#email_host_password)
|
||||
- Password for the email user.
|
||||
- [CORP_IDS](#corp_ids)
|
||||
- List of corp IDs who are members. Exclude if their alliance is in `ALLIANCE_IDS`
|
||||
- [ALLIANCE_IDS](#alliance_ids)
|
||||
- List of alliance IDs who are members.
|
||||
- [ESI_SSO_CLIENT_ID](#esi_sso_client_id)
|
||||
- EVE application ID from the developers site. See the [SSO Configuration Instruction](#ESI_SSO_CLIENT_ID)
|
||||
- [ESI_SSO_CLIENT_SECRET](#esi_sso_client_secret)
|
||||
- EVE application secret from the developers site.
|
||||
- [ESI_SSO_CALLBACK_URL](#esi_sso_callback_url)
|
||||
- OAuth callback URL. Should be `https://mydomain.com/sso/callback`
|
||||
|
||||
## Services
|
||||
### Member Services
|
||||
After installing services, enable specific services for members by setting the following to `True`
|
||||
- [ENABLE_AUTH_FORUM](#enable_auth_forum)
|
||||
- [ENABLE_AUTH_JABBER](#enable_auth_jabber)
|
||||
- [ENABLE_AUTH_MUMBLE](#enable_auth_mumble)
|
||||
- [ENABLE_AUTH_IPBOARD](#enable_auth_ipboard)
|
||||
- [ENABLE_AUTH_TEAMSPEAK3](#enable_auth_teamspeak3)
|
||||
- [ENABLE_AUTH_DISCORD](#enable_auth_discord)
|
||||
- [ENABLE_AUTH_DISCOURSE](#enable_auth_discourse)
|
||||
- [ENABLE_AUTH_IPS4](#enable_auth_ips4)
|
||||
- [ENABLE_AUTH_SMF](#enable_auth_smf)
|
||||
- [ENABLE_AUTH_MARKET](#enable_auth_market)
|
||||
- [ENABLE_AUTH_XENFORO](#enable_auth_xenforo)
|
||||
|
||||
### Blue Services
|
||||
After installing services, enable specific services for blues by setting the following to `True`
|
||||
- [ENABLE_BLUE_FORUM](#enable_blue_forum)
|
||||
- [ENABLE_BLUE_JABBER](#enable_blue_jabber)
|
||||
- [ENABLE_BLUE_MUMBLE](#enable_blue_mumble)
|
||||
- [ENABLE_BLUE_IPBOARD](#enable_blue_ipboard)
|
||||
- [ENABLE_BLUE_TEAMSPEAK3](#enable_blue_teamspeak3)
|
||||
- [ENABLE_BLUE_DISCORD](#enable_blue_discord)
|
||||
- [ENABLE_BLUE_DISCOURSE](#enable_blue_discourse)
|
||||
- [ENABLE_BLUE_IPS4](#enable_blue_ips4)
|
||||
- [ENABLE_BLUE_SMF](#enable_blue_smf)
|
||||
- [ENABLE_BLUE_MARKET](#enable_blue_market)
|
||||
- [ENABLE_BLUE_XENFORO](#enable_blue_xenforo)
|
||||
|
||||
### IPBoard
|
||||
If using IPBoard, the following need to be set
|
||||
- [IPBOARD_ENDPOINT](#ipboard_endpoint)
|
||||
- [IPBOARD_APIKEY](#ipboard_apikey)
|
||||
- [IPBOARD_APIMODULE](#ipboard_apimodule)
|
||||
|
||||
### XenForo
|
||||
If using XenForo, the following need to be set
|
||||
- [XENFORO_ENDPOINT](#xenforo_endpoint)
|
||||
- [XENFORO_APIKEY](#xenforo_apikey)
|
||||
|
||||
### Openfire
|
||||
If using Openfire, the following need to be set
|
||||
- [JABBER_URL](#jabber_url)
|
||||
- [JABBER_PORT](#jabber_port)
|
||||
- [JABBER_SERVER](#jabber_server)
|
||||
- [OPENFIRE_ADDRESS](#openfire_address)
|
||||
- [OPENFIRE_SECRET_KEY](#openfire_secret_key)
|
||||
- [BROADCAST_USER](#broadcast_user)
|
||||
- [BROADCAST_USER_PASSWORD](#broadcast_user_password)
|
||||
- [BROADCAST_SERVICE_NAME](#broadcast_service_name)
|
||||
|
||||
### Mumble
|
||||
If using Mumble, the following need to be set
|
||||
- [MUMBLE_URL](#mumble_url)
|
||||
|
||||
### PHPBB3
|
||||
If using phpBB3, the database needs to be defined.
|
||||
|
||||
### Teamspeak3
|
||||
If using Teamspeak3, the following need to be set
|
||||
- [TEAMSPEAK3_SERVER_IP](#teamspeak3_server_ip)
|
||||
- [TEAMSPEAK3_SERVER_PORT](#teamspeak3_server_port)
|
||||
- [TEAMSPEAK3_SERVERQUERY_USER](#teamspeak3_serverquery_user)
|
||||
- [TEAMSPEAK3_SERVERQUERY_PASSWORD](#teamspeak3_serverquery_password)
|
||||
- [TEAMSPEAK3_VIRTUAL_SERVER](#teamspeak3_virtual_server)
|
||||
- [TEAMSPEAK3_PUBLIC_URL](#teamspeak3_public_url)
|
||||
|
||||
### Discord
|
||||
If connecting to a Discord server, set the following
|
||||
- [DISCORD_SERVER_ID](#discord_server_id)
|
||||
- [DISCORD_USER_EMAIL](#discord_user_email)
|
||||
- [DISCORD_USER_PASSWORD](#discord_user_password)
|
||||
|
||||
### Discourse
|
||||
If connecting to Discourse, set the following
|
||||
- [DISCOURSE_URL](#discourse_url)
|
||||
- [DISCOURSE_API_USERNAME](#discourse_api_username)
|
||||
- [DISCOURSE_API_KEY](#discourse_api_key)
|
||||
- [DISCOURSE_SSO_SECRET](#discourse_sso_secret)
|
||||
|
||||
### IPSuite4
|
||||
If using IPSuite4 (aka IPBoard4) the following are required:
|
||||
- [IPS4_URL](#ips4_url)
|
||||
- the database needs to be defined
|
||||
|
||||
### SMF
|
||||
If using SMF the following are required:
|
||||
- [SMF_URL](#smf_url)
|
||||
- the database needs to be defined
|
||||
|
||||
## Optional
|
||||
### Standings
|
||||
To allow access to blues, a corp API key is required to pull standings from. Corp does not need to be owning corp or in owning alliance. Required mask is 16 (Communications/ContactList)
|
||||
- [CORP_API_ID](#corp_api_id)
|
||||
- [CORP_API_VCODE](#corp_api_vcode)
|
||||
|
||||
### Jacknife
|
||||
To view APIs on a different Jacknife install, set [JACK_KNIFE_URL](#jack_knife_url)
|
||||
|
||||
### Auto Groups
|
||||
Groups can be automatically assigned based on a user's corp or alliance. Set the following to `True` to enable this feature.
|
||||
- [MEMBER_CORP_GROUPS](#member_corp_groups)
|
||||
- [MEMBER_ALLIANCE_GROUPS](#member_alliance_groups)
|
||||
- [BLUE_CORP_GROUPS](#blue_corp_groups)
|
||||
- [BLUE_ALLIANCE_GROUPS](#blue_alliance_groups)
|
||||
|
||||
### Fleet-Up
|
||||
Fittings and operations can be imported from Fleet-Up. Define the following to do so.
|
||||
- [FLEETUP_APP_KEY](#fleetup_app_key)
|
||||
- [FLEETUP_USER_ID](#fleetup_user_id)
|
||||
- [FLEETUP_API_ID](#fleetup_api_id)
|
||||
- [FLEETUP_GROUP_ID](#fleetup_group_id)
|
||||
|
||||
# Description of Settings
|
||||
## Django
|
||||
### SECRET_KEY
|
||||
A random string used in cryptographic functions, such as password hashing. Changing after installation will render all sessions and password reset tokens invalid.
|
||||
### DEBUG
|
||||
Replaces the generic `SERVER ERROR (500)` page when an error is encountered with a page containing a traceback and variables. May expose sensitive information so not recommended for production.
|
||||
### ALLOWED_HOSTS
|
||||
A list of addresses used to validate headers: AllianceAuth will block connection coming from any other address unless `DEBUG` is `True`. This should be a list of URLs and IPs to allow. For instance, include 'mydomain.com', 'www.mydomain.com', and the server's IP address to ensure connections will be accepted.
|
||||
### DATABASES
|
||||
List of databases available. Contains the Django database, and may include service ones if enabled. Service databases are defined in their individual sections and appended as needed automatically.
|
||||
### LANGUAGE_CODE
|
||||
Friendly name of the local language.
|
||||
### TIME_ZONE
|
||||
Friendly name of the local timezone.
|
||||
### STATIC_URL
|
||||
Absolute URL to serve static files from.
|
||||
### STATIC_ROOT
|
||||
Root folder to store static files in.
|
||||
### SUPERUSER_STATE_BYPASS
|
||||
Overrides superuser account states to always return True on membership tests. If issues are encountered, or you want to test access to certain portions of the site, set to False to disable.
|
||||
## EMAIL SETTINGS
|
||||
### DOMAIN
|
||||
The URL to which emails will link.
|
||||
### EMAIL_HOST
|
||||
The host address of the email server.
|
||||
### EMAIL_PORT
|
||||
The host port of the email server.
|
||||
### EMAIL_HOST_USER
|
||||
The username to authenticate as on the email server. For GMail, this is the full address.
|
||||
### EMAIL_HOST_PASSWORD
|
||||
The password of the user used to authenticate on the email server.
|
||||
### EMAIL_USE_TLS
|
||||
Enable TLS connections to the email server. Default is True.
|
||||
## Front Page Links
|
||||
### KILLBOARD_URL
|
||||
Link to a killboard.
|
||||
### EXTERNAL_MEDIA_URL
|
||||
Link to another media site, eg YouTube channel.
|
||||
### FORUM_URL
|
||||
Link to forums. Also used as the phpbb3 URL if enabled.
|
||||
### SITE_NAME
|
||||
Name to show in the top-left corner of auth.
|
||||
## SSO Settings
|
||||
An application will need to be created on the developers site. Please select `Authenticated API Access`, and choose all scopes starting with `esi`.
|
||||
### ESI_SSO_CLIENT_ID
|
||||
The application cliend ID generated from the [developers site.](https://developers.eveonline.com)
|
||||
### ESI_SSO_CLIENT_SECRET
|
||||
The application secret key generated from the [developers site.](https://developers.eveonline.com)
|
||||
### ESI_SSO_CALLBACK_URL
|
||||
The callback URL for authentication handshake. Should be `https://mydomain.com/sso/callback`.
|
||||
## Default Group Settings
|
||||
### DEFAULT_AUTH_GROUP
|
||||
Name of the group members of the owning corp or alliance are put in.
|
||||
### DEFAULT_BLUE_GROUP
|
||||
Name of the group blues of the owning corp or alliance are put in.
|
||||
### MEMBER_CORP_GROUPS
|
||||
If `True`, add members to groups with their corp name, prefixed with `Corp_`
|
||||
### MEMBER_ALLIANCE_GROUPS
|
||||
If `True`, add members to groups with their alliance name, prefixed with `Alliance_`
|
||||
### BLUE_CORP_GROUPS
|
||||
If `True`, add blues to groups with their corp name, prefixed with `Corp_`
|
||||
### BLUE_ALLIANCE_GROUPS
|
||||
If `True`, add blues to groups with their alliance name, prefixed with `Alliance_`
|
||||
## Alliance Service Setup
|
||||
### ENABLE_AUTH_FORUM
|
||||
Allow members of the owning corp or alliance to generate accounts on a Phpbb3 install.
|
||||
### ENABLE_AUTH_JABBER
|
||||
Allow members of the owning corp or alliance to generate accounts on an Openfire install.
|
||||
### ENABLE_AUTH_MUMBLE
|
||||
Allow members of the owning corp or alliance to generate accounts on a Mumble install.
|
||||
### ENABLE_AUTH_IPBOARD
|
||||
Allow members of the owning corp or alliance to generate accounts on an IPBoard install.
|
||||
### ENABLE_AUTH_TEAMSPEAK3
|
||||
Allow members of the owning corp or alliance to generate accounts on a Teamspeak3 install.
|
||||
### ENABLE_AUTH_DISCORD
|
||||
Allow members of the owning corp or alliance to link accounts to a Discord server.
|
||||
### ENABLE_AUTH_DISCOURSE
|
||||
Allow members of the owning corp or alliance to generate accounts on a Discourse install
|
||||
### ENABLE_AUTH_IPS4
|
||||
Allow members of the owning corp or alliance to generate accounts on a IPSuite4 install.
|
||||
### ENABLE_AUTH_SMF
|
||||
Allow members of the owning corp or alliance to generate accounts on a SMF install.
|
||||
### ENABLE_AUTH_MARKET
|
||||
Allow members of the owning corp or alliance to generate accounts on an alliance market install.
|
||||
### ENABLE_AUTH_XENFORO
|
||||
Allow members of the owning corp or alliance to generate accounts on a XenForo install.
|
||||
## Blue Service Setup
|
||||
### ENABLE_BLUE_FORUM
|
||||
Allow blues of the owning corp or alliance to generate accounts on a Phpbb3 install.
|
||||
### ENABLE_BLUE_JABBER
|
||||
Allow blues of the owning corp or alliance to generate accounts on an Openfire install.
|
||||
### ENABLE_BLUE_MUMBLE
|
||||
Allow blues of the owning corp or alliance to generate accounts on a Mumble install.
|
||||
### ENABLE_BLUE_IPBOARD
|
||||
Allow blues of the owning corp or alliance to generate accounts on an IPBoard install.
|
||||
### ENABLE_BLUE_TEAMSPEAK3
|
||||
Allow blues of the owning corp or alliance to generate accounts on a Teamspeak3 install.
|
||||
### ENABLE_BLUE_DISCORD
|
||||
Allow blues of the owning corp or alliance to link accounts to a Discord server.
|
||||
### ENABLE_BLUE_DISCOURSE
|
||||
Allow blues of the owning corp or alliance to generate accounts on a Discourse install.
|
||||
### ENABLE_BLUE_IPS4
|
||||
Allow blues of the owning corp or alliance to generate accounts on an IPSuite4 install.
|
||||
### ENABLE_BLUE_SMF
|
||||
Allow blues of the owning corp or alliance to generate accounts on a SMF install.
|
||||
### ENABLE_BLUE_MARKET
|
||||
Allow blues of the owning corp or alliance to generate accounts on an alliance market install.
|
||||
### ENABLE_BLUE_XENFORO
|
||||
Allow blues of the owning corp or alliance to generate accounts on a XenForo install.
|
||||
## Tenant Configuration
|
||||
Characters of any corp or alliance with their ID here will be treated as a member.
|
||||
### CORP_IDS
|
||||
EVE corp IDs of member corps. Separate with a comma.
|
||||
### ALLIANCE_IDS
|
||||
EVE alliance IDs of member alliances. Separate with a comma.
|
||||
## Standings Configuration
|
||||
To allow blues to access auth, standings must be pulled from a corp-level API. This API needs access mask 16 (ContactList).
|
||||
### CORP_API_ID
|
||||
The ID of an API key for a corp from which to pull standings, if desired. Needed for blues to gain access.
|
||||
### CORP_API_VCODE
|
||||
The verification code of an API key for a corp from which to pull standings, if desired. Needed for blues to gain access.
|
||||
### BLUE_STANDING
|
||||
The minimum standing value to consider blue. Default is 5.0
|
||||
### STANDING_LEVEL
|
||||
Standings from the API come at two levels: `corp` and `alliance`. Select which level to consider here.
|
||||
## API Configuration
|
||||
### MEMBER_API_MASK
|
||||
Required access mask for members' API keys to be considered valid.
|
||||
### MEMBER_API_ACCOUNT
|
||||
If `True`, require API keys from members to be account-wide, not character-restricted.
|
||||
### BLUE_API_MASK
|
||||
Required access mask for blues' API keys to be considered valid.
|
||||
### BLUE_API_ACCOUNT
|
||||
If `True`, require API keys from blues to be account-wide, not character-restricted.
|
||||
### REJECT_OLD_APIS
|
||||
Require each submitted API be newer than the latest submitted API. Protects against recycled or stolen API keys.
|
||||
### REJECT_OLD_APIS_MARGIN
|
||||
Allows newly submitted APIs to have their ID this value lower than the highest API ID on record and still be accepted. Default is 50, 0 is safest.
|
||||
## EVE Provider Settings
|
||||
Data about EVE objects (characters, corps, alliances) can come from two sources: the XML API or the EVE Swagger Interface.
|
||||
These settings define the default source.
|
||||
|
||||
For most situations, the EVE Swagger Interface is best. But if it goes down or experiences issues, these can be reverted to the XML API.
|
||||
|
||||
Accepted values are `esi` and `xml`.
|
||||
### EVEONLINE_CHARACTER_PROVIDER
|
||||
The default data source to get character information. Default is `esi`
|
||||
### EVEONLINE_CORP_PROVIDER
|
||||
The default data source to get corporation information. Default is `esi`
|
||||
### EVEONLINE_ALLIANCE_PROVIDER
|
||||
The default data source to get alliance information. Default is `esi`
|
||||
### EVEONLINE_ITEMTYPE_PROVIDER
|
||||
The default data source to get item type information. Default is `esi`
|
||||
## Alliance Market
|
||||
### MARKET_URL
|
||||
The web address to access the Evernus Alliance Market application.
|
||||
### MARKET_DB
|
||||
The Evernus Alliance Market database connection information.
|
||||
## HR Configuration
|
||||
### JACK_KNIFE_URL
|
||||
Link to an install of [eve-jackknife](https://code.google.com/archive/p/eve-jackknife/)
|
||||
## IPBoard3 Configuration
|
||||
### IPBOARD_ENDPOINT
|
||||
URL to the `index.php` file of a IPBoard install's API server.
|
||||
### IPBOARD_APIKEY
|
||||
API key for accessing an IPBoard install's API
|
||||
### IPBOARD_APIMODULE
|
||||
Module to access while using the API
|
||||
## XenForo Configuration
|
||||
### XENFORO_ENDPOINT
|
||||
The address of the XenForo API. Should look like `https://mydomain.com/forum/api.php`
|
||||
### XENFORO_DEFAULT_GROUP
|
||||
The group ID of the group to assign to member. Default is 0.
|
||||
### XENFORO_APIKEY
|
||||
The API key generated from XenForo to allow API access.
|
||||
## Jabber Configuration
|
||||
### JABBER_URL
|
||||
Address to instruct members to connect their jabber clients to, in order to reach an Openfire install. Usually just `mydomain.com`
|
||||
### JABBER_PORT
|
||||
Port to instruct members to connect their jabber clients to, in order to reach an Openfire install. Usually 5223.
|
||||
### JABBER_SERVER
|
||||
Server name of an Openfire install. Usually `mydomain.com`
|
||||
### OPENFIRE_ADDRESS
|
||||
URL of the admin web interface for an Openfire install. Usually `http://mydomain.com:9090`. If HTTPS is desired, change port to 9091: `https://mydomain.com:9091`
|
||||
### OPENFIRE_SECRET_KEY
|
||||
Secret key used to authenticate with an Openfire admin interface.
|
||||
### BROADCAST_USER
|
||||
Openfire user account used to send broadcasts from. Default is `Broadcast`.
|
||||
### BROADCAST_USER_PASSWORD
|
||||
Password to use when authenticating as the `BROADCAST_USER`
|
||||
### BROADCAST_SERVICE_NAME
|
||||
Name of the broadcast service running on an Openfire install. Usually `broadcast`
|
||||
## Mumble Configuration
|
||||
### MUMBLE_URL
|
||||
Address to instruct members to connect their Mumble clients to.
|
||||
### MUMBLE_SERVER_ID
|
||||
Depreciated. We're too scared to delete it.
|
||||
## Teamspeak3 Configuration
|
||||
### TEAMSPEAK3_SERVER_IP
|
||||
IP of a Teamspeak3 server on which to manage users. Usually `127.0.0.1`
|
||||
### TEAMSPEAK3_SERVER_PORT
|
||||
Port on which to connect to a Teamspeak3 server at the `TEAMSPEAK3_SERVER_IP`. Usually `10011`
|
||||
### TEAMSPEAK3_SERVERQUERY_USER
|
||||
User account with which to authenticate on a Teamspeak3 server. Usually `serveradmin`.
|
||||
### TEAMSPEAK3_SERVERQUERY_PASSWORD
|
||||
Password to use when authenticating as the `TEAMSPEAK3_SERVERQUERY_USER`. Provided during first startup or when you define a custom serverquery user.
|
||||
### TEAMSPEAK3_VIRTUAL_SERVER
|
||||
ID of the server on which to manage users. Usually `1`.
|
||||
### TEAMSPEAK3_PUBLIC_URL
|
||||
Address to instruct members to connect their Teamspeak3 clients to. Usually `mydomain.com`
|
||||
## Discord Configuration
|
||||
### DISCORD_GUILD_ID
|
||||
The ID of a Discord server on which to manage users.
|
||||
### DISCORD_BOT_TOKEN
|
||||
The bot token obtained from defining a bot on the [Discord developers site.](https://discordapp.com/developers/applications/me)
|
||||
### DISCORD_INVITE_CODE
|
||||
A no-limit invite code required to add users to the server. Must be generated from the Discord server itself (instant invite).
|
||||
### DISCORD_APP_ID
|
||||
The application ID obtained from defining an application on the [Discord developers site.](https://discordapp.com/developers/applications/me)
|
||||
### DISCORD_APP_SECRET
|
||||
The application secret key obtained from defining an application on the [Discord developers site.](https://discordapp.com/developers/applications/me)
|
||||
### DISCORD_CALLBACK_URL
|
||||
The callback URL used for authenticaiton flow. Should be `https://mydomain.com/discord_callback`. Must match exactly the one used when defining the application.
|
||||
### DISCORD_SYNC_NAMES
|
||||
Override usernames on the server to match the user's main character.
|
||||
## Discourse Configuration
|
||||
### DISCOURSE_URL
|
||||
The web address of the Discourse server to direct users to.
|
||||
### DISCOURSE_API_USERNAME
|
||||
Username of the account which generated the API key on Discourse.
|
||||
### DISCOURSE_API_KEY
|
||||
API key defined on Discourse.
|
||||
### DISCOURSE_SSO_SECRET
|
||||
The SSO secret key defined on Discourse.
|
||||
## IPS4 Configuration
|
||||
### IPS4_URL
|
||||
URL of the IPSuite4 install to direct users to.
|
||||
### IPS4_API_KEY
|
||||
Depreciated. We're too scared to delete it.
|
||||
### IPS4_DB
|
||||
The database connection to manage users on.
|
||||
## SMF Configuration
|
||||
### SMF_URL
|
||||
URL of the SMF install to direct users to.
|
||||
### SMF_DB
|
||||
The database connection to manage users on.
|
||||
## Fleet-Up Configuration
|
||||
### FLEETUP_APP_KEY
|
||||
Application key as [defined on Fleet-Up.](http://fleet-up.com/Api/MyApps)
|
||||
### FLEETUP_USER_ID
|
||||
API user ID as [defined on Fleet-Up.](http://fleet-up.com/Api/MyKeys)
|
||||
### FLEETUP_API_ID
|
||||
API ID as [defined on Fleet-Up.](http://fleet-up.com/Api/MyKeys)
|
||||
### FLEETUP_GROUP_ID
|
||||
The group ID from which to pull data. Can be [retrieved from Fleet-Up](http://fleet-up.com/Api/Endpoints#groups_mygroupmemberships)
|
||||
## Logging Configuration
|
||||
This section is used to manage how logging messages are processed.
|
||||
|
||||
To turn off logging notifications, change the `handlers` `notifications` `class` to `logging.NullHandler`
|
||||
|
||||
## Everything below logging is magic. Do Not Touch
|
||||
63
docs/installation/auth/supervisor.md
Normal file
63
docs/installation/auth/supervisor.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Supervisor
|
||||
|
||||
>Supervisor is a client/server system that allows its users to control a number of processes on UNIX-like operating systems.
|
||||
|
||||
What that means is supervisor will take care of ensuring the celery workers are running (and mumble authenticator) and start the automatically on reboot. Handy, eh?
|
||||
|
||||
## Installation
|
||||
|
||||
Most OSes have a supervisor package available in their distribution.
|
||||
|
||||
Ubuntu:
|
||||
|
||||
sudo apt-get install supervisor
|
||||
|
||||
CentOS:
|
||||
|
||||
sudo yum install supervisor
|
||||
sudo systemctl enable supervisor.service
|
||||
sudo systemctl start supervisor.service
|
||||
|
||||
## Configuration
|
||||
|
||||
Auth provides example config files for the celery workers (celeryd), the periodic task scheduler (celerybeat), and the mumble authenticator. All of these are available in `thirdparty/Supervisor`.
|
||||
|
||||
For most users, all you have to do is copy the config files to `/etc/supervisor/conf.d` then restart the service. Copy `auth-celerybeat.conf` and `auth-celeryd.conf` for the celery workers, and `auth-mumble.conf` for the mumble authenticator. For all three just use a wildcard:
|
||||
|
||||
sudo cp thirdparty/Supervisor/* /etc/supervisor/conf.d
|
||||
|
||||
Ubuntu:
|
||||
|
||||
sudo service supervisor restart
|
||||
|
||||
CentOS:
|
||||
|
||||
sudo systemctl restart supervisor.service
|
||||
|
||||
## Checking Status
|
||||
|
||||
To ensure the processes are working, check their status:
|
||||
|
||||
sudo supervisorctl status
|
||||
|
||||
Processes will be `STARTING`, `RUNNING`, or `ERROR`. If an error has occurred, check their log files:
|
||||
- celeryd: `log/worker.log`
|
||||
- celerybeat: `log/beat.log`
|
||||
- authenticator: `log/authenticator.log`
|
||||
|
||||
## Customizing Config Files
|
||||
|
||||
The only real customization needed is if running in a virtual environment. The python path will have to be changed in order to start in the venv.
|
||||
|
||||
Edit the config files and find the line saying `command`. Replace `python` with `/path/to/venv/python`. This can be relative to the `directory` specified in the config file.
|
||||
|
||||
Note that for config changes to be loaded, the supervisor service must be restarted.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### auth-celerybeat fails to start
|
||||
Most often this is caused by a permissions issue on the allianceauth directory (the error will talk about `celerybeat.pid`). The easiest fix is to edit its config file and change the `user` from `allianceserver` to `root`.
|
||||
|
||||
### Workers are using old settings
|
||||
|
||||
Every time the codebase is updated or settings file changed, workers will have to be restarted. Easiest way is to restart the supervisor service (see configuration above for commands)
|
||||
75
docs/installation/auth/ubuntu.md
Normal file
75
docs/installation/auth/ubuntu.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Ubuntu Installation
|
||||
|
||||
It’s recommended to update all packages before proceeding.
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade
|
||||
sudo reboot
|
||||
|
||||
Now install all [dependencies](dependencies.md). For this guide you'll need the optional [Apache section](dependencies.md) as well.
|
||||
|
||||
sudo apt-get install xxxxxxx
|
||||
replacing the xs with the list of packages.
|
||||
|
||||
For security and permissions, it’s highly recommended you create a user to install under who is not the root account.
|
||||
|
||||
sudo adduser allianceserver
|
||||
|
||||
This user needs sudo powers. Add them by editing the sudoers file:
|
||||
|
||||
sudo nano /etc/sudoers
|
||||
|
||||
Find the line which says `root ALL=(ALL:ALL) ALL` - beneath it add another line `allianceserver ALL=(ALL:ALL) ALL` - now reboot.
|
||||
|
||||
**From this point on you need to be logged in as the allianceserver user**
|
||||
|
||||
AllianceAuth needs a MySQL user account. Create one as follows, replacing `PASSWORD` with an actual secure password:
|
||||
|
||||
mysql -u root -p
|
||||
CREATE USER 'allianceserver'@'localhost' IDENTIFIED BY 'PASSWORD';
|
||||
GRANT ALL PRIVILEGES ON * . * TO 'allianceserver'@'localhost';
|
||||
|
||||
Now we need to make the requisite databases.
|
||||
|
||||
create database alliance_auth;
|
||||
create database alliance_forum;
|
||||
create database alliance_jabber;
|
||||
create database alliance_mumble;
|
||||
|
||||
Ensure you are in the allianceserver home directory by issuing `cd`
|
||||
|
||||
Now we clone the source code:
|
||||
|
||||
git clone https://github.com/allianceauth/allianceauth.git
|
||||
|
||||
Enter the folder by issuing `cd allianceauth`
|
||||
|
||||
Ensure you're on the latest version with the following:
|
||||
|
||||
git tag | sort -n | tail -1 | xargs git checkout
|
||||
|
||||
Python package dependencies can be installed from the requirements file:
|
||||
|
||||
sudo pip install -r requirements.txt
|
||||
|
||||
The settings file needs configuring. See [this lengthy guide](settings.md) for specifics.
|
||||
|
||||
Django needs to install models to the database before it can start.
|
||||
|
||||
python manage.py migrate
|
||||
|
||||
AllianceAuth needs to generate corp and alliance models before it can assign users to them.
|
||||
|
||||
python manage.py shell < run_alliance_corp_update.py
|
||||
|
||||
Now we need to round up all the static files required to render templates. Answer ‘yes’ when prompted.
|
||||
|
||||
python manage.py collectstatic
|
||||
|
||||
Test the server by starting it manually.
|
||||
|
||||
python manage.py runserver 0.0.0.0:8000
|
||||
|
||||
If you see an error, stop, read it, and resolve it. If the server comes up and you can access it at `yourip:8000`, you're golden. It's ok to stop the server if you're going to be installing apache.
|
||||
|
||||
Once installed, follow the [Quick Start Guide](quickstart.md)
|
||||
10
docs/installation/index.md
Normal file
10
docs/installation/index.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Installation
|
||||
|
||||
```eval_rst
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
auth/index
|
||||
services/index
|
||||
|
||||
```
|
||||
54
docs/installation/services/discord.md
Normal file
54
docs/installation/services/discord.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Discord
|
||||
## Overview
|
||||
Discord is a web-based instant messaging client with voice. Kind of like teamspeak meets slack meets skype. It also has a standalone app for phones and desktop.
|
||||
|
||||
## Setup
|
||||
### Creating a Server
|
||||
*If you already have a Discord server, skip the creation step, but be sure to retrieve the server ID and enter it in settings.py*
|
||||
|
||||
Navigate to the [Discord site](https://discordapp.com/) and register an account, or log in if you have one already.
|
||||
|
||||
On the left side of the screen you’ll see a circle with a plus sign. This is the button to create a new server. Go ahead and do that, naming it something obvious.
|
||||
|
||||
Now retrieve the server ID from the URL of the page you’re on. The ID is the first of the very long numbers. For instance my testing server’s url look like:
|
||||
|
||||
https://discordapp.com/channels/120631096835571712/120631096835571712
|
||||
|
||||
with a server ID of `120631096835571712`
|
||||
|
||||
Update settings.py, inputting the server ID as `DISCORD_GUILD_ID`
|
||||
|
||||
### Generating an Invite
|
||||
Still on the Discord site, in your new server, an invite needs to be generated for users to join. If you with for users to initially join a different channel than `#general`, create it and follow the steps below, substituting this channel for `#general`.
|
||||
|
||||
On the left bar under the Text Channels heading, hover over `#general` on the right site. There are two icons, a box with an arrow and a gear. Press the box, then on the bottom left select Advanced Settings. Set the expiration to never, and no limit on uses. Press generate.
|
||||
|
||||
This returns a code that looks like `https://discord.gg/0fmA8MyXV6qt7XAZ`. The part after the last slash, `0fmA8MyXV6qt7XAZ`, is the invite code. Update settings.py, inputting this invite code as `DISCORD_INVITE_CODE`
|
||||
|
||||
### Registering an Application
|
||||
|
||||
Navigate to the [Discord Developers site.](https://discordapp.com/developers/applications/me) Press the plus sign to create a new application.
|
||||
|
||||
Give it a name and description relating to your auth site. Add a redirect to `https://mydomain.com/discord_callback`, substituting your domain. Press Create Application.
|
||||
|
||||
Update settings.py, inputting this redirect address as `DISCORD_CALLBACK_URL`
|
||||
|
||||
On the application summary page, press Create a Bot User.
|
||||
|
||||
Update settings.py with these pieces of information from the summary page:
|
||||
- From the App Details panel, `DISCORD_APP_ID` is the Client/Application ID
|
||||
- From the App Details panel, `DISCORD_APP_SECRET` is the Secret
|
||||
- From the App Bot Users panel, `DISCORD_BOT_TOKEN` is the Token
|
||||
|
||||
### Adding a Bot to the Server
|
||||
Once created, navigate to the services page of your AllianceAuth install as the superuser account. At the top there is a big green button labelled Link Discord Server. Click it, then from the drop down select the server you created, and then Authorize.
|
||||
|
||||
This adds a new user to your Discord server with a `BOT` tag, and a new role with the same name as your Discord application. Don't touch either of these. If for some reason the bot loses permissions or is removed from the server, click this button again.
|
||||
|
||||
To manage roles, this bot role must be at the top of the hierarchy. Edit your Discord server, roles, and click and drag the role with the same name as your application to the top of the list. This role must stay at the top of the list for the bot to work. Finally, the owner of the bot account must enable 2 Factor Authentication (this is required from discord for kicking and modifying member roles). If you are unsure what 2FA is or how to set it up, refer to [this support page](https://support.discordapp.com/hc/en-us/articles/219576828). It is also recommended to force 2fa on your server (this forces any admins or moderators to have 2fa enabled to perform similar functions on discord).
|
||||
|
||||
### Linking Accounts
|
||||
Instead of the usual account creation procedure, for Discord to work we need to link accounts to AllianceAuth. When attempting to enable the Discord service, users are redirected to the official Discord site to authenticate. They will need to create an account if they don't have one prior to continuing. Upon authorization, users are redirected back to AllianceAuth with an OAuth code which is used to join the Discord server.
|
||||
|
||||
## Managing Roles
|
||||
Once users link their accounts you’ll notice Roles get populated on Discord. These are the equivalent to Groups on every other service. The default permissions should be enough for members to chat and use comms. Add more permissions to the roles as desired through the server management window.
|
||||
122
docs/installation/services/discourse.md
Normal file
122
docs/installation/services/discourse.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Discourse
|
||||
## Install Docker
|
||||
|
||||
wget -qO- https://get.docker.io/ | sh
|
||||
|
||||
### Get docker permissions
|
||||
|
||||
sudo usermod -aG docker allianceserver
|
||||
|
||||
Logout, then back in for changes to take effect.
|
||||
|
||||
## Install Discourse
|
||||
|
||||
### Download Discourse
|
||||
|
||||
sudo mkdir /var/discourse
|
||||
sudo git clone https://github.com/discourse/discourse_docker.git /var/discourse
|
||||
|
||||
### Configure
|
||||
|
||||
cd /var/discourse
|
||||
sudo cp samples/standalone.yml containers/app.yml
|
||||
sudo nano containers/app.yml
|
||||
|
||||
Change the following:
|
||||
- `DISCOURSE_DEVELOPER_EMAILS` should be a list of admin account email addresses separated by commas
|
||||
- `DISCOUSE_HOSTNAME` should be 127.0.0.1
|
||||
- Everything with `SMTP` depends on your mail settings. Account created through auth do not require email validation, so to ignore everything email (NOT RECOMMENDED), just change the SMTP address to something random so it'll install. Note that not setting up email means any password resets emails won't be sent, and auth cannot reset these. [There are plenty of free email services online recommended by Discourse.](https://github.com/discourse/discourse/blob/master/docs/INSTALL-email.md#recommended-email-providers-for-discourse)
|
||||
|
||||
To install behind apache, look for this secion:
|
||||
|
||||
...
|
||||
## which TCP/IP ports should this container expose?
|
||||
expose:
|
||||
- "80:80" # fwd host port 80 to container port 80 (http)
|
||||
...
|
||||
|
||||
Change it to this:
|
||||
|
||||
...
|
||||
## which TCP/IP ports should this container expose?
|
||||
expose:
|
||||
- "7890:80" # fwd host port 7890 to container port 80 (http)
|
||||
...
|
||||
|
||||
Or any other port will do, if taken. Remember this number.
|
||||
|
||||
### Build and launch
|
||||
|
||||
sudo nano /etc/default/docker
|
||||
|
||||
Uncomment this line:
|
||||
|
||||
DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4"
|
||||
|
||||
Restart docker:
|
||||
|
||||
sudo service docker restart
|
||||
|
||||
Now build:
|
||||
|
||||
sudo ./launcher bootstrap app
|
||||
sudo ./launcher start app
|
||||
|
||||
## Apache config
|
||||
|
||||
Discourse must run on its own subdomain - it can't handle routing behind an alias like '/forums'. To do so, make a new apache config:
|
||||
|
||||
sudo nano /etc/apache2/sites-available/discourse.conf
|
||||
|
||||
And enter the following, changing the port if you used a different number:
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName discourse.mydomain.com
|
||||
ProxyPass / http://0.0.0.0:7890/
|
||||
ProxyPassReverse / http://0.0.0.0:7890/
|
||||
</VirtualHost>
|
||||
|
||||
Now enable proxies and restart apache:
|
||||
|
||||
sudo a2enmod proxy_http
|
||||
sudo service apache2 reload
|
||||
|
||||
## Configure API
|
||||
|
||||
### Generate admin account
|
||||
|
||||
From the /var/discourse folder,
|
||||
|
||||
./launcher enter app
|
||||
rake admin:create
|
||||
|
||||
Follow prompts, being sure to answer `y` when asked to allow admin privileges.
|
||||
|
||||
### Create API key
|
||||
|
||||
Navigate to `discourse.mydomain.com` and log on. Top right press the 3 lines and select `Admin`. Go to API tab and press `Generate Master API Key`.
|
||||
|
||||
Now go to the allianceauth folder and edit settings:
|
||||
|
||||
nano /home/allianceserver/allianceauth/alliance_auth/settings.py
|
||||
|
||||
Scroll down to the Discourse section and set the following:
|
||||
- `DISCOURSE_URL`: `discourse.mydomain.com`
|
||||
- `DISCOURSE_API_USERNAME`: the username of the admin account you generated the API key with
|
||||
- `DISCOURSE_API_KEY`: the key you just generated
|
||||
|
||||
### Configure SSO
|
||||
|
||||
Navigate to `discourse.mydomain.com` and log in. Back to the admin site, scroll down to find SSO settings and set the following:
|
||||
- `enable_sso`: True
|
||||
- `sso_url`: `http://mydomain.com/discourse_sso`
|
||||
- `sso_secret`: some secure key
|
||||
|
||||
Save, now change settings.py and add the following:
|
||||
- `DISCOURSE_SSO_SECRET`: the secure key you just set
|
||||
|
||||
### Enable for your members
|
||||
|
||||
Set either or both of `ENABLE_AUTH_DISCOURSE` and `ENABLE_BLUE_DISCOURSE` in settings.py for your members to gain access. Save and exit with control+o, enter, control+x.
|
||||
|
||||
## Done
|
||||
19
docs/installation/services/index.md
Normal file
19
docs/installation/services/index.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Services
|
||||
|
||||
```eval_rst
|
||||
.. toctree::
|
||||
|
||||
market
|
||||
discord
|
||||
discourse
|
||||
ipboard3
|
||||
mumble
|
||||
openfire
|
||||
phpbb3
|
||||
smf
|
||||
teamspeak3
|
||||
xenforo
|
||||
jacknife
|
||||
pathfinder
|
||||
|
||||
```
|
||||
40
docs/installation/services/ipboard3.md
Normal file
40
docs/installation/services/ipboard3.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# IPBoard3
|
||||
|
||||
You’re on your own for the initial install of IPBoard. It’s pretty much just download, unzip, and move to `/var/www/ipboard/`. Make sure to
|
||||
|
||||
sudo chown -R www-data:www-data /var/www/ipboard
|
||||
|
||||
a few times because it’s pretty finicky.
|
||||
|
||||
You’ll need to add another alias in your apache config, this one for `/ipboard` pointing to `/var/www/ipboard` and add another `<directory>` block for `/var/www/ipboard` with `Require all granted` or `Allow from all` depending on your apache version.
|
||||
|
||||
IPBoard needs a database table. Log in to mysql and run:
|
||||
|
||||
create database alliance_ipboard;
|
||||
|
||||
That’s all for SQL work. Control+D to close.
|
||||
|
||||
Navigate to http://yourdomain.com/ipboard and proceed with the install. If it whines about permissions make sure to `chown` again. Point it at that database we just made, using the `allianceserver` MySQL user account from the full install.
|
||||
|
||||
Once you get everything installed we need to copy the api module folder
|
||||
|
||||
sudo cp -a /home/allianceserver/allianceauth/thirdparty/IPBoard3/aa /var/www/ipboard/interface/board/modules/aa
|
||||
|
||||
and again run that `chown` command.
|
||||
|
||||
Log into the AdminCP for IPBoard and find your way to the `System` tab. On the left navigation bar, under `Tools and Settings`, select `API Users`.
|
||||
|
||||
Enable the API by toggling the `XML-RPC Status` from `disabled` to `enabled` (red box, top right of the page) and save. Now create a new api user. Put something descriptive for title such as ‘AllianceAuth’, then on the bottom panel click the `AllianceAuth` tab and tick all the boxes. Press `Create New API User` to save it.
|
||||
|
||||
Copy the API key. Now edit your settings.py as follows:
|
||||
|
||||
- IPBOARD_APIKEY is the key you just copied
|
||||
- IPBOARD_ENDPOINT is `http://yourdomain.com/ipboard/interface/board/index.php`
|
||||
|
||||
Now enable IPBoard for Auth and/or Blue by editing the auth settings.
|
||||
|
||||
Save and exit. Restart apache or gunicorn.
|
||||
|
||||
Test it by creating a user through Alliance Auth. Just note right now there’s no real error handling, so if account creation fails it’ll still return a username/password combo.
|
||||
|
||||
Good luck!
|
||||
71
docs/installation/services/jacknife.md
Normal file
71
docs/installation/services/jacknife.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Eve Jacknife
|
||||
|
||||
## Overview
|
||||
Eve Jacknife is used to audit an api so that you might see character skills and what ships they can fly, mails, contracts,assets, and any other given access from a specific api key.
|
||||
|
||||
## Dependencies
|
||||
All php and mysql dependencies should have been taken care of during setup.
|
||||
|
||||
## Installation
|
||||
### Get Code
|
||||
Navigate to your server's web directory: `cd /var/www`
|
||||
|
||||
Download the code: `sudo git clone https://github.com/whinis/eve-jacknife`
|
||||
|
||||
### Create Database
|
||||
|
||||
mysql -u root -p -e "create database jackknife; grant all privileges on jackknife.* to 'allianceserver'@'localhost';"
|
||||
|
||||
### Configure Settings
|
||||
|
||||
Change directory to jacknife: `cd eve-jacknife`
|
||||
|
||||
Now copy the template: `sudo cp base.config.php eve.config.php`
|
||||
|
||||
And now edit: `sudo nano eve.config.php`
|
||||
|
||||
Add the database user information:
|
||||
- `$sql_u = "allianceserver"`
|
||||
- `$sql_p = "MY_SQL_PASSWORD_HERE"`
|
||||
|
||||
## Apache Configuration
|
||||
|
||||
Change ownership of the directory: `sudo chown -R www-data:www-data ../eve-jacknife`
|
||||
|
||||
Eve Jacknife can be served two ways: on its own subdomain (`jacknife.mydomain.com`) or as an alias (`mydomain.com/jacknife`)
|
||||
|
||||
### Subdomain
|
||||
As its own subdomain, create a new apache config: `sudo nano /etc/apache2/sites-available/jacknife.conf` and enter the following:
|
||||
|
||||
<VirtualHost *:80>
|
||||
DocumentRoot "/var/www/eve-jacknife"
|
||||
ServerName jacknife.mydomain.com
|
||||
<Directory "/var/www/eve-jacknife">
|
||||
Require all granted
|
||||
AllowOverride all
|
||||
DirectoryIndex index.php
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
Enable the new site with `sudo a2ensite jacknife.conf` and then reload apache with `sudo service apache2 reload`
|
||||
|
||||
### Alias
|
||||
As an alias, edit your site config (usually 000-default): `sudo nano etc/apache2/sites-available/000-default.conf` and add the following inside the `VirtualHost` block:
|
||||
|
||||
Alias /jacknife "/var/www/eve-jacknife/"
|
||||
<Directory "/var/www/eve-jacknife">
|
||||
Require all granted
|
||||
DirectoryIndex index.php
|
||||
</Directory>
|
||||
|
||||
Reload apache to take effect: `sudo service apache2 reload`
|
||||
|
||||
## Install SQL
|
||||
|
||||
Once apache is configured, Eve Jacknife needs to install some data. Navigate to it in your browser and append `/Installer.php` to the URL.
|
||||
|
||||
Enter your database password and press Check. If all the boxes come back green press Save. On the next screen press Install and wait for it to finish.
|
||||
|
||||
## Update Auth Settings
|
||||
|
||||
Edit your aut settings file (`nano ~/allianceauth/alliance_auth/settings.py`) and replace `JACK_KNIFE_URL` with either `jacknife.mydomain.com/` or `mydomain.com/jacknife/` depending on your apache choice.
|
||||
104
docs/installation/services/market.md
Normal file
104
docs/installation/services/market.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Alliance Market
|
||||
|
||||
Alliance Market needs a database. Create one in mysql. Default name is `alliance_market`:
|
||||
|
||||
mysql -u root -p
|
||||
create database alliance_market;
|
||||
exit;
|
||||
|
||||
To clone the repo, install packages:
|
||||
|
||||
sudo apt-get install mercurial meld
|
||||
|
||||
Change to the web folder:
|
||||
|
||||
cd /var/www
|
||||
|
||||
Now clone the repo
|
||||
|
||||
sudo hg clone https://bitbucket.org/krojew/evernus-alliance-market
|
||||
|
||||
Make cache and log directories
|
||||
|
||||
sudo mkdir evernus-alliance-market/app/cache
|
||||
sudo mkdir evernus-alliance-market/app/logs
|
||||
sudo chmod -R 777 evernus-alliance-market/app/cache
|
||||
sudo chmod -R 777 evernus-alliance-market/app/logs
|
||||
|
||||
Change ownership to apache
|
||||
|
||||
sudo chown -R www-data:www-data evernus-alliance-market
|
||||
|
||||
Enter
|
||||
|
||||
cd evernus-alliance-market
|
||||
|
||||
Set environment variable
|
||||
|
||||
export SYMFONY_ENV=prod
|
||||
|
||||
Copy configuration
|
||||
|
||||
sudo cp app/config/parameters.yml.dist app/config/parameters.yml
|
||||
|
||||
Edit, changing the following:
|
||||
- `database_name` to `alliance_market`
|
||||
- `database_user` to your MySQL user (usually `allianceserver`)
|
||||
- `database_password` to your MySQL user password
|
||||
- email settings, eg gmail
|
||||
|
||||
Edit `app/config/config.yml` and add the following:
|
||||
|
||||
services:
|
||||
fos_user.doctrine_registry:
|
||||
alias: doctrine
|
||||
|
||||
Install composer [as per these instructions.](https://getcomposer.org/download/)
|
||||
|
||||
Update dependencies.
|
||||
|
||||
sudo php composer.phar update --optimize-autoloader
|
||||
|
||||
Prepare the cache:
|
||||
|
||||
sudo php app/console cache:clear --env=prod --no-debug
|
||||
|
||||
|
||||
Dump assets:
|
||||
|
||||
sudo php app/console assetic:dump --env=prod --no-debug
|
||||
|
||||
|
||||
Create DB entries
|
||||
|
||||
sudo php app/console doctrine:schema:update --force
|
||||
|
||||
Install SDE:
|
||||
|
||||
sudo php app/console evernus:update:sde
|
||||
|
||||
Edit your apache config. Add the following:
|
||||
|
||||
Alias /market /var/www/evernus-alliance-market/web/
|
||||
|
||||
<Directory "/var/www/evernus-alliance-market/web/">
|
||||
DirectoryIndex app.php
|
||||
Require all granted
|
||||
AllowOverride all
|
||||
</Directory>
|
||||
|
||||
Enable rewriting
|
||||
|
||||
sudo a2enmod rewrite
|
||||
|
||||
Restart apache
|
||||
|
||||
sudo service apache2 reload
|
||||
|
||||
Once again, set cache permissions:
|
||||
|
||||
sudo chown -R www-data:www-data app/
|
||||
|
||||
Add a user account through auth, then make it a superuser:
|
||||
|
||||
sudo php app/console fos:user:promote your_username --super
|
||||
81
docs/installation/services/mumble.md
Normal file
81
docs/installation/services/mumble.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# Mumble
|
||||
## Overview
|
||||
Mumble is a free voice chat server. While not as flashy as teamspeak, it has all the functionality and is easier to customize. And is better. I may be slightly biased.
|
||||
|
||||
## Dependencies
|
||||
The mumble server package can be retrieved from a repository we need to add, mumble/release.
|
||||
|
||||
sudo apt-add-repository ppa:mumble/release
|
||||
sudo apt-get update
|
||||
|
||||
Now two packages need to be installed:
|
||||
|
||||
sudo apt-get install python-software-properties mumble-server
|
||||
|
||||
## Configuring Mumble
|
||||
Mumble ships with a configuration file that needs customization. By default it’s located at /etc/mumble-server.ini. Open it with your favourite text editor:
|
||||
|
||||
sudo nano /etc/mumble-server.ini
|
||||
|
||||
REQUIRED: To enable the ICE authenticator, edit the following:
|
||||
|
||||
- `icesecretwrite=MY_CLEVER_PASSWORD`, obviously choosing a secure password
|
||||
|
||||
By default mumble operates on sqlite which is fine, but slower than a dedicated MySQL server. To customize the database, edit the following:
|
||||
|
||||
- uncomment the database line, and change it to `database=alliance_mumble`
|
||||
- `dbDriver=QMYSQL`
|
||||
- `dbUsername=allianceserver` or whatever you called the AllianceAuth MySQL user
|
||||
- `dbPassword=` that user’s password
|
||||
- `dbPort=3306`
|
||||
- `dbPrefix=murmur_`
|
||||
|
||||
To name your root channel, uncomment and set `registerName=` to whatever cool name you want
|
||||
|
||||
Save and close the file (control + O, control + X).
|
||||
|
||||
To get mumble superuser account credentials, run the following:
|
||||
|
||||
sudo dpkg-reconfigure mumble-server
|
||||
|
||||
Set the password to something you’ll remember and write it down. This is needed to manage ACLs.
|
||||
|
||||
Now restart the server to see the changes reflected.
|
||||
|
||||
sudo service mumble-server restart
|
||||
|
||||
That’s it! Your server is ready to be connected to at yourdomain.com:64738
|
||||
|
||||
## Configuring the Authenticator
|
||||
|
||||
The ICE authenticator lives in `allianceauth/thirdparty/Mumble/`, cd to this directory.
|
||||
|
||||
Make a copy of the default config:
|
||||
|
||||
cp authenticator.ini.example authenticator.ini
|
||||
|
||||
Edit `authenticator.ini` and change these values:
|
||||
|
||||
- `[database]`
|
||||
- `user = ` your allianceserver MySQL user
|
||||
- `password = ` your allianceserver MySQL user's password
|
||||
- `[ice]`
|
||||
- `secret = ` the `icewritesecret` password set earlier
|
||||
|
||||
Test your configuration by starting it: `python authenticator.py`
|
||||
|
||||
## Running the Authenticator
|
||||
|
||||
The authenticator needs to be running 24/7 to validate users on Mumble. The best way is to run it in a screen much like celery:
|
||||
|
||||
screen -dm bash -c 'python authenticator.py'
|
||||
|
||||
Much like celery tasks, this process needs to be started every time the server reboots. It needs to be launched from this directory, so cd to this folder to launch.
|
||||
|
||||
Note that groups will only be created on Mumble automatically when a user joins who is in the group.
|
||||
|
||||
## Making and Managing Channels
|
||||
ACL is really above the scope of this guide. Once AllianceAuth creates your groups, go ahead and follow one of the wonderful web guides available on how to set up channel ACL properly.
|
||||
|
||||
## Setup Complete
|
||||
You’ve finished the steps required to make AllianceAuth work with Mumble. Play around with it and make it your own.
|
||||
99
docs/installation/services/openfire.md
Normal file
99
docs/installation/services/openfire.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Openfire
|
||||
|
||||
## Overview
|
||||
Openfire is a java-based xmpp server (jabber).
|
||||
|
||||
## Dependencies
|
||||
One additional package is required - [openjdk8](http://askubuntu.com/questions/464755/how-to-install-openjdk-8-on-14-04-lts)
|
||||
|
||||
sudo add-apt-repository ppa:webupd8team/java -y
|
||||
sudo apt-get update
|
||||
sudo apt-get install oracle-java8-installer
|
||||
|
||||
## Setup
|
||||
### Download Installer
|
||||
Openfire is not available through repositories so we need to get a debian from the developer.
|
||||
|
||||
On your PC, naviage to the [Ignite Realtime downloads section](https://www.igniterealtime.org/downloads/index.jsp), and under Openfire select Linux, click on the debian file (2nd from bottom of list, ends with .deb).
|
||||
|
||||
Retrieve the file location by copying the url from the “click here” link.
|
||||
|
||||
In the console, ensure you’re in your user’s home directory: `cd ~`
|
||||
|
||||
Now download the package. Replace the link below with the link you got earlier.
|
||||
|
||||
wget https://www.igniterealtime.org/downloadServlet?filename=openfire/openfire_4.1.1_all.deb
|
||||
|
||||
Now install from the debian. Replace the filename with your file name (the last part of the download url is the file name)
|
||||
|
||||
sudo dpkg -i openfire_4.1.1_all.deb
|
||||
|
||||
### Web Configuration
|
||||
The remainder of the setup occurs through Openfire’s web interface. Navigate to http://yourdomain.com:9090, or if you’re behind CloudFlare, go straight to your server’s IP:9090.
|
||||
|
||||
Select your language. I sure hope it’s english if you’re reading this guide.
|
||||
|
||||
Under Server Settings, set the Domain to `yourdomain.com` replacing it with your actual domain. Don’t touch the rest.
|
||||
|
||||
Under Database Settings, select `Standard Database Connection`
|
||||
|
||||
On the next page, select `MySQL` from the dropdown list and change the following:
|
||||
- `[server]` is replaced by `127.0.0.1`
|
||||
- `[database]` is replaced by the name of the database to be used by Openfire
|
||||
- enter the MySQL username you created for AllianceAuth, usually `allianceserver`
|
||||
- enter the MySQL password for this user
|
||||
|
||||
If Openfire returns with a failed to connect error, re-check these settings. Note the lack of square brackets.
|
||||
|
||||
Under Profile Settings, leave `Default` selected.
|
||||
|
||||
Create an administrator account. The actual name is irrelevant, just don’t lost this login information.
|
||||
|
||||
Finally, log in to the console with your admin account.
|
||||
|
||||
### REST API Setup
|
||||
Navigate to the `plugins` tab, and then `Available Plugins` on the left navigation bar. You’ll need to fetch the list of available plugins by clicking the link.
|
||||
|
||||
Once loaded, press the green plus on the right for `REST API`.
|
||||
|
||||
Navigate the `Server` tab, `Sever Settings` subtab. At the bottom of the left navigation bar select `REST API`.
|
||||
|
||||
Select `Enabled`, and `Secret Key Auth`. Update Alliance Auth settings with this secret key as `OPENFIRE_SECRET_KEY`.
|
||||
|
||||
### Broadcast Plugin Setup
|
||||
|
||||
Navigate to the `Users/Groups` tab and select `Create New User` from the left navigation bar.
|
||||
|
||||
Username is what you set in `BROADCAST_USER` without the @ sign, usually `broadcast`.
|
||||
|
||||
Password is what you set in `BROADCAST_USER_PASSWORD`
|
||||
|
||||
Press `Create User` to save this user.
|
||||
|
||||
Broadcasting requires a plugin. Navigate to the `plugins` tab, press the green plus for the `Broadcast` plugin.
|
||||
|
||||
Navigate to the `Server` tab, `Server Manager` subtab, and select `System Properties`. Enter the following:
|
||||
|
||||
- Name: `plugin.broadcast.disableGroupPermissions`
|
||||
- Value: `True`
|
||||
- Do not encrypt this property value
|
||||
- Name: `plugin.broadcast.allowedUsers`
|
||||
- Value: `broadcast@yourdomain.com`, replacing the domain name with yours
|
||||
- Do not encrypt this property value
|
||||
|
||||
### Group Chat
|
||||
Channels are available which function like a chat room. Access can be controlled either by password or ACL (not unlike mumble).
|
||||
|
||||
Navigate to the `Group Chat` tab and select `Create New Room` from the left navigation bar.
|
||||
- Room ID is a short, easy-to-type version of the room’s name users will connect to
|
||||
- Room Name is the full name for the room
|
||||
- Description is short text describing the room’s purpose
|
||||
- Set a password if you want password authentication
|
||||
- Every other setting is optional. Save changes.
|
||||
|
||||
Now select your new room. On the left navigation bar, select `Permissions`.
|
||||
|
||||
ACL is achieved by assigning groups to each of the three tiers: `Owners`, `Admins` and `Members`. `Outcast` is the blacklist. You’ll usually only be assigning groups to the `Member` category.
|
||||
|
||||
## Setup Complete
|
||||
You’ve finished the steps required to make AllianceAuth work with Openfire. Play around with it and make it your own.
|
||||
77
docs/installation/services/pathfinder.md
Normal file
77
docs/installation/services/pathfinder.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Pathfinder
|
||||
Pathfinder is a wormhole mapping tool.
|
||||
|
||||
While auth doesn't integrate with pathfinder anymore, from personal experience I've found it much easier to use the following install process than to try and follow the pathfinder-supplied docs.
|
||||
|
||||
|
||||
## Installation
|
||||
### Get the code
|
||||
|
||||
Navigate to the install location: `cd /var/www/` and git clone the repo:
|
||||
|
||||
sudo git clone https://github.com/exodus4d/pathfinder.git
|
||||
|
||||
### Create logs and caches
|
||||
|
||||
Change directory to pathfinder: `cd pathfinder`
|
||||
|
||||
The logging and caching folders need to be created and have permission set. If upon installation you get Server Error 500, try resetting these permissions.
|
||||
|
||||
sudo mkdir logs
|
||||
sudo mkdir tmp/cache
|
||||
sudo chmod -R 766 logs
|
||||
sudo chmod -R 766 tmp/cache
|
||||
|
||||
## .htaccess Configuration
|
||||
|
||||
In your `pathfinder` directory there are two `.htaccess` files. The default installation instructions want you to choose one for rewriting purposes, and these force you to www.pathfinder.mydomain.com. Personally I don't like that.
|
||||
|
||||
So we'll frankenstein our own. We'll use the HTTP one as a base:
|
||||
|
||||
sudo mv .htaccess .htaccess_HTTPS
|
||||
sudo mv .htaccess_HTTPS .htaccess
|
||||
sudo nano .htaccess
|
||||
|
||||
Find the www rewriting section (labelled `Rewrite NONE www. to force www.`). Change it so that all lines start with a `#`:
|
||||
|
||||
#RewriteCond %{HTTP_HOST} !^www\.
|
||||
# skip "localhost" (dev environment)...
|
||||
#RewriteCond %{HTTP_HOST} !=localhost
|
||||
# skip IP calls (dev environment) e.g. 127.0.0.1
|
||||
#RewriteCond %{HTTP_HOST} !^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$
|
||||
# rewrite everything else to "http://" and "www."
|
||||
#RewriteRule .* http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
|
||||
|
||||
This allows us to choose SSL and www forwarding with our apache conf instead of this htaccess file.
|
||||
|
||||
## Apache Configuration
|
||||
The best way to have this is to setup a subdomain on your server.
|
||||
|
||||
Create a config file `sudo nano /etc/apache2/sites-available/pathfinder.conf` and enter [this configuration](http://pastebin.com/wmXyf6pN), being sure to edit the `ServerName`
|
||||
|
||||
Enable it with:
|
||||
|
||||
sudo a2ensite pathfinder.conf
|
||||
sudo service apache2 reload
|
||||
|
||||
## Configuration Files
|
||||
|
||||
The default configuration should be fine in most cases. Edit all values with caution!
|
||||
|
||||
environment.ini
|
||||
- `SERVER` Should be changed to `PRODUCTION`
|
||||
- `BASE` is the full filesystem path to the application root on your server. In our case, `/var/www/pathfinder/`
|
||||
- `URL` Is the URL to your app (without a trailing slash). In our case, `http://pathfinder.mydomain.com`
|
||||
- `DEBUG` sets the level of debugging (1,2 or 3) (check /logs for a more detail backtrace information)
|
||||
- `DB_*` sets your DB connection information
|
||||
- `SMTP_*` are used to send out emails, you will need an SMTP server login to make this work. (not required)
|
||||
- `SSO_CCP_*` follow the [official docs](https://github.com/exodus4d/pathfinder/wiki/CREST)
|
||||
|
||||
## Database Setup
|
||||
This is done through the browser. Go to `pathfinder.yourdomain.com/setup` and see the [official docs](https://github.com/exodus4d/pathfinder/wiki/Database) for instructions.
|
||||
|
||||
## Cron Jobs
|
||||
Again the [official docs](https://github.com/exodus4d/pathfinder/wiki/Cronjob) do a good job here.
|
||||
|
||||
## Finish Setup
|
||||
Once you've compelted the above steps, we need to disable the setup page. Edit the routes with `nano app/routes.ini` and put a `;` in front of the line starting with `GET @setup`
|
||||
69
docs/installation/services/phpbb3.md
Normal file
69
docs/installation/services/phpbb3.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# phpBB3
|
||||
|
||||
## Overview
|
||||
phpBB is a free php-based forum. It’s the default forum for AllianceAuth.
|
||||
|
||||
## Dependencies
|
||||
All dependencies should have been taken care of during setup.
|
||||
|
||||
## Setup
|
||||
### Download Phpbb3
|
||||
phpBB is available as a zip from their website. Navigate to the website’s [downloads section](https://www.phpbb.com/downloads/) using your PC browser and copy the URL for the latest version zip.
|
||||
|
||||
In the console, navigate to your user’s home directory: `cd ~`
|
||||
|
||||
Now download using wget, replacing the url with the url for the package you just retrieved
|
||||
|
||||
wget https://www.phpbb.com/files/release/phpBB-3.2.0.zip
|
||||
|
||||
This needs to be unpackaged. Unzip it, replacing the file name with that of the file you just downloaded
|
||||
|
||||
unzip phpBB-3.2.0.zip
|
||||
|
||||
Now we need to move this to our web directory. Usually `/var/www/forums`.
|
||||
|
||||
sudo mv phpBB3 /var/www/forums
|
||||
|
||||
The web server needs read/write permission to this folder
|
||||
|
||||
sudo chown -R www-data:www-data /var/www/forums
|
||||
|
||||
### Web Install
|
||||
Navigate to http://yourdomain.com/forums where you will be presented with an installer.
|
||||
|
||||
Click on the `Install` tab.
|
||||
|
||||
All the requirements should be met. Press `Start Install`.
|
||||
|
||||
Under Database Settings, set the following:
|
||||
- Database Type is `MySQL`
|
||||
- Database Server Hostname is `127.0.0.1`
|
||||
- Database Server Port is left blank
|
||||
- Database Name is `alliance_forum`
|
||||
- Database Username is your MySQL user for AllianceAuth, usually `allianceserver`
|
||||
- Database Password is this user’s password
|
||||
|
||||
You should see `Succesful Connection` and proceed.
|
||||
|
||||
Enter administrator credentials on the next page.
|
||||
|
||||
Everything from here should be intuitive.
|
||||
|
||||
phpBB will then write its own config file.
|
||||
|
||||
### Open the Forums
|
||||
Before users can see the forums, we need to remove the install directory
|
||||
|
||||
sudo rm -rf /var/www/forums/install
|
||||
|
||||
### Enabling Avatars
|
||||
AllianceAuth sets user avatars to their character portrait when the account is created or password reset. We need to allow external URLs for avatars for them to behave properly. Navigate to the admin control panel for phpbb3, and under the `General` tab, along the left navigation bar beneath `Board Configuration`, select `Avatar Settings`. Set `Enable Remote Avatars` to `Yes` and then `Submit`.
|
||||
|
||||

|
||||
|
||||
You can allow members to overwrite the portrait with a custom image if desired. Navigate to `Users and Groups`, `Group Permissions`, select the appropriate group (usually `Member` if you want everyone to have this ability), expand `Advanced Permissions`, under the `Profile` tab, set `Can Change Avatars` to `Yes`, and press `Apply Permissions`.
|
||||
|
||||

|
||||
|
||||
## Setup Complete
|
||||
You’ve finished the steps required to make AllianceAuth work with phpBB. Play around with it and make it your own.
|
||||
50
docs/installation/services/smf.md
Normal file
50
docs/installation/services/smf.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# SMF
|
||||
|
||||
## Overview
|
||||
SMF is a free php-based forum. It’s the one of the forums for AllianceAuth.
|
||||
|
||||
## Dependencies
|
||||
All dependencies should have been taken care of during setup.
|
||||
|
||||
## Setup
|
||||
### Download SMF
|
||||
Using your browser, you can download the latest version of SMF to your desktop computer. All SMF downloads can be found at SMF Downloads. The latest recommended version will always be available at http://www.simplemachines.org/download/index.php/latest/install/.
|
||||
|
||||
In the console, navigate to your user’s home directory: `cd ~`
|
||||
|
||||
Now download using wget, replacing the url with the url for the package you just retrieved
|
||||
|
||||
wget http://download.simplemachines.org/index.php?thanks;filename=smf_2-0-13_install.zip
|
||||
|
||||
This needs to be unpackaged. Unzip it, replacing the file name with that of the file you just downloaded
|
||||
|
||||
unzip smf_2-0-13_install.zip
|
||||
|
||||
Now we need to move this to our web directory. Usually `/var/www/forums`.
|
||||
|
||||
sudo mv smf /var/www/forums
|
||||
|
||||
The web server needs read/write permission to this folder
|
||||
|
||||
sudo chown -R www-data:www-data /var/www/forums
|
||||
|
||||
### Web Install
|
||||
Navigate to http://yourdomain.com/forums where you will be presented with an installer.
|
||||
|
||||
Click on the `Install` tab.
|
||||
|
||||
All the requirements should be met. Press `Start Install`.
|
||||
|
||||
Under Database Settings, set the following:
|
||||
- Database Type is `MySQL`
|
||||
- Database Server Hostname is `127.0.0.1`
|
||||
- Database Server Port is left blank
|
||||
- Database Name is `alliance_smf`
|
||||
- Database Username is your MySQL user for AllianceAuth, usually `allianceserver`
|
||||
- Database Password is this user’s password
|
||||
|
||||
Follow the Directions in the installer.
|
||||
|
||||
|
||||
## Setup Complete
|
||||
You’ve finished the steps required to make AllianceAuth work with SMF. Play around with it and make it your own.
|
||||
115
docs/installation/services/teamspeak3.md
Normal file
115
docs/installation/services/teamspeak3.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Teamspeak 3
|
||||
|
||||
## Overview
|
||||
Teamspeak3 is the most popular VOIP program for gamers.
|
||||
|
||||
But have you considered using Mumble? Not only is it free, but it has features and performance far superior to Teamspeak3.
|
||||
|
||||
## Dependencies
|
||||
All dependencies should have been taken care of during the AllianceAuth install.
|
||||
|
||||
## Setup
|
||||
Sticking with it? Alright, I tried.
|
||||
|
||||
### Download Installer
|
||||
To install we need a copy of the server. You can find the latest version from [this dl server](http://dl.4players.de/ts/releases/) (I’d recommed getting the latest stable version – find this version number from the [TeamSpeak site](https://www.teamspeak.com/downloads#)). Be sure to get a link to the linux version.
|
||||
|
||||
From the console, ensure you’re in the user’s home directory: `cd ~`
|
||||
|
||||
And now download the server, replacing the link with the link you got earlier.
|
||||
|
||||
http://dl.4players.de/ts/releases/3.0.13.6/teamspeak3-server_linux_amd64-3.0.13.6.tar.bz2
|
||||
|
||||
Now we need to extract the file.
|
||||
|
||||
tar -xf teamspeak3-server_linux_amd64-3.0.13.6.tar.bz2
|
||||
|
||||
### Create User
|
||||
Teamspeak needs its own user.
|
||||
|
||||
sudo adduser --disabled-login teamspeak
|
||||
|
||||
### Install Binary
|
||||
Now we move the server binary somewhere more accessible and change its ownership to the new user.
|
||||
|
||||
sudo mv teamspeak3-server_linux_amd64 /usr/local/teamspeak
|
||||
sudo chown -R teamspeak:teamspeak /usr/local/teamspeak
|
||||
|
||||
### Startup
|
||||
Now we generate a startup script so teamspeak comes up with the server.
|
||||
|
||||
sudo ln -s /usr/local/teamspeak/ts3server_startscript.sh /etc/init.d/teamspeak
|
||||
sudo update-rc.d teamspeak defaults
|
||||
|
||||
Finally we start the server.
|
||||
|
||||
sudo service teamspeak start
|
||||
|
||||
### Update Settings
|
||||
The console will spit out a block of text. **SAVE THIS**.
|
||||
|
||||
Update the AllianceAuth settings file with the following from that block of text:
|
||||
- `TEAMSPEAK3_SERVERQUERY_USER` is `loginname` (usually `serveradmin`)
|
||||
- `TEAMSPEAK3_SERVERQUERY_PASSWORD` is `password`
|
||||
|
||||
Save and reload apache. Restart celery workers as well.
|
||||
|
||||
sudo service apache2 reload
|
||||
|
||||
If you plan on claiming the ServerAdmin token, do so with a different TeamSpeak client profile than the one used for your auth account, or you will lose your admin status.
|
||||
|
||||
### Generate User Account
|
||||
And now we can generate ourselves a user account. Navigate to the services in AllianceAuth for your user account and press the checkmark for TeamSpeak 3.
|
||||
|
||||
Click the URL provided to automatically connect to our server. It will prompt you to redeem the serveradmin token, enter the `token` from startup.
|
||||
|
||||
### Groups
|
||||
|
||||
Now we need to make groups. AllianceAuth handles groups in teamspeak differently: instead of creating groups it creates an association between groups in TeamSpeak and groups in AllianceAuth. Go ahead and make the groups you want to associate with auth groups, keeping in mind multiple TeamSpeak groups can be associated with a single auth group.
|
||||
|
||||
Navigate back to the AllianceAuth admin interface (yourdomain.com/admin) and under `Services`, select `Auth / TS Groups`. In the top-right corner click `Add`.
|
||||
|
||||
The dropdown box provides all auth groups. Select one and assign TeamSpeak groups from the panels below. If these panels are empty, wait a minute for the database update to run, or see the [troubleshooting section](#ts-group-models-not-populating-on-admin-site) below.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `Insufficient client permissions (failed on Invalid permission: 0x26)`
|
||||
|
||||
Using the advanced permissions editor, ensure the `Guest` group has the permission `Use Privilege Keys to gain permissions` (under `Virtual Server` expand the `Administration` section)
|
||||
|
||||
To enable advanced permissions, on your client go to the `Tools` menu, `Application`, and under the `Misc` section, tick `Advanced permission system`
|
||||
|
||||
### TS group models not populating on admin site
|
||||
The method which populates these runs every 30 minutes. To populate manually, start a celery shell:
|
||||
|
||||
python manage.py celery shell
|
||||
|
||||
And execute the update:
|
||||
|
||||
run_ts3_group_update()
|
||||
|
||||
Ensure that command does not return an error.
|
||||
|
||||
### `2564 access to default group is forbidden`
|
||||
|
||||
This usually occurs because auth is trying to remove a user from the `Guest` group (group ID 8). The guest group is only assigned to a user when they have no other groups, unless you have changed the default teamspeak server config.
|
||||
|
||||
Teamspeak servers v3.0.13 and up are especially susceptible to this. Ensure the Channel Admin Group is not set to `Guest (8)`. Check by right clicking on the server name, `Edit virtual server`, and in the middle of the panel select the `Misc` tab.
|
||||
|
||||
### `TypeError: string indices must be integers, not str`
|
||||
|
||||
This error generally means teamspeak returned an error message that went unhandled. The full traceback is required for proper debugging, which the logs do not record. Please check the superuser notifications for this record and get in touch with a developer.
|
||||
|
||||
### `3331 flood ban`
|
||||
|
||||
This most commonly happens when your teamspeak server is externally hosted. You need to add the auth server IP to the teamspeak serverquery whitelist. This varies by provider.
|
||||
|
||||
If you have SSH access to the server hosting it, you need to locate the teamspeak server folder and add the auth server IP on a new line in `server_query_whitelist.txt`
|
||||
|
||||
### `520 invalid loginname or password`
|
||||
|
||||
The serverquery account login specified in settings.py is incorrect. Please verify `TEAMSPEAK3_SERVERQUERY_USER` and `TEAMSPEAK3_SERVERQUERY_PASSWORD`. The [installation section](#update-settings) describes where to get them.
|
||||
|
||||
### `2568 insufficient client permissions`
|
||||
|
||||
This usually occurs if you've created a separate serverquery user to use with auth. It has not been assigned sufficient permissions to complete all the tasks required of it. The full list of required permissions is not known, so assign liberally.
|
||||
31
docs/installation/services/xenforo.md
Normal file
31
docs/installation/services/xenforo.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# XenForo
|
||||
|
||||
In this chapter we will explore how to setup AllianceAuth to work with [XenForo](https://xenforo.com/). At this point we will assume that you already have XenForo installed with a valid license (please keep in mind that XenForo is not free nor open-source, therefore you need to purchase a license first). If you come across any problems related with the installation of XenForo please contact their support service.
|
||||
|
||||
|
||||
## XenAPI
|
||||
|
||||
By default XenForo does not support any kind of API, however there is a third-party package called [XenAPI](https://github.com/Contex/XenAPI) which provides a simple REST interface by which we can access XenForo's functions in order to create and edit users.
|
||||
|
||||
The installation of XenAPI is pretty straight forward. The only thing you need to do is to download the `api.php` from the official repository and upload it in the root folder of your XenForo installation. The final result should look like this:
|
||||
*forumswebsite.com/***api.php**
|
||||
|
||||
Now that XenAPI is installed the only thing left to do is to provide a key.
|
||||
|
||||
```php
|
||||
$restAPI = new RestAPI('REPLACE_THIS_WITH_AN_API_KEY');
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
AllianceAuth only needs to know 3 things about XenForo.
|
||||
|
||||
+ The API Endpoint
|
||||
+ The API Key
|
||||
+ The default group
|
||||
|
||||
The first two should be self explanatory. The default group is where AllianceAuth will add the user once his account is created. Unfortunately XenAPI **cannot create new groups**, therefore you have to create a group manually and then get its ID.
|
||||
|
||||
When you have a forum section which should be accessible ONLY by the auth'd users the access settings must be set to the default group.
|
||||
|
||||
In the future we will have different groups for blues and alliance/corp members.
|
||||
297
docs/maintenance/changelog.md
Normal file
297
docs/maintenance/changelog.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# Changelog
|
||||
|
||||
## From now on all changelogs will be included as release notes.
|
||||
https://github.com/allianceauth/allianceauth/releases
|
||||
|
||||
### 547
|
||||
Oct 16
|
||||
|
||||
Golly this is a big one. Upgrading takes a bit of work. [For full instructions click here.](https://github.com/allianceauth/allianceauth/pull/547#issue-183247630)
|
||||
|
||||
- Update django version to 1.10
|
||||
- Remove member/blue permissions
|
||||
- implement user states
|
||||
- implement Django's messaging framework for site feedback
|
||||
- remove pathfinder support
|
||||
- remove fleet fits page
|
||||
- remove wormhole tracker
|
||||
- do not store service passwords
|
||||
- supervisor configs for celery tasks and authenticator
|
||||
- buttons on admin site to sync service groups
|
||||
- show number of notifications
|
||||
- fix all button css
|
||||
- rewrite and centralize API checks
|
||||
- bulk mark read / delete for notifications
|
||||
- replace hard-coded urls with reverse by name
|
||||
- python 3 compatibility
|
||||
- correct navbar active link with translated urls
|
||||
|
||||
### 468
|
||||
June 12
|
||||
- XenForo integration added
|
||||
- Discord integration updated to use OAuth and official API
|
||||
- FleetUp fixes for empty responses
|
||||
|
||||
### 441
|
||||
May 27
|
||||
- Added option to require new API keys
|
||||
- Reduces threat of stolen keys being used to create accounts
|
||||
- Requires two new settings:
|
||||
- `REJECT_OLD_APIS`, default `False`
|
||||
- `REJECT_OLD_APIS_MARGIN`, default 50
|
||||
|
||||
### 423
|
||||
May 9
|
||||
- Added statistics to fleet activity tracking
|
||||
- Capture teamspeak error codes in logs from failed account creation
|
||||
|
||||
### 401
|
||||
Apr 29
|
||||
- Added FleetUp integration
|
||||
- Added Fleet Activity Tracking links
|
||||
- settings.py has new entries and will have to be updated
|
||||
|
||||
### 394
|
||||
Apr 17
|
||||
- Added Discourse integration
|
||||
- Added Pathfinder integration
|
||||
- settings.py has new entries and will have to be updated
|
||||
|
||||
### 386
|
||||
Apr 15 2016
|
||||
- Corrected Teamspeak group sync triggers
|
||||
- Modified username sanitization to reduce username collisions
|
||||
|
||||
### 369
|
||||
Apr 7 2016
|
||||
- Added Evernus Alliance Market Integration
|
||||
- Requires libffi-devel (centos) or libffi-dev (ubuntu) and pip install bcrypt
|
||||
|
||||
### 365
|
||||
Apr 6 2016
|
||||
- Added SMF2 Forums integration
|
||||
- Requires a settings.py file update for existing installs
|
||||
|
||||
### 360
|
||||
Apr 4 2016
|
||||
- Added a countdown / local time to the fleet operations timers
|
||||
- Fixed the corporation structure timers so the countdown shows up correctly
|
||||
|
||||
### 340
|
||||
Mar 31 2016
|
||||
- Added Support for IP Board 4 / IP Suite 4
|
||||
- You must update settings.py accordingly if updating form a previous version.
|
||||
- only allows for the member group to sync. Additional groups must be manually added
|
||||
- Fixed a bug with corporation stats not showing correct users and numbers
|
||||
|
||||
### 328
|
||||
Mar 24 2016
|
||||
- Added Enhancements to the SRP Management
|
||||
- Users can now enable and disable srp links.
|
||||
- The Approve and Reject buttons will show up depending on the srp status.
|
||||
- Fixed an issue where SRP Requests were not getting the proper status assigned.
|
||||
|
||||
### 321
|
||||
Mar 23 2016
|
||||
- Added Ship types and kill board data to the SRP management.
|
||||
- These are automatically pulled from zKillboard.
|
||||
- zKillboard is the only killboard links that the SRP Manager Accepts Now.
|
||||
|
||||
### 314
|
||||
Mar 22 2016
|
||||
- Revamp of the Human Resources Application and Management System
|
||||
- see the [docs](../features/hrapplications.md) for how to use the new system
|
||||
- a completely untested conversion script exists. If you want to view your old models, contact Adarnof to try it out
|
||||
- Moved Error Handling for the API Keys to the API Calls to better handle API server outages
|
||||
- Removed the infamous database update task
|
||||
- implemented a receiver to update service groups as they change
|
||||
|
||||
To remove the database update task from the scheduler, navigate to your django admin site, and delete the run_databaseUpdate model from the periodic tasks. Restart celery.
|
||||
|
||||
Mumble now uses an ICE authenticator. This requires an additional dependency. Please install `libbz2-dev` and `libssl-dev` prior to running the update script:
|
||||
|
||||
sudo apt-get install libbz2-dev libssl-dev
|
||||
|
||||
Now run the update script.
|
||||
|
||||
Old Mumble accounts are incompatible. Users will need to recreate them (sorry). To clear the old ones out:
|
||||
|
||||
python manage.py shell
|
||||
from services.tasks import disable_mumble
|
||||
disable_mumble()
|
||||
|
||||
To set up the authenticator, follow the [Mumble setup guide.](../installation/services/mumble.md)
|
||||
|
||||
Optional: you can delete the entire mumble database block in settings.py
|
||||
|
||||
### 304
|
||||
Mar 8 2016
|
||||
- Repurposed Signature Tracker for Wormhole Use. Combat sites are a ever changing thing therefore removed.
|
||||
- Increased run_databaseUpdate time to 10 minutes to address stability problems for larger alliances.
|
||||
|
||||
### 296
|
||||
Feb 27 2016
|
||||
- corrected an issue with populating corp stats for characters with missing api keys
|
||||
- moved log files to dedicated folder to allow apache access so it can rotate them
|
||||
- merged Corp Stats and Member Tracking apps
|
||||
- `corp_stats` and `corputils` permissions have been depreciated
|
||||
- assign either of `corp_apis` or `alliance_apis` to get access to Corp Stats app
|
||||
- `corp_apis` populates APIs of user's main corp
|
||||
- `alliance_apis` populates APIs of user's main alliance
|
||||
|
||||
### 289
|
||||
Feb 25 2016
|
||||
- Changed the start time format on the fleet operations board to use the 24 hour format
|
||||
- Fixed an issue when updating the fleet operations timers the date time picker would not work.
|
||||
|
||||
### 286
|
||||
Feb 23 2016
|
||||
- Added ability to remove notifications
|
||||
|
||||
### 278
|
||||
Feb 18 2016
|
||||
- notifications for events:
|
||||
- api failed validation during refresh
|
||||
- group request accepted/rejected
|
||||
- corp application accepted/rejected
|
||||
- services disabled
|
||||
- logging notifications include traceback
|
||||
- automatically assign alliance groups of the form "Alliance_NAME"
|
||||
- parallel corp model updates via celery broker for performance improvements
|
||||
- new functions to clear service info for decommissioning a service
|
||||
|
||||
settings.py will need to be updated to include the new settings.
|
||||
|
||||
### 265
|
||||
Feb 13 2016
|
||||
- prototype notification system
|
||||
- logging errors as notifications based on new permission `logging_notifications`
|
||||
|
||||
The logging configuration in settings.py has changed. Please update.
|
||||
|
||||
### 263
|
||||
Feb 12 2016
|
||||
- revamped `run_corp_update` function which actually works
|
||||
- fixed group syncing in discord and openfire
|
||||
|
||||
### 259
|
||||
Feb 11 2016
|
||||
- Added ability to edit structure timers
|
||||
- Added ability to edit fleet operations timers
|
||||
- Added ability to edit Signatures
|
||||
|
||||
|
||||
### 245
|
||||
Feb 7 2016
|
||||
|
||||
- ability to toggle assigning corp groups
|
||||
- users able to manually trigger api refresh
|
||||
|
||||
Two new settings in [settings.py](../installation/auth/settings.md) - `MEMBER_CORP_GROUPS` and `BLUE_CORP_GROUPS` - be sure to add them.
|
||||
|
||||
### 226
|
||||
Jan 31 2016
|
||||
|
||||
Been a while since one of these was written so a big list.
|
||||
|
||||
- corrected user permission caching for Phpbb3
|
||||
- open groups which don't require application approval
|
||||
- additional weblink data for TS3 to encourage proper usernames
|
||||
- corp-restricted timers
|
||||
- signature tracker
|
||||
- tolerate random 221 errors from EVE api servers till CCP FoxFour gets it sorted
|
||||
- new corp member auditing app
|
||||
- fleet operation timers
|
||||
- revamped member status checking and assignment
|
||||
|
||||
Loads of new permissions. See the readme for descriptions.
|
||||
|
||||
Need to install new requirements - `sudo pip install -r requirements.txt`
|
||||
|
||||
Incompatible with Python2.6 or older. Please update. Please. It's 2016 people.
|
||||
|
||||
Settings.py got nuked. Backup your current settings.py, copy the example, and repopulate.
|
||||
|
||||
New caching directory for requests - if you're using apache to serve the site, `cache/` needs to be writable by the webserver. Easiest way is to `chown -R www-data:www-data cache` from within the allianceauth install dir.
|
||||
|
||||
### 145
|
||||
Jan 6 2016
|
||||
|
||||
- complete logging system for all apps
|
||||
- custom service passwords
|
||||
- Discord token caching to prevent locking out users
|
||||
- Jabber broadcast group restrictions
|
||||
- Password reset email contains domain
|
||||
- Index page only renders forums/killboard/media if url specified
|
||||
- timestamps on hrapplication comments
|
||||
- corrected corp/alliance model creation logic
|
||||
- corrected typecasting of access masks during api checks
|
||||
- prevent TS3 from attempting to sync groups if not installed
|
||||
|
||||
New permissions - see readme.
|
||||
|
||||
Need to install new requirements.
|
||||
|
||||
Settings.py has changed. Make a new one from the example.
|
||||
|
||||
### 118
|
||||
Dec 2 2015
|
||||
|
||||
- add timers by time remaining
|
||||
- Discord support
|
||||
- corrected celerytask logic
|
||||
- handle many 500s thrown in views
|
||||
|
||||
New settings.py again. Need to reinstall requirements.
|
||||
|
||||
### 107
|
||||
Nov 28 2015
|
||||
|
||||
- added broadcast plugin support for openfire
|
||||
- timer addition by remaining time, not fixed date
|
||||
- corrected alliance model deletion logic
|
||||
- corrected name rendering on templates
|
||||
|
||||
Openfire setup guide has been updated for the new plugin.
|
||||
|
||||
### 102
|
||||
Nov 25 2015
|
||||
|
||||
- variable API requirements
|
||||
- api access mask validation during refresh
|
||||
- support for customization of templates
|
||||
- celery task resource reduction
|
||||
- vagrant support
|
||||
|
||||
All templates and staticfiles have been moved. If you've customized any of these, make a backup before pulling changes.
|
||||
|
||||
New command `python manage.py collectstatic` added to install guide. Should be run after every update.
|
||||
|
||||
New settings.py template. Make a backup of the old one, copy the example, and populate.
|
||||
|
||||
### 87
|
||||
Nov 15 2015
|
||||
|
||||
A couple quality-of-life improvements.
|
||||
|
||||
- corrected an error in the Teamspeak3 Manager improperly parsing responses
|
||||
- added the ability to hide groups from the web interface
|
||||
- added a feature for phpbb avatars to be set to the character portrait
|
||||
|
||||
New permissions for the `HiddenGroup` model only affect the admin site (default model permissions)
|
||||
|
||||
The Phpbb3 setup guide has been updated to reflect avatars.
|
||||
|
||||
### 72
|
||||
Nov 5th 2015
|
||||
|
||||
On November 5th we performed two major pulls from Adarnof’s and Kaezon’s forks.
|
||||
|
||||
Improvements include:
|
||||
|
||||
- ability to deploy for either corp or alliance
|
||||
- improved logic for member status transitions
|
||||
- group syncing to TS3
|
||||
- template corrections
|
||||
|
||||
Migration to the new version is a bit trickier because of changes to settings.py - it's easiest to archive the old one, make a copy of the new template, and repopulate it.
|
||||
10
docs/maintenance/index.md
Normal file
10
docs/maintenance/index.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Maintenance
|
||||
|
||||
```eval_rst
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
changelog
|
||||
troubleshooting
|
||||
|
||||
```
|
||||
49
docs/maintenance/troubleshooting.md
Normal file
49
docs/maintenance/troubleshooting.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Something broken? Stuck on an issue? Can't get it set up?
|
||||
|
||||
Start here:
|
||||
- check the [issues](https://github.com/allianceauth/allianceauth/issues?utf8=%E2%9C%93&q=is%3Aissue) - especially closed ones
|
||||
- check the [forums](https://forums.eveonline.com/default.aspx?g=posts&t=383030)
|
||||
|
||||
No answer?
|
||||
- open an [issue](https://github.com/allianceauth/allianceauth/issues)
|
||||
- harass us on [gitter](https://gitter.im/R4stl1n/allianceauth)
|
||||
- post to the [forums](https://forums.eveonline.com/default.aspx?g=posts&t=383030)
|
||||
|
||||
## Common Problems
|
||||
|
||||
### `pip install -r requirements.txt` is failing
|
||||
|
||||
Either you need to `sudo` that command, or it's a missing dependency. Check [the list](../installation/auth/dependencies.md), reinstall, and try again.
|
||||
|
||||
### I'm getting an error 500 trying to connect to the website on a new install
|
||||
|
||||
Read the apache error log: `sudo nano /var/log/apache2/error.log`
|
||||
|
||||
If it talks about failing to import something, google its name and install it.
|
||||
|
||||
If it whines about being unable to configure logger, see below.
|
||||
|
||||
### Failed to configure log handler
|
||||
|
||||
Make sure the log directory is write-able: `chmod -R 777 /home/allianceserver/allianceauth/log`, then reload apache/celery/supervisor/etc.
|
||||
|
||||
### Groups aren't syncing to services
|
||||
|
||||
Make sure the background processes are running: `ps aux | grep celery` should return more than 1 line. More lines if you have more cores on your server's processor. If there are more than two lines starting with `SCREEN`, kill all of them with `kill #` where `#` is the process ID (second column), then restart with [these background process commands](../installation/auth/quickstart.md) from the allianceauth directory. You can't start these commands as root.
|
||||
|
||||
If that doesn't do it, try clearing the worker queue. First kill all celery processes as described above, then do the following:
|
||||
|
||||
redis-cli FLUSHALL
|
||||
python manage.py celeryd --purge
|
||||
|
||||
Press control+C once.
|
||||
|
||||
python manage.py celeryd --discard
|
||||
|
||||
Press control+C once.
|
||||
|
||||
Now start celery again with [these background process commands.](../installation/auth/quickstart.md)
|
||||
|
||||
While debugging, it is useful to see if tasks are being executed. The easiest tool is [flower](http://flower.readthedocs.io/). Install it with this: `sudo pip install flower`, then start it with this: `celery flower --broker=amqp://guest:guest@localhost:5672//`. To view the status, navigate to your server IP, port 5555.
|
||||
36
docs/make.bat
Normal file
36
docs/make.bat
Normal file
@@ -0,0 +1,36 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
set SPHINXPROJ=AllianceAuth
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
|
||||
:end
|
||||
popd
|
||||
3
docs/requirements.txt
Normal file
3
docs/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
sphinx>=1.4.0,<1.5.0
|
||||
sphinx_rtd_theme>=0.1.9
|
||||
recommonmark==0.4.0
|
||||
@@ -13,7 +13,17 @@ admin.site.register(EveCorporationInfo)
|
||||
|
||||
class EveApiKeyPairAdmin(admin.ModelAdmin):
|
||||
search_fields = ['api_id', 'user__username']
|
||||
list_display = ['api_id', 'user']
|
||||
list_display = ['api_id', 'user', 'characters']
|
||||
|
||||
@staticmethod
|
||||
def characters(obj):
|
||||
return ', '.join(sorted([c.character_name for c in EveCharacter.objects.filter(api_id=obj.api_id)]))
|
||||
|
||||
def get_search_results(self, request, queryset, search_term):
|
||||
queryset, use_distinct = super(EveApiKeyPairAdmin, self).get_search_results(request, queryset, search_term)
|
||||
chars = EveCharacter.objects.filter(character_name__icontains=search_term)
|
||||
queryset |= EveApiKeyPair.objects.filter(api_id__in=[char.api_id for char in chars if bool(char.api_id)])
|
||||
return queryset, use_distinct
|
||||
|
||||
|
||||
class EveCharacterAdmin(admin.ModelAdmin):
|
||||
@@ -23,7 +33,7 @@ class EveCharacterAdmin(admin.ModelAdmin):
|
||||
@staticmethod
|
||||
def main_character(obj):
|
||||
if obj.user:
|
||||
auth = AuthServicesInfo.objects.get_or_create(user=obj.user)[0]
|
||||
auth = AuthServicesInfo.objects.get(user=obj.user)
|
||||
if auth and auth.main_char_id:
|
||||
try:
|
||||
return EveCharacter.objects.get(character_id=auth.main_char_id)
|
||||
|
||||
@@ -28,7 +28,10 @@ class UpdateKeyForm(forms.Form):
|
||||
raise forms.ValidationError("API ID must be a number")
|
||||
|
||||
def clean(self):
|
||||
super(UpdateKeyForm, self).clean()
|
||||
if 'api_id' not in self.cleaned_data or 'api_key' not in self.cleaned_data:
|
||||
# need to check if api_id and vcode in cleaned_data because
|
||||
# if they fail, they get removed from the dict but this method still happens
|
||||
return self.cleaned_data
|
||||
|
||||
if EveManager.check_if_api_key_pair_exist(self.cleaned_data['api_id']):
|
||||
logger.debug("UpdateKeyForm failed cleaning as API id %s already exists." % self.cleaned_data['api_id'])
|
||||
|
||||
@@ -3,71 +3,58 @@ from eveonline.models import EveCharacter
|
||||
from eveonline.models import EveApiKeyPair
|
||||
from eveonline.models import EveAllianceInfo
|
||||
from eveonline.models import EveCorporationInfo
|
||||
|
||||
from eveonline.providers import eve_adapter_factory, EveXmlProvider
|
||||
from services.managers.eve_api_manager import EveApiManager
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EveManager:
|
||||
def __init__(self):
|
||||
pass
|
||||
class EveManager(object):
|
||||
adapter = None
|
||||
|
||||
@classmethod
|
||||
def get_adapter(cls):
|
||||
if not cls.adapter:
|
||||
cls.adapter = eve_adapter_factory()
|
||||
return cls.adapter
|
||||
|
||||
@classmethod
|
||||
def get_character(cls, character_id):
|
||||
return cls.get_adapter().get_character(character_id)
|
||||
|
||||
@staticmethod
|
||||
def create_character(character_id, character_name, corporation_id,
|
||||
corporation_name, corporation_ticker, alliance_id,
|
||||
alliance_name, user, api_id):
|
||||
logger.debug("Creating model for character %s id %s" % (character_name, character_id))
|
||||
if not EveCharacter.objects.filter(character_id=character_id).exists():
|
||||
eve_char = EveCharacter()
|
||||
eve_char.character_id = character_id
|
||||
eve_char.character_name = character_name
|
||||
eve_char.corporation_id = corporation_id
|
||||
eve_char.corporation_name = corporation_name
|
||||
eve_char.corporation_ticker = corporation_ticker
|
||||
eve_char.alliance_id = alliance_id
|
||||
eve_char.alliance_name = alliance_name
|
||||
eve_char.user = user
|
||||
eve_char.api_id = api_id
|
||||
eve_char.save()
|
||||
logger.info("Created new character model %s for user %s" % (eve_char, user))
|
||||
else:
|
||||
logger.warn("Attempting to create existing character model with id %s" % character_id)
|
||||
def create_character(id, user, api_id):
|
||||
return EveManager.create_character_obj(EveManager.get_character(id), user, api_id)
|
||||
|
||||
@staticmethod
|
||||
def create_characters_from_list(chars, user, api_id):
|
||||
logger.debug("Creating characters from batch: %s" % chars.result)
|
||||
for char in chars.result:
|
||||
if not EveManager.check_if_character_exist(chars.result[char]['name']):
|
||||
EveManager.create_character(chars.result[char]['id'],
|
||||
chars.result[char]['name'],
|
||||
chars.result[char]['corp']['id'],
|
||||
chars.result[char]['corp']['name'],
|
||||
EveApiManager.get_corporation_ticker_from_id(
|
||||
chars.result[char]['corp']['id']),
|
||||
chars.result[char]['alliance']['id'],
|
||||
chars.result[char]['alliance']['name'],
|
||||
user, api_id)
|
||||
def create_character_obj(character, user, api_id):
|
||||
EveCharacter.objects.create(
|
||||
character_id = character.id,
|
||||
character_name = character.name,
|
||||
corporation_id = character.corp.id,
|
||||
corporation_name = character.corp.name,
|
||||
corporation_ticker = character.corp.ticker,
|
||||
alliance_id = character.alliance.id,
|
||||
alliance_name = character.alliance.name,
|
||||
user = user,
|
||||
api_id = api_id,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def update_characters_from_list(chars):
|
||||
logger.debug("Updating characters from list: %s" % chars.result)
|
||||
for char in chars.result:
|
||||
if EveManager.check_if_character_exist(chars.result[char]['name']):
|
||||
eve_char = EveManager.get_character_by_character_name(chars.result[char]['name'])
|
||||
logger.debug("Got existing character model %s" % eve_char)
|
||||
eve_char.corporation_id = chars.result[char]['corp']['id']
|
||||
eve_char.corporation_name = chars.result[char]['corp']['name']
|
||||
eve_char.corporation_ticker = EveApiManager.get_corporation_ticker_from_id(
|
||||
chars.result[char]['corp']['id'])
|
||||
eve_char.alliance_id = chars.result[char]['alliance']['id']
|
||||
eve_char.alliance_name = chars.result[char]['alliance']['name']
|
||||
eve_char.save()
|
||||
logger.info("Updated character model %s" % eve_char)
|
||||
else:
|
||||
logger.warn(
|
||||
"Attempting to update non-existing character model with name %s" % chars.result[char]['name'])
|
||||
def update_character(id):
|
||||
return EveManager.update_character_obj(EveManager.get_character(id))
|
||||
|
||||
@staticmethod
|
||||
def update_character_obj(char):
|
||||
model = EveCharacter.objects.get(character_id=char.id)
|
||||
model.character_name = char.name
|
||||
model.corporation_id = char.corp.id
|
||||
model.corporation_name = char.corp.name
|
||||
model.corporation_ticker = char.corp.ticker
|
||||
model.alliance_id = char.alliance.id
|
||||
model.alliance_name = char.alliance.name
|
||||
model.save()
|
||||
|
||||
@staticmethod
|
||||
def create_api_keypair(api_id, api_key, user_id):
|
||||
@@ -82,65 +69,92 @@ class EveManager:
|
||||
else:
|
||||
logger.warn("Attempting to create existing api keypair with id %s" % api_id)
|
||||
|
||||
@staticmethod
|
||||
def create_alliance_info(alliance_id, alliance_name, alliance_ticker, alliance_executor_corp_id,
|
||||
alliance_member_count, is_blue):
|
||||
logger.debug("Creating alliance info for alliance %s id %s" % (alliance_name, alliance_id))
|
||||
if not EveManager.check_if_alliance_exists_by_id(alliance_id):
|
||||
alliance_info = EveAllianceInfo()
|
||||
alliance_info.alliance_id = alliance_id
|
||||
alliance_info.alliance_name = alliance_name
|
||||
alliance_info.alliance_ticker = alliance_ticker
|
||||
alliance_info.executor_corp_id = alliance_executor_corp_id
|
||||
alliance_info.member_count = alliance_member_count
|
||||
alliance_info.is_blue = is_blue
|
||||
alliance_info.save()
|
||||
logger.info("Created alliance model for %s" % alliance_info)
|
||||
else:
|
||||
logger.warn("Attempting to create existing alliance model with id %s" % alliance_id)
|
||||
@classmethod
|
||||
def get_alliance(cls, alliance_id):
|
||||
return cls.get_adapter().get_alliance(alliance_id)
|
||||
|
||||
@staticmethod
|
||||
def update_alliance_info(alliance_id, alliance_executor_corp_id, alliance_member_count, is_blue):
|
||||
logger.debug("Updating alliance model with id %s" % alliance_id)
|
||||
if EveManager.check_if_alliance_exists_by_id(alliance_id):
|
||||
alliance_info = EveAllianceInfo.objects.get(alliance_id=alliance_id)
|
||||
alliance_info.executor_corp_id = alliance_executor_corp_id
|
||||
alliance_info.member_count = alliance_member_count
|
||||
alliance_info.is_blue = is_blue
|
||||
alliance_info.save()
|
||||
logger.debug("Updated alliance model %s" % alliance_info)
|
||||
else:
|
||||
logger.warn("Attempting to update non-existing alliance model with id %s" % alliance_id)
|
||||
def create_alliance(id, is_blue=False):
|
||||
return EveManager.create_alliance_obj(EveManager.get_alliance(id), is_blue=is_blue)
|
||||
|
||||
@staticmethod
|
||||
def create_corporation_info(corp_id, corp_name, corp_ticker, corp_member_count, is_blue, alliance):
|
||||
logger.debug("Creating corp info for corp %s id %s" % (corp_name, corp_id))
|
||||
if not EveManager.check_if_corporation_exists_by_id(corp_id):
|
||||
corp_info = EveCorporationInfo()
|
||||
corp_info.corporation_id = corp_id
|
||||
corp_info.corporation_name = corp_name
|
||||
corp_info.corporation_ticker = corp_ticker
|
||||
corp_info.member_count = corp_member_count
|
||||
corp_info.is_blue = is_blue
|
||||
if alliance:
|
||||
corp_info.alliance = alliance
|
||||
corp_info.save()
|
||||
logger.info("Created corp model for %s" % corp_info)
|
||||
else:
|
||||
logger.warn("Attempting to create existing corp model with id %s" % corp_id)
|
||||
def create_alliance_obj(alliance, is_blue=False):
|
||||
EveAllianceInfo.objects.create(
|
||||
alliance_id = alliance.id,
|
||||
alliance_name = alliance.name,
|
||||
alliance_ticker = alliance.ticker,
|
||||
executor_corp_id = alliance.executor_corp_id,
|
||||
is_blue = is_blue,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def update_corporation_info(corp_id, corp_member_count, alliance, is_blue):
|
||||
logger.debug("Updating corp model with id %s" % corp_id)
|
||||
if EveManager.check_if_corporation_exists_by_id(corp_id):
|
||||
corp_info = EveCorporationInfo.objects.get(corporation_id=corp_id)
|
||||
corp_info.member_count = corp_member_count
|
||||
corp_info.alliance = alliance
|
||||
corp_info.is_blue = is_blue
|
||||
corp_info.save()
|
||||
logger.debug("Updated corp model %s" % corp_info)
|
||||
else:
|
||||
logger.warn("Attempting to update non-existant corp model with id %s" % corp_id)
|
||||
def update_alliance(id, is_blue=None):
|
||||
return EveManager.update_alliance_obj(EveManager.get_alliance(id), is_blue=is_blue)
|
||||
|
||||
@staticmethod
|
||||
def update_alliance_obj(alliance, is_blue=None):
|
||||
model = EveAllianceInfo.objects.get(alliance_id=alliance.id)
|
||||
model.executor_corp_id = alliance.executor_corp_id
|
||||
model.is_blue = model.is_blue if is_blue == None else is_blue
|
||||
model.save()
|
||||
|
||||
@staticmethod
|
||||
def populate_alliance(id):
|
||||
alliance_model = EveAllianceInfo.objects.get(alliance_id=id)
|
||||
alliance = EveManager.get_alliance(id)
|
||||
for corp_id in alliance.corp_ids:
|
||||
if not EveCorporationInfo.objects.filter(corporation_id=corp_id).exists():
|
||||
EveManager.create_corporation(corp_id, is_blue=alliance_model.is_blue)
|
||||
EveCorporationInfo.objects.filter(corporation_id__in=alliance.corp_ids).update(alliance=alliance_model)
|
||||
EveCorporationInfo.objects.filter(alliance=alliance_model).exclude(corporation_id__in=alliance.corp_ids).update(alliance=None)
|
||||
|
||||
@classmethod
|
||||
def get_corporation(cls, corp_id):
|
||||
return cls.get_adapter().get_corp(corp_id)
|
||||
|
||||
@staticmethod
|
||||
def create_corporation(id, is_blue=False):
|
||||
return EveManager.create_corporation_obj(EveManager.get_corporation(id), is_blue=is_blue)
|
||||
|
||||
@staticmethod
|
||||
def create_corporation_obj(corp, is_blue=False):
|
||||
try:
|
||||
alliance = EveAllianceInfo.objects.get(alliance_id=corp.alliance_id)
|
||||
except EveAllianceInfo.DoesNotExist:
|
||||
alliance = None
|
||||
EveCorporationInfo.objects.create(
|
||||
corporation_id = corp.id,
|
||||
corporation_name = corp.name,
|
||||
corporation_ticker = corp.ticker,
|
||||
member_count = corp.members,
|
||||
alliance = alliance,
|
||||
is_blue = is_blue,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def update_corporation(id, is_blue=None):
|
||||
return EveManager.update_corporation_obj(EveManager.get_corporation(id), is_blue=is_blue)
|
||||
|
||||
@staticmethod
|
||||
def update_corporation_obj(corp, is_blue=None):
|
||||
model = EveCorporationInfo.objects.get(corporation_id=corp.id)
|
||||
model.member_count = corp.members
|
||||
try:
|
||||
model.alliance = EveAllianceInfo.objects.get(alliance_id=corp.alliance_id)
|
||||
except EveAllianceInfo.DoesNotExist:
|
||||
model.alliance = None
|
||||
model.is_blue = model.is_blue if is_blue == None else is_blue
|
||||
model.save()
|
||||
|
||||
@classmethod
|
||||
def get_itemtype(cls, type_id):
|
||||
return cls.get_adapter().get_itemtype(type_id)
|
||||
|
||||
@staticmethod
|
||||
def get_characters_from_api(api):
|
||||
char_result = EveApiManager.get_characters_from_api(api.api_id, api.api_key).result
|
||||
provider = EveXmlProvider(adapter=EveManager.get_adapter())
|
||||
return [provider._build_character(result) for id, result in char_result.items()]
|
||||
|
||||
@staticmethod
|
||||
def get_api_key_pairs(user):
|
||||
@@ -227,6 +241,10 @@ class EveManager:
|
||||
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_characters_by_api_id(api_id):
|
||||
return EveCharacter.objects.filter(api_id=api_id)
|
||||
|
||||
@staticmethod
|
||||
def get_charater_alliance_id_by_id(char_id):
|
||||
if EveCharacter.objects.filter(character_id=char_id).exists():
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-12-16 23:22
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('eveonline', '0004_eveapikeypair_sso_verified'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='eveallianceinfo',
|
||||
name='member_count',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2017-01-02 19:23
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('eveonline', '0005_remove_eveallianceinfo_member_count'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='evecharacter',
|
||||
name='alliance_id',
|
||||
field=models.CharField(blank=True, default='', max_length=254, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evecharacter',
|
||||
name='alliance_name',
|
||||
field=models.CharField(blank=True, default='', max_length=254, null=True),
|
||||
),
|
||||
]
|
||||
122
eveonline/migrations/0007_unique_id_name.py
Normal file
122
eveonline/migrations/0007_unique_id_name.py
Normal file
@@ -0,0 +1,122 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-01-18 13:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def get_duplicates(items):
|
||||
return set([item for item in items if items.count(item) > 1])
|
||||
|
||||
|
||||
def enforce_unique_characters(apps, schema_editor):
|
||||
EveCharacter = apps.get_model('eveonline', 'EveCharacter')
|
||||
|
||||
ids = [c.character_id for c in EveCharacter.objects.all()]
|
||||
duplicates = get_duplicates(ids)
|
||||
for c_id in duplicates:
|
||||
dupes = EveCharacter.objects.filter(character_id=c_id)
|
||||
dupes.exclude(pk=dupes[0].pk).delete()
|
||||
|
||||
names = [c.character_name for c in EveCharacter.objects.all()]
|
||||
duplicates = get_duplicates(names)
|
||||
for name in duplicates:
|
||||
dupes = EveCharacter.objects.filter(character_name=name)
|
||||
dupes.exclude(pk=dupes[0].pk).delete()
|
||||
|
||||
|
||||
def enforce_unique_corporations(apps, schema_editor):
|
||||
EveCorporationInfo = apps.get_model('eveonline', 'EveCorporationInfo')
|
||||
|
||||
ids = [c.corporation_id for c in EveCorporationInfo.objects.all()]
|
||||
duplicates = get_duplicates(ids)
|
||||
for c_id in duplicates:
|
||||
dupes = EveCorporationInfo.objects.filter(corporation_id=c_id)
|
||||
dupes.exclude(pk=dupes[0].pk).delete()
|
||||
|
||||
names = [c.corporation_name for c in EveCorporationInfo.objects.all()]
|
||||
duplicates = get_duplicates(names)
|
||||
for name in duplicates:
|
||||
dupes = EveCorporationInfo.objects.filter(character_name=name)
|
||||
dupes.exclude(pk=dupes[0].pk).delete()
|
||||
|
||||
|
||||
def enforce_unique_alliances(apps, schema_editor):
|
||||
EveAllianceInfo = apps.get_model('eveonline', 'EveAllianceInfo')
|
||||
EveCorporationInfo = apps.get_model('eveonline', 'EveCorporationInfo')
|
||||
|
||||
ids = [a.alliance_id for a in EveAllianceInfo.objects.all()]
|
||||
duplicates = get_duplicates(ids)
|
||||
for a_id in duplicates:
|
||||
dupes = EveAllianceInfo.objects.filter(alliance_id=a_id)
|
||||
to_be_kept = dupes[0]
|
||||
EveCorporationInfo.objects.filter(alliance__pk__in=[a.pk for a in dupes.exclude(pk=to_be_kept.pk)]).update(
|
||||
alliance=to_be_kept.pk)
|
||||
dupes.exclude(pk=to_be_kept.pk).delete()
|
||||
|
||||
names = [a.alliance_name for a in EveAllianceInfo.objects.all()]
|
||||
duplicates = get_duplicates(names)
|
||||
for name in duplicates:
|
||||
dupes = EveAllianceInfo.objects.filter(alliance_name=name)
|
||||
to_be_kept = dupes[0]
|
||||
EveCorporationInfo.objects.filter(alliance__in=[a.pk for a in dupes.exclude(pk=to_be_kept.pk)]).update(
|
||||
alliance=to_be_kept.pk)
|
||||
dupes.exclude(pk=to_be_kept.pk).delete()
|
||||
|
||||
|
||||
def enforce_unique_apis(apps, schema_editor):
|
||||
EveApiKeyPair = apps.get_model('eveonline', 'EveApiKeyPair')
|
||||
|
||||
ids = [api.api_id for api in EveApiKeyPair.objects.all()]
|
||||
duplicates = get_duplicates(ids)
|
||||
for api_id in duplicates:
|
||||
dupes = EveApiKeyPair.objects.filter(api_id=api_id)
|
||||
dupes.exclude(pk=dupes[0].pk).delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('eveonline', '0006_allow_null_evecharacter_alliance'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(enforce_unique_characters, migrations.RunPython.noop),
|
||||
migrations.RunPython(enforce_unique_corporations, migrations.RunPython.noop),
|
||||
migrations.RunPython(enforce_unique_alliances, migrations.RunPython.noop),
|
||||
migrations.RunPython(enforce_unique_apis, migrations.RunPython.noop),
|
||||
migrations.AlterField(
|
||||
model_name='evecharacter',
|
||||
name='character_id',
|
||||
field=models.CharField(max_length=254, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evecharacter',
|
||||
name='character_name',
|
||||
field=models.CharField(max_length=254, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evecorporationinfo',
|
||||
name='corporation_id',
|
||||
field=models.CharField(max_length=254, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='evecorporationinfo',
|
||||
name='corporation_name',
|
||||
field=models.CharField(max_length=254, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='eveallianceinfo',
|
||||
name='alliance_id',
|
||||
field=models.CharField(max_length=254, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='eveallianceinfo',
|
||||
name='alliance_name',
|
||||
field=models.CharField(max_length=254, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='eveapikeypair',
|
||||
name='api_id',
|
||||
field=models.CharField(max_length=254, unique=True),
|
||||
),
|
||||
]
|
||||
@@ -6,13 +6,13 @@ from django.contrib.auth.models import User
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EveCharacter(models.Model):
|
||||
character_id = models.CharField(max_length=254)
|
||||
character_name = models.CharField(max_length=254)
|
||||
character_id = models.CharField(max_length=254, unique=True)
|
||||
character_name = models.CharField(max_length=254, unique=True)
|
||||
corporation_id = models.CharField(max_length=254)
|
||||
corporation_name = models.CharField(max_length=254)
|
||||
corporation_ticker = models.CharField(max_length=254)
|
||||
alliance_id = models.CharField(max_length=254)
|
||||
alliance_name = models.CharField(max_length=254)
|
||||
alliance_id = models.CharField(max_length=254, blank=True, null=True, default='')
|
||||
alliance_name = models.CharField(max_length=254, blank=True, null=True, default='')
|
||||
api_id = models.CharField(max_length=254)
|
||||
user = models.ForeignKey(User, blank=True, null=True)
|
||||
|
||||
@@ -22,7 +22,7 @@ class EveCharacter(models.Model):
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EveApiKeyPair(models.Model):
|
||||
api_id = models.CharField(max_length=254)
|
||||
api_id = models.CharField(max_length=254, unique=True)
|
||||
api_key = models.CharField(max_length=254)
|
||||
user = models.ForeignKey(User, blank=True, null=True)
|
||||
sso_verified = models.BooleanField(default=False)
|
||||
@@ -33,12 +33,11 @@ class EveApiKeyPair(models.Model):
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EveAllianceInfo(models.Model):
|
||||
alliance_id = models.CharField(max_length=254)
|
||||
alliance_name = models.CharField(max_length=254)
|
||||
alliance_id = models.CharField(max_length=254, unique=True)
|
||||
alliance_name = models.CharField(max_length=254, unique=True)
|
||||
alliance_ticker = models.CharField(max_length=254)
|
||||
executor_corp_id = models.CharField(max_length=254)
|
||||
is_blue = models.BooleanField(default=False)
|
||||
member_count = models.IntegerField()
|
||||
|
||||
def __str__(self):
|
||||
return self.alliance_name
|
||||
@@ -46,8 +45,8 @@ class EveAllianceInfo(models.Model):
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EveCorporationInfo(models.Model):
|
||||
corporation_id = models.CharField(max_length=254)
|
||||
corporation_name = models.CharField(max_length=254)
|
||||
corporation_id = models.CharField(max_length=254, unique=True)
|
||||
corporation_name = models.CharField(max_length=254, unique=True)
|
||||
corporation_ticker = models.CharField(max_length=254)
|
||||
member_count = models.IntegerField()
|
||||
is_blue = models.BooleanField(default=False)
|
||||
|
||||
479
eveonline/providers.py
Normal file
479
eveonline/providers.py
Normal file
@@ -0,0 +1,479 @@
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from esi.clients import esi_client_factory
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
import json
|
||||
from bravado.exception import HTTPNotFound, HTTPUnprocessableEntity
|
||||
import evelink
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# optional setting to control cached object lifespan
|
||||
OBJ_CACHE_DURATION = int(getattr(settings, 'EVEONLINE_OBJ_CACHE_DURATION', 300))
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ObjectNotFound(Exception):
|
||||
def __init__(self, obj_id, type_name):
|
||||
self.id = obj_id
|
||||
self.type = type_name
|
||||
|
||||
def __str__(self):
|
||||
return '%s with ID %s not found.' % (self.type, self.id)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Entity(object):
|
||||
def __init__(self, id, name):
|
||||
self.id = id
|
||||
self.name = name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __repr__(self):
|
||||
return "<{} ({}): {}>".format(self.__class__.__name__, self.id, self.name)
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self.id)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.id == other.id
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data_dict):
|
||||
return cls(data_dict['id'], data_dict['name'])
|
||||
|
||||
|
||||
class Corporation(Entity):
|
||||
def __init__(self, provider, id, name, ticker, ceo_id, members, alliance_id):
|
||||
super(Corporation, self).__init__(id, name)
|
||||
self.provider = provider
|
||||
self.ticker = ticker
|
||||
self.ceo_id = ceo_id
|
||||
self.members = members
|
||||
self.alliance_id = alliance_id
|
||||
self._alliance = None
|
||||
self._ceo = None
|
||||
|
||||
@property
|
||||
def alliance(self):
|
||||
if self.alliance_id:
|
||||
if not self._alliance:
|
||||
self._alliance = self.provider.get_alliance(self.alliance_id)
|
||||
return self._alliance
|
||||
return Entity(None, None)
|
||||
|
||||
@property
|
||||
def ceo(self):
|
||||
if not self._ceo:
|
||||
self._ceo = self.provider.get_character(self.ceo_id)
|
||||
return self._ceo
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'ticker': self.ticker,
|
||||
'ceo_id': self.ceo_id,
|
||||
'members': self.members,
|
||||
'alliance_id': self.alliance_id
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dict):
|
||||
return cls(
|
||||
None,
|
||||
dict['id'],
|
||||
dict['name'],
|
||||
dict['ticker'],
|
||||
dict['ceo_id'],
|
||||
dict['members'],
|
||||
dict['alliance_id'],
|
||||
)
|
||||
|
||||
|
||||
class Alliance(Entity):
|
||||
def __init__(self, provider, id, name, ticker, corp_ids, executor_corp_id):
|
||||
super(Alliance, self).__init__(id, name)
|
||||
self.provider = provider
|
||||
self.ticker = ticker
|
||||
self.corp_ids = corp_ids
|
||||
self.executor_corp_id = executor_corp_id
|
||||
self._corps = {}
|
||||
|
||||
def corp(self, id):
|
||||
assert id in self.corp_ids
|
||||
if id not in self._corps:
|
||||
self._corps[id] = self.provider.get_corp(id)
|
||||
self._corps[id]._alliance = self
|
||||
return self._corps[id]
|
||||
|
||||
@property
|
||||
def corps(self):
|
||||
return sorted([self.corp(id) for id in self.corp_ids], key=lambda x: x.name)
|
||||
|
||||
@property
|
||||
def executor_corp(self):
|
||||
return self.corp(self.executor_corp_id)
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'ticker': self.ticker,
|
||||
'corp_ids': self.corp_ids,
|
||||
'executor_corp_id': self.executor_corp_id,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dict):
|
||||
return cls(
|
||||
None,
|
||||
dict['id'],
|
||||
dict['name'],
|
||||
dict['ticker'],
|
||||
dict['corp_ids'],
|
||||
dict['executor_corp_id'],
|
||||
)
|
||||
|
||||
|
||||
class Character(Entity):
|
||||
def __init__(self, provider, id, name, corp_id, alliance_id):
|
||||
super(Character, self).__init__(id, name)
|
||||
self.provider = provider
|
||||
self.corp_id = corp_id
|
||||
self.alliance_id = alliance_id
|
||||
self._corp = None
|
||||
self._alliance = None
|
||||
|
||||
@property
|
||||
def corp(self):
|
||||
if not self._corp:
|
||||
self._corp = self.provider.get_corp(self.corp_id)
|
||||
return self._corp
|
||||
|
||||
@property
|
||||
def alliance(self):
|
||||
if self.alliance_id:
|
||||
return self.corp.alliance
|
||||
return Entity(None, None)
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'corp_id': self.corp_id,
|
||||
'alliance_id': self.alliance_id,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dict):
|
||||
return cls(
|
||||
None,
|
||||
dict['id'],
|
||||
dict['name'],
|
||||
dict['corp_id'],
|
||||
dict['alliance_id'],
|
||||
)
|
||||
|
||||
|
||||
class ItemType(Entity):
|
||||
def __init__(self, provider, type_id, name):
|
||||
super(ItemType, self).__init__(type_id, name)
|
||||
self.provider = provider
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data_dict):
|
||||
return cls(
|
||||
None,
|
||||
data_dict['id'],
|
||||
data_dict['name'],
|
||||
)
|
||||
|
||||
|
||||
class EveProvider(object):
|
||||
def get_alliance(self, alliance_id):
|
||||
"""
|
||||
:return: an Alliance object for the given ID
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_corp(self, corp_id):
|
||||
"""
|
||||
:return: a Corporation object for the given ID
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_character(self, character_id):
|
||||
"""
|
||||
:return: a Character object for the given ID
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_itemtype(self, type_id):
|
||||
"""
|
||||
:return: an ItemType object for the given ID
|
||||
"""
|
||||
raise NotImplemented()
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EveSwaggerProvider(EveProvider):
|
||||
def __init__(self, token=None, adapter=None):
|
||||
self.client = esi_client_factory(token=token)
|
||||
self.adapter = adapter or self
|
||||
|
||||
def __str__(self):
|
||||
return 'esi'
|
||||
|
||||
def get_alliance(self, alliance_id):
|
||||
try:
|
||||
data = self.client.Alliance.get_alliances_alliance_id(alliance_id=alliance_id).result()
|
||||
corps = self.client.Alliance.get_alliances_alliance_id_corporations(alliance_id=alliance_id).result()
|
||||
model = Alliance(
|
||||
self.adapter,
|
||||
alliance_id,
|
||||
data['alliance_name'],
|
||||
data['ticker'],
|
||||
corps,
|
||||
data['executor_corp'],
|
||||
)
|
||||
return model
|
||||
except HTTPNotFound:
|
||||
raise ObjectNotFound(alliance_id, 'alliance')
|
||||
|
||||
def get_corp(self, corp_id):
|
||||
try:
|
||||
data = self.client.Corporation.get_corporations_corporation_id(corporation_id=corp_id).result()
|
||||
model = Corporation(
|
||||
self.adapter,
|
||||
corp_id,
|
||||
data['corporation_name'],
|
||||
data['ticker'],
|
||||
data['ceo_id'],
|
||||
data['member_count'],
|
||||
data['alliance_id'] if 'alliance_id' in data else None,
|
||||
)
|
||||
return model
|
||||
except HTTPNotFound:
|
||||
raise ObjectNotFound(id, 'corporation')
|
||||
|
||||
def get_character(self, character_id):
|
||||
try:
|
||||
data = self.client.Character.get_characters_character_id(character_id=character_id).result()
|
||||
alliance_id = self.adapter.get_corp(data['corporation_id']).alliance_id
|
||||
model = Character(
|
||||
self.adapter,
|
||||
character_id,
|
||||
data['name'],
|
||||
data['corporation_id'],
|
||||
alliance_id,
|
||||
)
|
||||
return model
|
||||
except (HTTPNotFound, HTTPUnprocessableEntity):
|
||||
raise ObjectNotFound(character_id, 'character')
|
||||
|
||||
def get_itemtype(self, type_id):
|
||||
try:
|
||||
data = self.client.Universe.get_universe_types_type_id(type_id=type_id).result()
|
||||
return ItemType(self.adapter, type_id, data['name'])
|
||||
except (HTTPNotFound, HTTPUnprocessableEntity):
|
||||
raise ObjectNotFound(type_id, 'type')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EveXmlProvider(EveProvider):
|
||||
def __init__(self, api_key=None, adapter=None):
|
||||
"""
|
||||
:param api_key: eveonline.EveApiKeyPair
|
||||
"""
|
||||
self.api = evelink.api.API(api_key=(api_key.api_id, api_key.api_key)) if api_key else evelink.api.API()
|
||||
self.adapter = adapter or self
|
||||
|
||||
def __str__(self):
|
||||
return 'xml'
|
||||
|
||||
def get_alliance(self, id):
|
||||
api = evelink.eve.EVE(api=self.api)
|
||||
alliances = api.alliances().result
|
||||
try:
|
||||
results = alliances[int(id)]
|
||||
except KeyError:
|
||||
raise ObjectNotFound(id, 'alliance')
|
||||
model = Alliance(
|
||||
self.adapter,
|
||||
id,
|
||||
results['name'],
|
||||
results['ticker'],
|
||||
results['member_corps'],
|
||||
results['executor_id'],
|
||||
)
|
||||
return model
|
||||
|
||||
def get_corp(self, id):
|
||||
api = evelink.corp.Corp(api=self.api)
|
||||
try:
|
||||
corpinfo = api.corporation_sheet(corp_id=int(id)).result
|
||||
except evelink.api.APIError as e:
|
||||
if int(e.code) == 523:
|
||||
raise ObjectNotFound(id, 'corporation')
|
||||
raise e
|
||||
model = Corporation(
|
||||
self.adapter,
|
||||
id,
|
||||
corpinfo['name'],
|
||||
corpinfo['ceo']['id'],
|
||||
corpinfo['members']['current'],
|
||||
corpinfo['ticker'],
|
||||
corpinfo['alliance']['id'] if corpinfo['alliance'] else None,
|
||||
)
|
||||
return model
|
||||
|
||||
def _build_character(self, result):
|
||||
return Character(
|
||||
self.adapter,
|
||||
result['id'],
|
||||
result['name'],
|
||||
result['corp']['id'],
|
||||
result['alliance']['id'],
|
||||
)
|
||||
|
||||
def get_character(self, id):
|
||||
api = evelink.eve.EVE(api=self.api)
|
||||
try:
|
||||
charinfo = api.character_info_from_id(id).result
|
||||
except evelink.api.APIError as e:
|
||||
if int(e.code) == 105:
|
||||
raise ObjectNotFound(id, 'character')
|
||||
raise e
|
||||
return self._build_character(charinfo)
|
||||
|
||||
def get_itemtype(self, type_id):
|
||||
api = evelink.eve.EVE(api=self.api)
|
||||
try:
|
||||
type_name = api.type_name_from_id(type_id).result
|
||||
assert type_name != 'Unknown Type'
|
||||
return ItemType(self.adapter, type_id, type_name)
|
||||
except AssertionError:
|
||||
raise ObjectNotFound(type_id, 'itemtype')
|
||||
|
||||
|
||||
class EveAdapter(EveProvider):
|
||||
"""
|
||||
Redirects queries to appropriate data source.
|
||||
"""
|
||||
|
||||
def __init__(self, char_provider, corp_provider, alliance_provider, itemtype_provider):
|
||||
self.char_provider = char_provider
|
||||
self.corp_provider = corp_provider
|
||||
self.alliance_provider = alliance_provider
|
||||
self.itemtype_provider = itemtype_provider
|
||||
self.char_provider.adapter = self
|
||||
self.corp_provider.adapter = self
|
||||
self.alliance_provider.adapter = self
|
||||
self.itemtype_provider.adapter = self
|
||||
|
||||
def __repr__(self):
|
||||
return "<{} (character:{} corp:{} alliance:{} itemtype:{})>".format(self.__class__.__name__,
|
||||
str(self.char_provider),
|
||||
str(self.corp_provider),
|
||||
str(self.alliance_provider),
|
||||
str(self.itemtype_provider))
|
||||
|
||||
@staticmethod
|
||||
def _get_from_cache(obj_class, id):
|
||||
data = cache.get('%s__%s' % (obj_class.__name__.lower(), id))
|
||||
if data:
|
||||
obj = obj_class.from_dict(json.loads(data))
|
||||
logger.debug('Got from cache: %s' % obj.__repr__())
|
||||
return obj
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _cache(obj):
|
||||
logger.debug('Caching: %s ' % obj.__repr__())
|
||||
cache.set('%s__%s' % (obj.__class__.__name__.lower(), obj.id), json.dumps(obj.serialize()),
|
||||
int(OBJ_CACHE_DURATION))
|
||||
|
||||
def get_character(self, id):
|
||||
obj = self._get_from_cache(Character, id)
|
||||
if obj:
|
||||
obj.provider = self
|
||||
else:
|
||||
obj = self._get_character(id)
|
||||
self._cache(obj)
|
||||
return obj
|
||||
|
||||
def get_corp(self, id):
|
||||
obj = self._get_from_cache(Corporation, id)
|
||||
if obj:
|
||||
obj.provider = self
|
||||
else:
|
||||
obj = self._get_corp(id)
|
||||
self._cache(obj)
|
||||
return obj
|
||||
|
||||
def get_alliance(self, id):
|
||||
obj = self._get_from_cache(Alliance, id)
|
||||
if obj:
|
||||
obj.provider = self
|
||||
else:
|
||||
obj = self._get_alliance(id)
|
||||
self._cache(obj)
|
||||
return obj
|
||||
|
||||
def get_itemtype(self, type_id):
|
||||
obj = self._get_from_cache(ItemType, type_id)
|
||||
if obj:
|
||||
obj.provider = self
|
||||
else:
|
||||
obj = self._get_itemtype(type_id)
|
||||
self._cache(obj)
|
||||
return obj
|
||||
|
||||
def _get_character(self, id):
|
||||
return self.char_provider.get_character(id)
|
||||
|
||||
def _get_corp(self, id):
|
||||
return self.corp_provider.get_corp(id)
|
||||
|
||||
def _get_alliance(self, id):
|
||||
return self.alliance_provider.get_alliance(id)
|
||||
|
||||
def _get_itemtype(self, type_id):
|
||||
return self.itemtype_provider.get_itemtype(type_id)
|
||||
|
||||
|
||||
CHARACTER_PROVIDER = getattr(settings, 'EVEONLINE_CHARACTER_PROVIDER', '') or 'esi'
|
||||
CORP_PROVIDER = getattr(settings, 'EVEONLINE_CORP_PROVIDER', '') or 'esi'
|
||||
ALLIANCE_PROVIDER = getattr(settings, 'EVEONLINE_ALLIANCE_PROVIDER', '') or 'esi'
|
||||
ITEMTYPE_PROVIDER = getattr(settings, 'EVEONLINE_ITEMTYPE_PROVIDER', '') or 'esi'
|
||||
|
||||
|
||||
def eve_adapter_factory(character_source=CHARACTER_PROVIDER, corp_source=CORP_PROVIDER,
|
||||
alliance_source=ALLIANCE_PROVIDER, itemtype_source=ITEMTYPE_PROVIDER, api_key=None, token=None):
|
||||
sources = [character_source, corp_source, alliance_source, itemtype_source]
|
||||
providers = []
|
||||
|
||||
if 'xml' in sources:
|
||||
xml = EveXmlProvider(api_key=api_key)
|
||||
if 'esi' in sources:
|
||||
esi = EveSwaggerProvider(token=token)
|
||||
|
||||
for source in sources:
|
||||
if source == 'xml':
|
||||
providers.append(xml)
|
||||
elif source == 'esi':
|
||||
providers.append(esi)
|
||||
else:
|
||||
raise ValueError('Unrecognized data source "%s"' % source)
|
||||
return EveAdapter(providers[0], providers[1], providers[2], providers[3])
|
||||
@@ -26,21 +26,16 @@ def refresh_api(api):
|
||||
try:
|
||||
EveApiManager.validate_api(api.api_id, api.api_key, api.user)
|
||||
# Update characters
|
||||
characters = EveApiManager.get_characters_from_api(api.api_id, api.api_key)
|
||||
EveManager.update_characters_from_list(characters)
|
||||
new_character = False
|
||||
for char in characters.result:
|
||||
# Ensure we have a model for all characters on key
|
||||
if not EveManager.check_if_character_exist(characters.result[char]['name']):
|
||||
logger.debug(
|
||||
"API key %s has a new character on the account: %s" % (api.api_id, characters.result[char]['name']))
|
||||
new_character = True
|
||||
if new_character:
|
||||
logger.debug("Creating new character %s from api key %s" % (characters.result[char]['name'], api.api_id))
|
||||
EveManager.create_characters_from_list(characters, api.user, api.api_id)
|
||||
characters = EveManager.get_characters_from_api(api)
|
||||
for c in characters:
|
||||
try:
|
||||
EveManager.update_character_obj(c)
|
||||
except EveCharacter.DoesNotExist:
|
||||
logger.debug("API key %s has a new character on the account: %s" % (api.api_id, c))
|
||||
EveManager.create_character_obj(c, api.user, api.api_id)
|
||||
current_chars = EveCharacter.objects.filter(api_id=api.api_id)
|
||||
for c in current_chars:
|
||||
if not int(c.character_id) in characters.result:
|
||||
if not int(c.character_id) in [c.id for c in characters]:
|
||||
logger.info("Character %s no longer found on API ID %s" % (c, api.api_id))
|
||||
c.delete()
|
||||
except evelink.api.APIError as e:
|
||||
@@ -81,7 +76,7 @@ def refresh_user_apis(user):
|
||||
for x in apis:
|
||||
refresh_api(x)
|
||||
# Check our main character
|
||||
auth = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
if auth.main_char_id:
|
||||
if EveCharacter.objects.filter(character_id=auth.main_char_id).exists() is False:
|
||||
logger.info(
|
||||
@@ -105,153 +100,53 @@ def run_api_refresh():
|
||||
refresh_user_apis.delay(u)
|
||||
|
||||
|
||||
def populate_alliance(id, blue=False):
|
||||
logger.debug("Populating alliance model with id %s blue %s" % (id, blue))
|
||||
alliance_info = EveApiManager.get_alliance_information(id)
|
||||
|
||||
if not alliance_info:
|
||||
raise ValueError("Supplied alliance id %s is invalid" % id)
|
||||
|
||||
if not EveAllianceInfo.objects.filter(alliance_id=id).exists():
|
||||
EveManager.create_alliance_info(alliance_info['id'], alliance_info['name'], alliance_info['ticker'],
|
||||
alliance_info['executor_id'], alliance_info['member_count'], blue)
|
||||
alliance = EveAllianceInfo.objects.get(alliance_id=id)
|
||||
for member_corp in alliance_info['member_corps']:
|
||||
if EveCorporationInfo.objects.filter(corporation_id=member_corp).exists():
|
||||
corp = EveCorporationInfo.objects.get(corporation_id=member_corp)
|
||||
if corp.alliance != alliance:
|
||||
corp.alliance = alliance
|
||||
corp.save()
|
||||
else:
|
||||
logger.info("Creating new alliance member corp id %s" % member_corp)
|
||||
corpinfo = EveApiManager.get_corporation_information(member_corp)
|
||||
EveManager.create_corporation_info(corpinfo['id'], corpinfo['name'], corpinfo['ticker'],
|
||||
corpinfo['members']['current'], blue, alliance)
|
||||
|
||||
@task
|
||||
def update_corp(id):
|
||||
EveManager.update_corporation(id)
|
||||
|
||||
@task
|
||||
def update_alliance(id):
|
||||
alliance = EveAllianceInfo.objects.get(alliance_id=id)
|
||||
corps = EveCorporationInfo.objects.filter(alliance=alliance)
|
||||
logger.debug("Updating alliance %s with %s member corps" % (alliance, len(corps)))
|
||||
allianceinfo = EveApiManager.get_alliance_information(alliance.alliance_id)
|
||||
if allianceinfo:
|
||||
EveManager.update_alliance_info(allianceinfo['id'], allianceinfo['executor_id'],
|
||||
allianceinfo['member_count'], alliance.is_blue)
|
||||
for corp in corps:
|
||||
if corp.corporation_id in allianceinfo['member_corps'] is False:
|
||||
logger.info("Corp %s no longer in alliance %s" % (corp, alliance))
|
||||
corp.alliance = None
|
||||
corp.save()
|
||||
populate_alliance(alliance.alliance_id, blue=alliance.is_blue)
|
||||
elif EveApiManager.check_if_alliance_exists(alliance.alliance_id) is False:
|
||||
logger.info("Alliance %s has closed. Deleting model" % alliance)
|
||||
alliance.delete()
|
||||
|
||||
|
||||
@task
|
||||
def update_corp(id):
|
||||
corp = EveCorporationInfo.objects.get(corporation_id=id)
|
||||
logger.debug("Updating corp %s" % corp)
|
||||
corpinfo = EveApiManager.get_corporation_information(corp.corporation_id)
|
||||
if corpinfo:
|
||||
alliance = None
|
||||
if EveAllianceInfo.objects.filter(alliance_id=corpinfo['alliance']['id']).exists():
|
||||
alliance = EveAllianceInfo.objects.get(alliance_id=corpinfo['alliance']['id'])
|
||||
EveManager.update_corporation_info(corpinfo['id'], corpinfo['members']['current'], alliance, corp.is_blue)
|
||||
elif EveApiManager.check_if_corp_exists(corp.corporation_id) is False:
|
||||
logger.info("Corp %s has closed. Deleting model" % corp)
|
||||
corp.delete()
|
||||
|
||||
# Run Every 2 hours
|
||||
EveManager.update_alliance(id)
|
||||
EveManager.populate_alliance(id)
|
||||
|
||||
|
||||
@periodic_task(run_every=crontab(minute=0, hour="*/2"))
|
||||
def run_corp_update():
|
||||
if EveApiManager.check_if_api_server_online() is False:
|
||||
if not EveApiManager.check_if_api_server_online():
|
||||
logger.warn("Aborted updating corp and alliance models: API server unreachable")
|
||||
return
|
||||
standing_level = 'alliance'
|
||||
|
||||
# generate member corps
|
||||
for corp_id in settings.STR_CORP_IDS:
|
||||
if EveCorporationInfo.objects.filter(corporation_id=corp_id).exists():
|
||||
update_corp(corp_id)
|
||||
else:
|
||||
EveManager.create_corporation(corp_id)
|
||||
|
||||
# generate member alliances
|
||||
for alliance_id in settings.STR_ALLIANCE_IDS:
|
||||
if EveAllianceInfo.objects.filter(alliance_id=alliance_id).exists():
|
||||
logger.debug("Updating existing owner alliance model with id %s" % alliance_id)
|
||||
update_alliance(alliance_id)
|
||||
else:
|
||||
EveManager.create_alliance(alliance_id)
|
||||
EveManager.populate_alliance(alliance_id)
|
||||
|
||||
# update existing corp models
|
||||
for corp in EveCorporationInfo.objects.all():
|
||||
update_corp.delay(corp.corporation_id)
|
||||
|
||||
# update existing alliance models
|
||||
for alliance in EveAllianceInfo.objects.all():
|
||||
update_alliance.delay(alliance.alliance_id)
|
||||
|
||||
try:
|
||||
# get corp info for owning corp if required
|
||||
ownercorpinfo = {}
|
||||
if settings.IS_CORP:
|
||||
standing_level = 'corp'
|
||||
logger.debug("Getting information for owning corp with id %s" % settings.CORP_ID)
|
||||
ownercorpinfo = EveApiManager.get_corporation_information(settings.CORP_ID)
|
||||
if not ownercorpinfo:
|
||||
logger.error("Failed to retrieve corp info for owning corp id %s - bad corp id?" % settings.CORP_ID)
|
||||
return
|
||||
|
||||
# check if we need to update an alliance model
|
||||
alliance_id = ''
|
||||
if ownercorpinfo and ownercorpinfo['alliance']['id']:
|
||||
alliance_id = ownercorpinfo['alliance']['id']
|
||||
elif settings.IS_CORP is False:
|
||||
alliance_id = settings.ALLIANCE_ID
|
||||
|
||||
# get and create alliance info for owning alliance if required
|
||||
alliance = None
|
||||
if alliance_id:
|
||||
logger.debug("Getting information for owning alliance with id %s" % alliance_id)
|
||||
ownerallianceinfo = EveApiManager.get_alliance_information(alliance_id)
|
||||
if not ownerallianceinfo:
|
||||
logger.error("Failed to retrieve corp info for owning alliance id %s - bad alliance id?" % alliance_id)
|
||||
return
|
||||
if EveAllianceInfo.objects.filter(alliance_id=ownerallianceinfo['id']).exists():
|
||||
logger.debug("Updating existing owner alliance model with id %s" % alliance_id)
|
||||
EveManager.update_alliance_info(ownerallianceinfo['id'], ownerallianceinfo['executor_id'],
|
||||
ownerallianceinfo['member_count'], False)
|
||||
else:
|
||||
populate_alliance(alliance_id)
|
||||
alliance = EveAllianceInfo.objects.get(alliance_id=alliance_id)
|
||||
|
||||
# create corp info for owning corp if required
|
||||
if ownercorpinfo:
|
||||
if EveCorporationInfo.objects.filter(corporation_id=ownercorpinfo['id']).exists():
|
||||
logger.debug("Updating existing owner corp model with id %s" % ownercorpinfo['id'])
|
||||
EveManager.update_corporation_info(ownercorpinfo['id'], ownercorpinfo['members']['current'], alliance,
|
||||
False)
|
||||
else:
|
||||
logger.info("Creating model for owning corp with id %s" % ownercorpinfo['id'])
|
||||
EveManager.create_corporation_info(ownercorpinfo['id'], ownercorpinfo['name'], ownercorpinfo['ticker'],
|
||||
ownercorpinfo['members']['current'], False, alliance)
|
||||
|
||||
# validate and create corp models for member corps of owning alliance
|
||||
if alliance:
|
||||
current_corps = EveCorporationInfo.objects.filter(alliance=alliance)
|
||||
for corp in current_corps:
|
||||
if corp.corporation_id in ownerallianceinfo['member_corps'] is False:
|
||||
logger.info("Corp %s is no longer in owning alliance %s - updating model." % (corp, alliance))
|
||||
corp.alliance = None
|
||||
corp.save()
|
||||
for member_corp in ownerallianceinfo['member_corps']:
|
||||
if EveCorporationInfo.objects.filter(corporation_id=member_corp).exists():
|
||||
corp = EveCorporationInfo.objects.get(corporation_id=member_corp)
|
||||
if corp.alliance == alliance is not True:
|
||||
logger.info("Associating corp %s with owning alliance %s" % (corp, alliance))
|
||||
corp.alliance = alliance
|
||||
corp.save()
|
||||
else:
|
||||
corpinfo = EveApiManager.get_corporation_information(member_corp)
|
||||
logger.info("Creating model for owning alliance member corp with id %s" % corpinfo['id'])
|
||||
EveManager.create_corporation_info(corpinfo['id'], corpinfo['name'], corpinfo['ticker'],
|
||||
corpinfo['members']['current'], False, alliance)
|
||||
|
||||
# update existing corp models
|
||||
for corp in EveCorporationInfo.objects.all():
|
||||
update_corp.delay(corp.corporation_id)
|
||||
|
||||
# update existing alliance models
|
||||
for alliance in EveAllianceInfo.objects.all():
|
||||
update_alliance.delay(alliance.alliance_id)
|
||||
|
||||
# create standings
|
||||
standings = EveApiManager.get_corp_standings()
|
||||
if standings:
|
||||
standings = standings[standing_level]
|
||||
standings = standings[settings.STANDING_LEVEL]
|
||||
for standing in standings:
|
||||
if int(standings[standing]['standing']) >= settings.BLUE_STANDING:
|
||||
if float(standings[standing]['standing']) >= settings.BLUE_STANDING:
|
||||
logger.debug("Standing %s meets threshold" % standing)
|
||||
if EveApiManager.check_if_id_is_alliance(standing):
|
||||
logger.debug("Standing %s is an alliance" % standing)
|
||||
@@ -262,7 +157,7 @@ def run_corp_update():
|
||||
alliance.is_blue = True
|
||||
alliance.save()
|
||||
else:
|
||||
populate_alliance(standing, blue=True)
|
||||
EveManager.create_alliance(standing, is_blue=True)
|
||||
elif EveApiManager.check_if_id_is_corp(standing):
|
||||
logger.debug("Standing %s is a corp" % standing)
|
||||
if EveCorporationInfo.objects.filter(corporation_id=standing).exists():
|
||||
@@ -273,13 +168,7 @@ def run_corp_update():
|
||||
corp.save()
|
||||
else:
|
||||
logger.info("Creating model for blue corp with id %s" % standing)
|
||||
corpinfo = EveApiManager.get_corporation_information(standing)
|
||||
corp_alliance = None
|
||||
if EveAllianceInfo.objects.filter(alliance_id=corpinfo['alliance']['id']).exists():
|
||||
logger.debug("New corp model for standing %s has existing alliance model" % standing)
|
||||
corp_alliance = EveAllianceInfo.objects.get(alliance_id=corpinfo['alliance']['id'])
|
||||
EveManager.create_corporation_info(corpinfo['id'], corpinfo['name'], corpinfo['ticker'],
|
||||
corpinfo['members']['current'], True, corp_alliance)
|
||||
EveManager.create_corporation(standing, is_blue=True)
|
||||
|
||||
# update alliance standings
|
||||
for alliance in EveAllianceInfo.objects.filter(is_blue=True):
|
||||
@@ -310,43 +199,26 @@ def run_corp_update():
|
||||
logger.info("Corp %s is no longer blue" % corp)
|
||||
corp.is_blue = False
|
||||
corp.save()
|
||||
except evelink.api.APIError as e:
|
||||
logger.error("Model update failed with error code %s" % e.code)
|
||||
|
||||
# delete unnecessary alliance models
|
||||
for alliance in EveAllianceInfo.objects.filter(is_blue=False):
|
||||
logger.debug("Checking to delete alliance %s" % alliance)
|
||||
if not settings.IS_CORP:
|
||||
if not alliance.alliance_id == settings.ALLIANCE_ID:
|
||||
logger.info("Deleting unnecessary alliance model %s" % alliance)
|
||||
alliance.delete()
|
||||
else:
|
||||
if not alliance.evecorporationinfo_set.filter(corporation_id=settings.CORP_ID).exists():
|
||||
logger.info("Deleting unnecessary alliance model %s" % alliance)
|
||||
alliance.delete()
|
||||
# delete unnecessary alliance models
|
||||
for alliance in EveAllianceInfo.objects.filter(is_blue=False):
|
||||
logger.debug("Checking to delete alliance %s" % alliance)
|
||||
if not alliance.alliance_id in settings.STR_ALLIANCE_IDS:
|
||||
logger.info("Deleting unnecessary alliance model %s" % alliance)
|
||||
alliance.delete()
|
||||
|
||||
# delete unnecessary corp models
|
||||
for corp in EveCorporationInfo.objects.filter(is_blue=False):
|
||||
logger.debug("Checking to delete corp %s" % corp)
|
||||
if not settings.IS_CORP:
|
||||
if corp.alliance:
|
||||
logger.debug("Corp %s has alliance %s" % (corp, corp.alliance))
|
||||
if not corp.alliance.alliance_id == settings.ALLIANCE_ID:
|
||||
logger.info("Deleting unnecessary corp model %s" % corp)
|
||||
corp.delete()
|
||||
else:
|
||||
# delete unnecessary corp models
|
||||
for corp in EveCorporationInfo.objects.filter(is_blue=False):
|
||||
logger.debug("Checking to delete corp %s" % corp)
|
||||
if not corp.corporation_id in settings.STR_CORP_IDS:
|
||||
logger.debug("Corp %s is not member corp" % corp)
|
||||
if corp.alliance:
|
||||
logger.debug("Corp %s has alliance %s" % (corp, corp.alliance))
|
||||
if not corp.alliance.alliance_id in settings.STR_ALLIANCE_IDS:
|
||||
logger.info("Deleting unnecessary corp model %s" % corp)
|
||||
corp.delete()
|
||||
else:
|
||||
if corp.corporation_id != settings.CORP_ID:
|
||||
logger.debug("Corp %s is not owning corp" % corp)
|
||||
if corp.alliance:
|
||||
logger.debug("Corp %s has alliance %s" % (corp, corp.alliance))
|
||||
if not corp.alliance.evecorporationinfo_set.filter(corporation_id=settings.CORP_ID).exists():
|
||||
logger.info("Deleting unnecessary corp model %s" % corp)
|
||||
corp.delete()
|
||||
else:
|
||||
logger.info("Deleting unnecessary corp model %s" % corp)
|
||||
corp.delete()
|
||||
else:
|
||||
logger.debug("Corp %s is owning corp" % corp)
|
||||
except evelink.api.APIError as e:
|
||||
logger.error("Model update failed with error code %s" % e.code)
|
||||
logger.info("Deleting unnecessary corp model %s" % corp)
|
||||
corp.delete()
|
||||
|
||||
@@ -2,7 +2,6 @@ from __future__ import unicode_literals
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
|
||||
from eveonline.forms import UpdateKeyForm
|
||||
from eveonline.managers import EveManager
|
||||
from authentication.managers import AuthServicesInfoManager
|
||||
@@ -11,8 +10,7 @@ from eveonline.models import EveApiKeyPair, EveCharacter
|
||||
from authentication.models import AuthServicesInfo
|
||||
from authentication.tasks import set_state
|
||||
from eveonline.tasks import refresh_api
|
||||
|
||||
from eve_sso.decorators import token_required
|
||||
from esi.decorators import token_required
|
||||
from django.conf import settings
|
||||
import logging
|
||||
|
||||
@@ -30,7 +28,7 @@ def add_api_key(request):
|
||||
api_key=form.cleaned_data['api_key']).exists():
|
||||
# allow orphaned keys to proceed to SSO validation upon re-entry
|
||||
api_key = EveApiKeyPair.objects.get(api_id=form.cleaned_data['api_id'],
|
||||
api_key=form.cleaned_data['api_key'])
|
||||
api_key=form.cleaned_data['api_key'])
|
||||
elif EveApiKeyPair.objects.filter(api_id=form.cleaned_data['api_id']).exists():
|
||||
logger.warn('API %s re-added with different vcode.' % form.cleaned_data['api_id'])
|
||||
EveApiKeyPair.objects.filter(api_id=form.cleaned_data['api_id']).delete()
|
||||
@@ -46,19 +44,19 @@ def add_api_key(request):
|
||||
api_key.save()
|
||||
owner = request.user
|
||||
# Grab characters associated with the key pair
|
||||
characters = EveApiManager.get_characters_from_api(form.cleaned_data['api_id'],
|
||||
form.cleaned_data['api_key'])
|
||||
EveManager.create_characters_from_list(characters, owner, form.cleaned_data['api_id'])
|
||||
characters = EveManager.get_characters_from_api(api_key)
|
||||
[EveManager.create_character_obj(c, owner, api_key.api_id) for c in characters if
|
||||
not EveCharacter.objects.filter(character_id=c.id).exists()]
|
||||
logger.info("Successfully processed api add form for user %s" % request.user)
|
||||
if not settings.API_SSO_VALIDATION:
|
||||
messages.success(request, 'Added API key %s to your account.' % form.cleaned_data['api_id'])
|
||||
auth = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
auth = AuthServicesInfo.objects.get(user=request.user)
|
||||
if not auth.main_char_id:
|
||||
return redirect('auth_characters')
|
||||
return redirect("/api_key_management/")
|
||||
return redirect("auth_dashboard")
|
||||
else:
|
||||
logger.debug('Requesting SSO validation of API %s by user %s' % (api_key.api_id, request.user))
|
||||
return render(request, 'registered/apisso.html', context={'api':api_key})
|
||||
return render(request, 'registered/apisso.html', context={'api': api_key})
|
||||
else:
|
||||
logger.debug("Form invalid: returning to form.")
|
||||
else:
|
||||
@@ -70,18 +68,17 @@ def add_api_key(request):
|
||||
|
||||
@login_required
|
||||
@token_required(new=True)
|
||||
def api_sso_validate(request, tokens, api_id):
|
||||
def api_sso_validate(request, token, api_id):
|
||||
logger.debug('api_sso_validate called by user %s for api %s' % (request.user, api_id))
|
||||
api = get_object_or_404(EveApiKeyPair, api_id=api_id)
|
||||
if api.user and api.user != request.user:
|
||||
logger.warning('User %s attempting to take ownership of api %s from %s' % (request.user, api_id, api.user))
|
||||
messages.warning(request, 'API %s already claimed by user %s' % (api_id, api.user))
|
||||
return redirect('auth_api_key_management')
|
||||
return redirect('auth_dashboard')
|
||||
elif api.sso_verified:
|
||||
logger.debug('API %s has already been verified.' % api_id)
|
||||
messages.info(request, 'API %s has already been verified' % api_id)
|
||||
return redirect('auth_api_key_management')
|
||||
token = tokens[0]
|
||||
return redirect('auth_dashboard')
|
||||
logger.debug('API %s has not been verified. Checking if token for %s matches.' % (api_id, token.character_name))
|
||||
characters = EveApiManager.get_characters_from_api(api.api_id, api.api_key).result
|
||||
if token.character_id in characters:
|
||||
@@ -90,59 +87,72 @@ def api_sso_validate(request, tokens, api_id):
|
||||
api.save()
|
||||
EveCharacter.objects.filter(character_id__in=characters).update(user=request.user, api_id=api_id)
|
||||
messages.success(request, 'Confirmed ownership of API %s' % api.api_id)
|
||||
auth, c = AuthServicesInfo.objects.get_or_create(user=request.user)
|
||||
auth = AuthServicesInfo.objects.get(user=request.user)
|
||||
if not auth.main_char_id:
|
||||
return redirect('auth_characters')
|
||||
return redirect('auth_api_key_management')
|
||||
return redirect('auth_dashboard')
|
||||
else:
|
||||
messages.warning(request, '%s not found on API %s. Please SSO as a character on the API.' % (token.character_name, api.api_id))
|
||||
return render(request, 'registered/apisso.html', context={'api':api})
|
||||
messages.warning(request, '%s not found on API %s. Please SSO as a character on the API.' % (
|
||||
token.character_name, api.api_id))
|
||||
return render(request, 'registered/apisso.html', context={'api': api})
|
||||
|
||||
|
||||
@login_required
|
||||
def api_key_management_view(request):
|
||||
logger.debug("api_key_management_view called by user %s" % request.user)
|
||||
context = {
|
||||
'apikeypairs': EveManager.get_api_key_pairs(request.user.id),
|
||||
'api_sso_validation': settings.API_SSO_VALIDATION or False
|
||||
}
|
||||
def dashboard_view(request):
|
||||
logger.debug("dashboard_view called by user %s" % request.user)
|
||||
auth_info = AuthServicesInfo.objects.get(user=request.user)
|
||||
apikeypairs = EveManager.get_api_key_pairs(request.user.id)
|
||||
sso_validation = settings.API_SSO_VALIDATION or False
|
||||
api_chars = []
|
||||
|
||||
return render(request, 'registered/apikeymanagment.html', context=context)
|
||||
if apikeypairs:
|
||||
for api in apikeypairs:
|
||||
api_chars.append({
|
||||
'id': api.api_id,
|
||||
'sso_verified': api.sso_verified if sso_validation else True,
|
||||
'characters': EveCharacter.objects.filter(api_id=api.api_id),
|
||||
})
|
||||
|
||||
context = {
|
||||
'main': EveManager.get_character_by_id(auth_info.main_char_id),
|
||||
'apis': api_chars,
|
||||
'api_sso_validation': settings.API_SSO_VALIDATION or False,
|
||||
}
|
||||
return render(request, 'registered/dashboard.html', context=context)
|
||||
|
||||
|
||||
@login_required
|
||||
def api_key_removal(request, api_id):
|
||||
logger.debug("api_key_removal called by user %s for api id %s" % (request.user, api_id))
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
EveManager.delete_api_key_pair(api_id, request.user.id)
|
||||
EveManager.delete_characters_by_api_id(api_id, request.user.id)
|
||||
messages.success(request, 'Deleted API key %s' % api_id)
|
||||
logger.info("Succesfully processed api delete request by user %s for api %s" % (request.user, api_id))
|
||||
if EveCharacter.objects.filter(character_id=authinfo.main_char_id).exists():
|
||||
return redirect("auth_api_key_management")
|
||||
else:
|
||||
if not EveCharacter.objects.filter(character_id=authinfo.main_char_id).exists():
|
||||
authinfo.main_char_id = None
|
||||
authinfo.save()
|
||||
set_state(request.user)
|
||||
return redirect("auth_characters")
|
||||
return redirect("auth_dashboard")
|
||||
|
||||
|
||||
@login_required
|
||||
def characters_view(request):
|
||||
logger.debug("characters_view called by user %s" % request.user)
|
||||
render_items = {'characters': EveManager.get_characters_by_owner_id(request.user.id),
|
||||
'authinfo': AuthServicesInfo.objects.get_or_create(user=request.user)[0]}
|
||||
render_items = {'characters': EveCharacter.objects.filter(user=request.user),
|
||||
'authinfo': AuthServicesInfo.objects.get(user=request.user)}
|
||||
return render(request, 'registered/characters.html', context=render_items)
|
||||
|
||||
|
||||
@login_required
|
||||
def main_character_change(request, char_id):
|
||||
logger.debug("main_character_change called by user %s for character id %s" % (request.user, char_id))
|
||||
if EveManager.check_if_character_owned_by_user(char_id, request.user):
|
||||
if EveCharacter.objects.filter(character_id=char_id).exists() and EveCharacter.objects.get(
|
||||
character_id=char_id).user == request.user:
|
||||
AuthServicesInfoManager.update_main_char_id(char_id, request.user)
|
||||
messages.success(request, 'Changed main character ID to %s' % char_id)
|
||||
set_state(request.user)
|
||||
return redirect("auth_characters")
|
||||
return redirect("auth_dashboard")
|
||||
messages.error(request, 'Failed to change main character - selected character is not owned by your account.')
|
||||
return redirect("auth_characters")
|
||||
|
||||
@@ -162,4 +172,4 @@ def user_refresh_api(request, api_id):
|
||||
else:
|
||||
messages.warning(request, 'Unable to locate API key %s' % api_id)
|
||||
logger.warn("User %s unable to refresh api id %s - api key not found" % (request.user, api_id))
|
||||
return redirect("auth_api_key_management")
|
||||
return redirect("auth_dashboard")
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{% extends 'public/base.html' %}
|
||||
{% block title %}Fleet Participation{% endblock %}
|
||||
{% block page_title %}Fleet Participation{% endblock %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">Character not found!</h1>
|
||||
<div class="col-lg-12 container" id="example">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">{{ character_name }}</div>
|
||||
<div class="panel-body">
|
||||
<div class="col-lg-2 col-sm-2">
|
||||
<img class="ra-avatar img-responsive" src="https://image.eveonline.com/Character/{{ character_id }}_128.jpg">
|
||||
</div>
|
||||
<div class="col-lg-10 col-sm-2">
|
||||
<div class="alert alert-danger" role="alert">Character not registered!</div>
|
||||
This character is not part of any registered API-key. You must go to <a href=" {% url 'auth_api_key_management' %}">API key management</a> and add an API with the character on before being able to click fleet attendance links.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,63 @@
|
||||
{% extends "public/base.html" %}
|
||||
{% load bootstrap %}
|
||||
{% load staticfiles %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap_pagination %}
|
||||
{% block title %}Alliance Auth{% endblock %}
|
||||
{% block page_title %}{% trans "Fatlink view" %}{% endblock page_title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header text-center">{% blocktrans %}Edit fatlink "{{ fatlink }}"{% endblocktrans %}
|
||||
<div class="text-right">
|
||||
<form>
|
||||
<button type="submit" onclick="return confirm('Are you sure?')" class="btn btn-danger" name="deletefat" value="True">
|
||||
{% trans "Delete fat" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</h1>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">{% trans "Registered characters" %}</div>
|
||||
<div class="panel-body">
|
||||
<div class="text-center">
|
||||
{% bootstrap_paginate registered_fats range=10 %}
|
||||
</div>
|
||||
<table class="table table-responsive table-hover">
|
||||
<tr>
|
||||
<th class="text-center">{% trans "User" %}</th>
|
||||
<th class="text-center">{% trans "Character" %}</th>
|
||||
<th class="text-center">{% trans "System" %}</th>
|
||||
<th class="text-center">{% trans "Ship" %}</th>
|
||||
<th class="text-center">{% trans "Eve Time" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
{% for fat in registered_fats %}
|
||||
<tr>
|
||||
<td class="text-center">{{ fat.user }}</td>
|
||||
<td class="text-center">{{ fat.character.character_name }}</td>
|
||||
{% if fat.station != "No Station" %}
|
||||
<td class="text-center">Docked in {{ fat.system }}</td>
|
||||
{% else %}
|
||||
<td class="text-center">{{ fat.system }}</td>
|
||||
{% endif %}
|
||||
<td class="text-center">{{ fat.shiptype }}</td>
|
||||
<td class="text-center">{{ fat.fatlink.fatdatetime }}</td>
|
||||
<td class="text-center">
|
||||
<form>
|
||||
<button type="submit" class="btn btn-warning" name="removechar" value="{{ fat.character.character_id }}">
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="/static/js/dateformat.js"></script>
|
||||
|
||||
{% endblock content %}
|
||||
@@ -6,16 +6,17 @@ from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils import timezone
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from eveonline.models import EveCharacter
|
||||
from eveonline.models import EveCorporationInfo
|
||||
from eveonline.managers import EveManager
|
||||
from fleetactivitytracking.forms import FatlinkForm
|
||||
from fleetactivitytracking.models import Fatlink, Fat
|
||||
|
||||
from slugify import slugify
|
||||
from esi.decorators import token_required
|
||||
|
||||
from collections import OrderedDict
|
||||
from slugify import slugify
|
||||
|
||||
import string
|
||||
import random
|
||||
@@ -25,15 +26,29 @@ import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
FATS_PER_PAGE = int(getattr(settings, 'FATS_PER_PAGE', 20))
|
||||
|
||||
|
||||
def get_page(model_list, page_num):
|
||||
p = Paginator(model_list, FATS_PER_PAGE)
|
||||
try:
|
||||
fats = p.page(page_num)
|
||||
except PageNotAnInteger:
|
||||
fats = p.page(1)
|
||||
except EmptyPage:
|
||||
fats = p.page(p.num_pages)
|
||||
return fats
|
||||
|
||||
|
||||
class CorpStat(object):
|
||||
def __init__(self, corp_id, corp=None, blue=False):
|
||||
def __init__(self, corp_id, start_of_month, start_of_next_month, corp=None):
|
||||
if corp:
|
||||
self.corp = corp
|
||||
else:
|
||||
self.corp = EveCorporationInfo.objects.get(corporation_id=corp_id)
|
||||
self.n_fats = 0
|
||||
self.blue = blue
|
||||
self.n_fats = Fat.objects.filter(character__corporation_id=self.corp.corporation_id).filter(
|
||||
fatlink__fatdatetime__gte=start_of_month).filter(fatlink__fatdatetime__lte=start_of_next_month).count()
|
||||
self.blue = self.corp.is_blue
|
||||
|
||||
def avg_fat(self):
|
||||
return "%.2f" % (float(self.n_fats) / float(self.corp.member_count))
|
||||
@@ -69,7 +84,7 @@ def fatlink_view(request):
|
||||
else:
|
||||
context = {'user': user, 'fats': latest_fats}
|
||||
|
||||
return render(request, 'registered/fatlinkview.html', context=context)
|
||||
return render(request, 'fleetactivitytracking/fatlinkview.html', context=context)
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -81,41 +96,42 @@ def fatlink_statistics_view(request, year=datetime.date.today().year, month=date
|
||||
start_of_next_month = first_day_of_next_month(year, month)
|
||||
start_of_previous_month = first_day_of_previous_month(year, month)
|
||||
|
||||
fatStats = OrderedDict()
|
||||
fat_stats = {}
|
||||
|
||||
if settings.IS_CORP:
|
||||
fatStats[settings.CORP_NAME] = CorpStat(settings.CORP_ID)
|
||||
else:
|
||||
alliance_corps = EveCorporationInfo.objects.filter(alliance__alliance_id=settings.ALLIANCE_ID)
|
||||
# get FAT stats for member corps
|
||||
for corp_id in settings.STR_CORP_IDS:
|
||||
fat_stats[corp_id] = CorpStat(corp_id, start_of_month, start_of_next_month)
|
||||
for alliance_id in settings.STR_ALLIANCE_IDS:
|
||||
alliance_corps = EveCorporationInfo.objects.filter(alliance__alliance_id=alliance_id)
|
||||
for corp in alliance_corps:
|
||||
fatStats[corp.corporation_name] = CorpStat(corp.corporation_id, corp=corp)
|
||||
fat_stats[corp.corporation_id] = CorpStat(corp.corporation_id, start_of_month, start_of_next_month)
|
||||
|
||||
fatlinks_in_span = Fatlink.objects.filter(fatdatetime__gte=start_of_month).filter(
|
||||
fatdatetime__lt=start_of_next_month)
|
||||
# get FAT stats for corps not in alliance
|
||||
fats_in_span = Fat.objects.filter(fatlink__fatdatetime__gte=start_of_month).filter(
|
||||
fatlink__fatdatetime__lt=start_of_next_month).exclude(character__corporation_id__in=fat_stats)
|
||||
|
||||
for fatlink in fatlinks_in_span:
|
||||
fats_in_fatlink = Fat.objects.filter(fatlink=fatlink)
|
||||
for fat in fats_in_fatlink:
|
||||
fatStats.setdefault(fat.character.corporation_name,
|
||||
CorpStat(fat.character.corporation_id, blue=True)
|
||||
).n_fats += 1
|
||||
for fat in fats_in_span:
|
||||
if fat.character.corporation_id not in fat_stats:
|
||||
fat_stats[fat.character.corporation_id] = CorpStat(fat.character.corporation_id, start_of_month,
|
||||
start_of_next_month)
|
||||
|
||||
fatStatsList = [fatStat for corp_name, fatStat in fatStats.items()]
|
||||
fatStatsList.sort(key=lambda stat: stat.corp.corporation_name)
|
||||
fatStatsList.sort(key=lambda stat: (stat.n_fats, stat.n_fats / stat.corp.member_count), reverse=True)
|
||||
# collect and sort stats
|
||||
stat_list = [fat_stats[x] for x in fat_stats]
|
||||
stat_list.sort(key=lambda stat: stat.corp.corporation_name)
|
||||
stat_list.sort(key=lambda stat: (stat.n_fats, stat.n_fats / stat.corp.member_count), reverse=True)
|
||||
|
||||
if datetime.datetime.now() > start_of_next_month:
|
||||
context = {'fatStats': fatStatsList, 'month': start_of_month.strftime("%B"), 'year': year,
|
||||
context = {'fatStats': stat_list, 'month': start_of_month.strftime("%B"), 'year': year,
|
||||
'previous_month': start_of_previous_month, 'next_month': start_of_next_month}
|
||||
else:
|
||||
context = {'fatStats': fatStatsList, 'month': start_of_month.strftime("%B"), 'year': year,
|
||||
context = {'fatStats': stat_list, 'month': start_of_month.strftime("%B"), 'year': year,
|
||||
'previous_month': start_of_previous_month}
|
||||
|
||||
return render(request, 'registered/fatlinkstatisticsview.html', context=context)
|
||||
return render(request, 'fleetactivitytracking/fatlinkstatisticsview.html', context=context)
|
||||
|
||||
|
||||
@login_required
|
||||
def fatlink_personal_statistics_view(request, year=datetime.date.today().year, main_name=None):
|
||||
def fatlink_personal_statistics_view(request, year=datetime.date.today().year):
|
||||
year = int(year)
|
||||
logger.debug("Personal statistics view for year %i called by %s" % (year, request.user))
|
||||
|
||||
@@ -139,7 +155,7 @@ def fatlink_personal_statistics_view(request, year=datetime.date.today().year, m
|
||||
else:
|
||||
context = {'user': user, 'monthlystats': monthlystats, 'year': year, 'previous_year': year - 1}
|
||||
|
||||
return render(request, 'registered/fatlinkpersonalstatisticsview.html', context=context)
|
||||
return render(request, 'fleetactivitytracking/fatlinkpersonalstatisticsview.html', context=context)
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -173,55 +189,65 @@ def fatlink_monthly_personal_statistics_view(request, year, month, char_id=None)
|
||||
context["created_fats"] = created_fats
|
||||
context["n_created_fats"] = len(created_fats)
|
||||
|
||||
return render(request, 'registered/fatlinkpersonalmonthlystatisticsview.html', context=context)
|
||||
return render(request, 'fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html', context=context)
|
||||
|
||||
|
||||
@login_required
|
||||
def click_fatlink_view(request, hash, fatname):
|
||||
# Take IG-header data and register the fatlink if not existing already.
|
||||
# use obj, created = Fat.objects.get_or_create()
|
||||
# onload="CCPEVE.requestTrust('http://www.mywebsite.com')"
|
||||
@token_required(
|
||||
scopes=['esi-location.read_location.v1', 'esi-location.read_ship_type.v1', 'esi-universe.read_structures.v1'])
|
||||
def click_fatlink_view(request, token, hash, fatname):
|
||||
try:
|
||||
fatlink = Fatlink.objects.filter(hash=hash)[0]
|
||||
|
||||
if 'HTTP_EVE_TRUSTED' in request.META and request.META['HTTP_EVE_TRUSTED'] == "Yes":
|
||||
# Retrieve the latest fatlink using the hash.
|
||||
try:
|
||||
fatlink = Fatlink.objects.filter(hash=hash)[0]
|
||||
if (timezone.now() - fatlink.fatdatetime) < datetime.timedelta(seconds=(fatlink.duration * 60)):
|
||||
|
||||
if (timezone.now() - fatlink.fatdatetime) < datetime.timedelta(seconds=(fatlink.duration * 60)):
|
||||
character = EveManager.get_character_by_id(token.character_id)
|
||||
|
||||
character = EveManager.get_character_by_id(request.META['HTTP_EVE_CHARID'])
|
||||
|
||||
if character:
|
||||
fat = Fat()
|
||||
fat.system = request.META['HTTP_EVE_SOLARSYSTEMNAME']
|
||||
if 'HTTP_EVE_STATIONNAME' in request.META:
|
||||
fat.station = request.META['HTTP_EVE_STATIONNAME']
|
||||
else:
|
||||
fat.station = "No Station"
|
||||
fat.shiptype = request.META['HTTP_EVE_SHIPTYPENAME']
|
||||
fat.fatlink = fatlink
|
||||
fat.character = character
|
||||
fat.user = character.user
|
||||
try:
|
||||
fat.full_clean()
|
||||
fat.save()
|
||||
context = {'trusted': True, 'registered': True}
|
||||
except ValidationError as e:
|
||||
messages = []
|
||||
for errorname, message in e.message_dict.items():
|
||||
messages.append(message[0].decode())
|
||||
context = {'trusted': True, 'errormessages': messages}
|
||||
if character:
|
||||
# get data
|
||||
c = token.get_esi_client()
|
||||
location = c.Location.get_characters_character_id_location(character_id=token.character_id).result()
|
||||
ship = c.Location.get_characters_character_id_ship(character_id=token.character_id).result()
|
||||
location['solar_system_name'] = \
|
||||
c.Universe.get_universe_systems_system_id(system_id=location['solar_system_id']).result()[
|
||||
'solar_system_name']
|
||||
if location['structure_id']:
|
||||
location['station_name'] = \
|
||||
c.Universe.get_universe_structures_structure_id(structure_id=location['structure_id']).result()[
|
||||
'name']
|
||||
elif location['station_id']:
|
||||
location['station_name'] = \
|
||||
c.Universe.get_universe_stations_station_id(station_id=location['station_id']).result()[
|
||||
'station_name']
|
||||
else:
|
||||
context = {'character_id': request.META['HTTP_EVE_CHARID'],
|
||||
'character_name': request.META['HTTP_EVE_CHARNAME']}
|
||||
return render(request, 'public/characternotexisting.html', context=context)
|
||||
location['station_name'] = "No Station"
|
||||
ship['ship_type_name'] = EveManager.get_itemtype(ship['ship_type_id']).name
|
||||
|
||||
fat = Fat()
|
||||
fat.system = location['solar_system_name']
|
||||
fat.station = location['station_name']
|
||||
fat.shiptype = ship['ship_type_name']
|
||||
fat.fatlink = fatlink
|
||||
fat.character = character
|
||||
fat.user = character.user
|
||||
try:
|
||||
fat.full_clean()
|
||||
fat.save()
|
||||
messages.success(request, 'Fleet participation registered.')
|
||||
except ValidationError as e:
|
||||
err_messages = []
|
||||
for errorname, message in e.message_dict.items():
|
||||
err_messages.append(message[0].decode())
|
||||
messages.error(request, ' '.join(err_messages))
|
||||
else:
|
||||
context = {'trusted': True, 'expired': True}
|
||||
except ObjectDoesNotExist:
|
||||
context = {'trusted': True}
|
||||
else:
|
||||
context = {'trusted': False, 'fatname': fatname}
|
||||
return render(request, 'public/clickfatlinkview.html', context=context)
|
||||
context = {'character_id': token.character_id,
|
||||
'character_name': token.character_name}
|
||||
return render(request, 'fleetactivitytracking/characternotexisting.html', context=context)
|
||||
else:
|
||||
messages.error(request, 'FAT link has expired.')
|
||||
except (ObjectDoesNotExist, KeyError):
|
||||
messages.error(request, 'Invalid FAT link.')
|
||||
return redirect('auth_fatlink_view')
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -250,7 +276,7 @@ def create_fatlink_view(request):
|
||||
for errorname, message in e.message_dict.items():
|
||||
messages.append(message[0].decode())
|
||||
context = {'form': form, 'errormessages': messages}
|
||||
return render(request, 'registered/fatlinkformatter.html', context=context)
|
||||
return render(request, 'fleetactivitytracking/fatlinkformatter.html', context=context)
|
||||
else:
|
||||
form = FatlinkForm()
|
||||
context = {'form': form, 'badrequest': True}
|
||||
@@ -263,7 +289,7 @@ def create_fatlink_view(request):
|
||||
|
||||
context = {'form': form}
|
||||
|
||||
return render(request, 'registered/fatlinkformatter.html', context=context)
|
||||
return render(request, 'fleetactivitytracking/fatlinkformatter.html', context=context)
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -271,24 +297,26 @@ def create_fatlink_view(request):
|
||||
def modify_fatlink_view(request, hash=""):
|
||||
logger.debug("modify_fatlink_view called by user %s" % request.user)
|
||||
if not hash:
|
||||
return redirect('/fat/')
|
||||
return redirect('auth_fatlink_view')
|
||||
|
||||
fatlink = Fatlink.objects.filter(hash=hash)[0]
|
||||
|
||||
if request.GET.get('removechar'):
|
||||
if request.GET.get('removechar', None):
|
||||
character_id = request.GET.get('removechar')
|
||||
character = EveCharacter.objects.get(character_id=character_id)
|
||||
logger.debug("Removing character %s from fleetactivitytracking %s" % (character.character_name, fatlink.name))
|
||||
|
||||
Fat.objects.filter(fatlink=fatlink).filter(character=character).delete()
|
||||
|
||||
if request.GET.get('deletefat'):
|
||||
if request.GET.get('deletefat', None):
|
||||
logger.debug("Removing fleetactivitytracking %s" % fatlink.name)
|
||||
fatlink.delete()
|
||||
return redirect('/fat/')
|
||||
return redirect('auth_fatlink_view')
|
||||
|
||||
registered_fats = Fat.objects.filter(fatlink=fatlink).order_by('character')
|
||||
registered_fats = Fat.objects.filter(fatlink=fatlink).order_by('character__character_name')
|
||||
|
||||
context = {'fatlink': fatlink, 'registered_fats': registered_fats}
|
||||
fat_page = get_page(registered_fats, request.GET.get('page', 1))
|
||||
|
||||
return render(request, 'registered/fatlinkmodify.html', context=context)
|
||||
context = {'fatlink': fatlink, 'registered_fats': fat_page}
|
||||
|
||||
return render(request, 'fleetactivitytracking/fatlinkmodify.html', context=context)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.contrib import admin
|
||||
|
||||
from groupmanagement.models import GroupDescription
|
||||
from groupmanagement.models import GroupRequest
|
||||
from groupmanagement.models import HiddenGroup
|
||||
from groupmanagement.models import OpenGroup
|
||||
from groupmanagement.models import AuthGroup
|
||||
|
||||
|
||||
admin.site.register(GroupDescription)
|
||||
class AuthGroupAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
Admin model for AuthGroup
|
||||
"""
|
||||
filter_horizontal = ('group_leaders',)
|
||||
|
||||
admin.site.register(GroupRequest)
|
||||
admin.site.register(HiddenGroup)
|
||||
admin.site.register(OpenGroup)
|
||||
admin.site.register(AuthGroup, AuthGroupAdmin)
|
||||
|
||||
5
groupmanagement/context_processors.py
Normal file
5
groupmanagement/context_processors.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from groupmanagement.managers import GroupManager
|
||||
|
||||
|
||||
def can_manage_groups(request):
|
||||
return {'can_manage_groups': GroupManager.can_manage_groups(request.user)}
|
||||
56
groupmanagement/managers.py
Normal file
56
groupmanagement/managers.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from django.contrib.auth.models import Group
|
||||
from django.conf import settings
|
||||
from authentication.managers import UserState
|
||||
|
||||
class GroupManager:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_joinable_groups():
|
||||
return Group.objects.exclude(authgroup__internal=True)
|
||||
|
||||
@staticmethod
|
||||
def get_group_leaders_groups(user):
|
||||
return Group.objects.filter(authgroup__group_leaders__in=[user])
|
||||
|
||||
@staticmethod
|
||||
def joinable_group(group):
|
||||
"""
|
||||
Check if a group is a user joinable group, i.e.
|
||||
not an internal group for Corp, Alliance, Members etc
|
||||
:param group: django.contrib.auth.models.Group object
|
||||
:return: bool True if its joinable, False otherwise
|
||||
"""
|
||||
return not group.authgroup.internal
|
||||
|
||||
@staticmethod
|
||||
def has_management_permission(user):
|
||||
return user.has_perm('auth.group_management')
|
||||
|
||||
@classmethod
|
||||
def can_manage_groups(cls, user):
|
||||
"""
|
||||
For use with user_passes_test decorator.
|
||||
Check if the user can manage groups. Either has the
|
||||
auth.group_management permission or is a leader of at least one group
|
||||
and is also a Member.
|
||||
:param user: django.contrib.auth.models.User for the request
|
||||
:return: bool True if user can manage groups, False otherwise
|
||||
"""
|
||||
if user.is_authenticated:
|
||||
return cls.has_management_permission(user) or (user.leads_groups.all() and UserState.member_state(user))
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def can_manage_group(cls, user, group):
|
||||
"""
|
||||
Check user has permission to manage the given group
|
||||
:param user: User object to test permission of
|
||||
:param group: Group object the user is attempting to manage
|
||||
:return: True if the user can manage the group
|
||||
"""
|
||||
if user.is_authenticated:
|
||||
return cls.has_management_permission(user) or (
|
||||
user.leads_groups.filter(group=group).exists() and UserState.member_state(user))
|
||||
return False
|
||||
112
groupmanagement/migrations/0004_authgroup.py
Normal file
112
groupmanagement/migrations/0004_authgroup.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.2 on 2016-12-04 10:25
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
def internal_group(group):
|
||||
return (
|
||||
"Corp_" in group.name or
|
||||
"Alliance_" in group.name or
|
||||
settings.DEFAULT_AUTH_GROUP in group.name or
|
||||
settings.DEFAULT_BLUE_GROUP in group.name
|
||||
)
|
||||
|
||||
|
||||
def combine_group_models(apps, schema_editor):
|
||||
Group = apps.get_model("auth", "Group")
|
||||
AuthGroup = apps.get_model("groupmanagement", "AuthGroup")
|
||||
|
||||
for group in Group.objects.all():
|
||||
authgroup = AuthGroup(group=group)
|
||||
if not hasattr(group, 'hiddengroup'):
|
||||
authgroup.hidden = False
|
||||
if hasattr(group, 'opengroup'):
|
||||
authgroup.open = True
|
||||
|
||||
if hasattr(group, 'groupdescription'):
|
||||
authgroup.description = group.groupdescription.description
|
||||
|
||||
authgroup.internal = internal_group(group)
|
||||
authgroup.save()
|
||||
|
||||
|
||||
def reverse_group_models(apps, schema_editor):
|
||||
Group = apps.get_model("auth", "Group")
|
||||
GroupDescription = apps.get_model("groupmanagement", "GroupDescription")
|
||||
OpenGroup = apps.get_model("groupmanagement", "OpenGroup")
|
||||
HiddenGroup = apps.get_model("groupmanagement", "HiddenGroup")
|
||||
|
||||
for group in Group.objects.all():
|
||||
if not hasattr(group, 'authgroup') or group.authgroup is None:
|
||||
continue
|
||||
if group.authgroup.open:
|
||||
OpenGroup.objects.get_or_create(group=group)
|
||||
else:
|
||||
try:
|
||||
OpenGroup.objects.get(group=group).delete()
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
|
||||
if group.authgroup.hidden:
|
||||
HiddenGroup.objects.get_or_create(group=group)
|
||||
else:
|
||||
try:
|
||||
HiddenGroup.objects.get(group=group).delete()
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
|
||||
if len(group.authgroup.description):
|
||||
GroupDescription.objects.update_or_create(group=group,
|
||||
defaults={'description': group.authgroup.description})
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('groupmanagement', '0003_default_groups'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AuthGroup',
|
||||
fields=[
|
||||
('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True,
|
||||
serialize=False, to='auth.Group')),
|
||||
('internal', models.BooleanField(default=True, help_text='Internal group, users cannot see, join or request to join this group.<br>Used for groups such as Members, Corp_*, Alliance_* etc.<br><b>Overrides Hidden and Open options when selected.</b>')),
|
||||
('hidden', models.BooleanField(default=True, help_text='Group is hidden from users but can still join with the correct link.')),
|
||||
('open', models.BooleanField(default=False, help_text='Group is open and users will be automatically added upon request. <br>If the group is not open users will need their request manually approved.')),
|
||||
('description', models.CharField(max_length=512, blank=True, help_text='Description of the group shown to users.', )),
|
||||
|
||||
('group_leaders', models.ManyToManyField(related_name='leads_groups', to=settings.AUTH_USER_MODEL, blank=True, help_text='Group leaders can process group requests for this group specifically. Use the auth.group_management permission to allow a user to manage all groups.',)),
|
||||
],
|
||||
),
|
||||
migrations.RunPython(combine_group_models, reverse_group_models),
|
||||
migrations.RemoveField(
|
||||
model_name='groupdescription',
|
||||
name='group',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='hiddengroup',
|
||||
name='group',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='opengroup',
|
||||
name='group',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='GroupDescription',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='HiddenGroup',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='OpenGroup',
|
||||
),
|
||||
]
|
||||
@@ -3,19 +3,12 @@ from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import Group
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
from eveonline.models import EveCharacter
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class GroupDescription(models.Model):
|
||||
description = models.CharField(max_length=512)
|
||||
group = models.OneToOneField(Group)
|
||||
|
||||
def __str__(self):
|
||||
return self.group.name + " - Description"
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class GroupRequest(models.Model):
|
||||
status = models.CharField(max_length=254)
|
||||
@@ -29,16 +22,57 @@ class GroupRequest(models.Model):
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class HiddenGroup(models.Model):
|
||||
group = models.OneToOneField(Group)
|
||||
class AuthGroup(models.Model):
|
||||
"""
|
||||
Extends Django Group model with a one-to-one field
|
||||
Attributes are accessible via group as if they were in the model
|
||||
e.g. group.authgroup.internal
|
||||
|
||||
Logic:
|
||||
Internal - not requestable by users, at all. Covers Corp_, Alliance_, Members etc groups.
|
||||
Groups are internal by default
|
||||
|
||||
Not Internal and:
|
||||
Hidden - users cannot view, can request if they have the direct link.
|
||||
Not Hidden - Users can view and request the group
|
||||
Open - Users are automatically accepted into the group
|
||||
Not Open - Users requests must be approved before they are added to the group
|
||||
"""
|
||||
group = models.OneToOneField(Group, on_delete=models.CASCADE, primary_key=True)
|
||||
|
||||
internal = models.BooleanField(default=True,
|
||||
help_text="Internal group, users cannot see, join or request to join this group.<br>"
|
||||
"Used for groups such as Members, Corp_*, Alliance_* etc.<br>"
|
||||
"<b>Overrides Hidden and Open options when selected.</b>")
|
||||
hidden = models.BooleanField(default=True,
|
||||
help_text="Group is hidden from users but can still join with the correct link.")
|
||||
open = models.BooleanField(default=False,
|
||||
help_text="Group is open and users will be automatically added upon request. <br>"
|
||||
"If the group is not open users will need their request manually approved.")
|
||||
# Group leaders have management access to this group
|
||||
group_leaders = models.ManyToManyField(User, related_name='leads_groups', blank=True,
|
||||
help_text="Group leaders can process group requests for this group "
|
||||
"specifically. Use the auth.group_management permission to allow "
|
||||
"a user to manage all groups.")
|
||||
|
||||
description = models.CharField(max_length=512, blank=True, help_text="Description of the group shown to users.")
|
||||
|
||||
def __str__(self):
|
||||
return self.group.name + " - Hidden"
|
||||
return self.group.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class OpenGroup(models.Model):
|
||||
group = models.OneToOneField(Group)
|
||||
@receiver(post_save, sender=Group)
|
||||
def create_auth_group(sender, instance, created, **kwargs):
|
||||
"""
|
||||
Creates the AuthGroup model when a group is created
|
||||
"""
|
||||
if created:
|
||||
AuthGroup.objects.create(group=instance)
|
||||
|
||||
def __str__(self):
|
||||
return self.group.name + " - Open"
|
||||
|
||||
@receiver(post_save, sender=Group)
|
||||
def save_auth_group(sender, instance, **kwargs):
|
||||
"""
|
||||
Ensures AuthGroup model is saved automatically
|
||||
"""
|
||||
instance.authgroup.save()
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.shortcuts import render, redirect
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
from django.contrib.auth.decorators import user_passes_test
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib import messages
|
||||
from notifications import notify
|
||||
from groupmanagement.models import GroupDescription
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db.models import Count
|
||||
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
|
||||
from django.http import Http404
|
||||
from groupmanagement.managers import GroupManager
|
||||
from groupmanagement.models import GroupRequest
|
||||
from groupmanagement.models import HiddenGroup
|
||||
from groupmanagement.models import OpenGroup
|
||||
from authentication.models import AuthServicesInfo
|
||||
from eveonline.managers import EveManager
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import logging
|
||||
|
||||
@@ -20,17 +20,25 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('auth.group_management')
|
||||
@user_passes_test(GroupManager.can_manage_groups)
|
||||
def group_management(request):
|
||||
logger.debug("group_management called by user %s" % request.user)
|
||||
acceptrequests = []
|
||||
leaverequests = []
|
||||
|
||||
for grouprequest in GroupRequest.objects.all():
|
||||
if GroupManager.has_management_permission(request.user):
|
||||
# Full access
|
||||
group_requests = GroupRequest.objects.all()
|
||||
else:
|
||||
# Group specific leader
|
||||
group_requests = GroupRequest.objects.filter(group__authgroup__group_leaders__in=[request.user])
|
||||
|
||||
for grouprequest in group_requests:
|
||||
if grouprequest.leave_request:
|
||||
leaverequests.append(grouprequest)
|
||||
else:
|
||||
acceptrequests.append(grouprequest)
|
||||
|
||||
logger.debug("Providing user %s with %s acceptrequests and %s leaverequests." % (
|
||||
request.user, len(acceptrequests), len(leaverequests)))
|
||||
|
||||
@@ -40,12 +48,98 @@ def group_management(request):
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('auth.group_management')
|
||||
@user_passes_test(GroupManager.can_manage_groups)
|
||||
def group_membership(request):
|
||||
logger.debug("group_membership called by user %s" % request.user)
|
||||
# Get all open and closed groups
|
||||
if GroupManager.has_management_permission(request.user):
|
||||
# Full access
|
||||
groups = GroupManager.get_joinable_groups()
|
||||
else:
|
||||
# Group leader specific
|
||||
groups = GroupManager.get_group_leaders_groups(request.user)
|
||||
|
||||
groups = groups.exclude(authgroup__internal=True).annotate(num_members=Count('user')).order_by('name')
|
||||
|
||||
render_items = {'groups': groups}
|
||||
|
||||
return render(request, 'registered/groupmembership.html', context=render_items)
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(GroupManager.can_manage_groups)
|
||||
def group_membership_list(request, group_id):
|
||||
logger.debug("group_membership_list called by user %s for group id %s" % (request.user, group_id))
|
||||
try:
|
||||
group = Group.objects.get(id=group_id)
|
||||
|
||||
# Check its a joinable group i.e. not corp or internal
|
||||
# And the user has permission to manage it
|
||||
if not GroupManager.joinable_group(group) or not GroupManager.can_manage_group(request.user, group):
|
||||
logger.warning("User %s attempted to view the membership of group %s but permission was denied" %
|
||||
(request.user, group_id))
|
||||
raise PermissionDenied
|
||||
|
||||
except ObjectDoesNotExist:
|
||||
raise Http404("Group does not exist")
|
||||
|
||||
members = list()
|
||||
|
||||
for member in group.user_set.all().order_by('username'):
|
||||
authinfo = AuthServicesInfo.objects.get(user=member)
|
||||
|
||||
members.append({
|
||||
'user': member,
|
||||
'main_char': EveManager.get_character_by_id(authinfo.main_char_id)
|
||||
})
|
||||
|
||||
render_items = {'group': group, 'members': members}
|
||||
|
||||
return render(request, 'registered/groupmembers.html', context=render_items)
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(GroupManager.can_manage_groups)
|
||||
def group_membership_remove(request, group_id, user_id):
|
||||
logger.debug("group_membership_remove called by user %s for group id %s on user id %s" %
|
||||
(request.user, group_id, user_id))
|
||||
try:
|
||||
group = Group.objects.get(id=group_id)
|
||||
|
||||
# Check its a joinable group i.e. not corp or internal
|
||||
# And the user has permission to manage it
|
||||
if not GroupManager.joinable_group(group) or not GroupManager.can_manage_group(request.user, group):
|
||||
logger.warning("User %s attempted to remove a user from group %s but permission was denied" % (request.user,
|
||||
group_id))
|
||||
raise PermissionDenied
|
||||
|
||||
try:
|
||||
user = group.user_set.get(id=user_id)
|
||||
# Remove group from user
|
||||
user.groups.remove(group)
|
||||
logger.info("User %s removed user %s from group %s" % (request.user, user, group))
|
||||
messages.success(request, "Removed user %s from group %s" % (user, group))
|
||||
except ObjectDoesNotExist:
|
||||
messages.warning(request, "User does not exist in that group")
|
||||
|
||||
except ObjectDoesNotExist:
|
||||
messages.warning(request, "Group does not exist")
|
||||
|
||||
return redirect('auth_group_membership_list', group_id)
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(GroupManager.can_manage_groups)
|
||||
def group_accept_request(request, group_request_id):
|
||||
logger.debug("group_accept_request called by user %s for grouprequest id %s" % (request.user, group_request_id))
|
||||
try:
|
||||
group_request = GroupRequest.objects.get(id=group_request_id)
|
||||
group, created = Group.objects.get_or_create(name=group_request.group.name)
|
||||
|
||||
if not GroupManager.joinable_group(group_request.group) or \
|
||||
not GroupManager.can_manage_group(request.user, group_request.group):
|
||||
raise PermissionDenied
|
||||
|
||||
group_request.user.groups.add(group)
|
||||
group_request.user.save()
|
||||
group_request.delete()
|
||||
@@ -55,6 +149,11 @@ def group_accept_request(request, group_request_id):
|
||||
message="Your application to %s has been accepted." % group_request.group)
|
||||
messages.success(request,
|
||||
'Accepted application from %s to %s.' % (group_request.main_char, group_request.group))
|
||||
|
||||
except PermissionDenied as p:
|
||||
logger.warning("User %s attempted to accept group join request %s but permission was denied" %
|
||||
(request.user, group_request_id))
|
||||
raise p
|
||||
except:
|
||||
messages.error(request, 'An unhandled error occurred while processing the application from %s to %s.' % (
|
||||
group_request.main_char, group_request.group))
|
||||
@@ -66,12 +165,15 @@ def group_accept_request(request, group_request_id):
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('auth.group_management')
|
||||
@user_passes_test(GroupManager.can_manage_groups)
|
||||
def group_reject_request(request, group_request_id):
|
||||
logger.debug("group_reject_request called by user %s for group request id %s" % (request.user, group_request_id))
|
||||
try:
|
||||
group_request = GroupRequest.objects.get(id=group_request_id)
|
||||
|
||||
if not GroupManager.can_manage_group(request.user, group_request.group):
|
||||
raise PermissionDenied
|
||||
|
||||
if group_request:
|
||||
logger.info("User %s rejected group request from user %s to group %s" % (
|
||||
request.user, group_request.user, group_request.group.name))
|
||||
@@ -80,6 +182,11 @@ def group_reject_request(request, group_request_id):
|
||||
message="Your application to %s has been rejected." % group_request.group)
|
||||
messages.success(request,
|
||||
'Rejected application from %s to %s.' % (group_request.main_char, group_request.group))
|
||||
|
||||
except PermissionDenied as p:
|
||||
logger.warning("User %s attempted to reject group join request %s but permission was denied" %
|
||||
(request.user, group_request_id))
|
||||
raise p
|
||||
except:
|
||||
messages.error(request, 'An unhandled error occured while processing the application from %s to %s.' % (
|
||||
group_request.main_char, group_request.group))
|
||||
@@ -91,12 +198,16 @@ def group_reject_request(request, group_request_id):
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('auth.group_management')
|
||||
@user_passes_test(GroupManager.can_manage_groups)
|
||||
def group_leave_accept_request(request, group_request_id):
|
||||
logger.debug(
|
||||
"group_leave_accept_request called by user %s for group request id %s" % (request.user, group_request_id))
|
||||
try:
|
||||
group_request = GroupRequest.objects.get(id=group_request_id)
|
||||
|
||||
if not GroupManager.can_manage_group(request.user, group_request.group):
|
||||
raise PermissionDenied
|
||||
|
||||
group, created = Group.objects.get_or_create(name=group_request.group.name)
|
||||
group_request.user.groups.remove(group)
|
||||
group_request.user.save()
|
||||
@@ -107,6 +218,10 @@ def group_leave_accept_request(request, group_request_id):
|
||||
message="Your request to leave %s has been accepted." % group_request.group)
|
||||
messages.success(request,
|
||||
'Accepted application from %s to leave %s.' % (group_request.main_char, group_request.group))
|
||||
except PermissionDenied as p:
|
||||
logger.warning("User %s attempted to accept group leave request %s but permission was denied" %
|
||||
(request.user, group_request_id))
|
||||
raise p
|
||||
except:
|
||||
messages.error(request, 'An unhandled error occured while processing the application from %s to leave %s.' % (
|
||||
group_request.main_char, group_request.group))
|
||||
@@ -118,13 +233,16 @@ def group_leave_accept_request(request, group_request_id):
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required('auth.group_management')
|
||||
@user_passes_test(GroupManager.can_manage_groups)
|
||||
def group_leave_reject_request(request, group_request_id):
|
||||
logger.debug(
|
||||
"group_leave_reject_request called by user %s for group request id %s" % (request.user, group_request_id))
|
||||
try:
|
||||
group_request = GroupRequest.objects.get(id=group_request_id)
|
||||
|
||||
if not GroupManager.can_manage_group(request.user, group_request.group):
|
||||
raise PermissionDenied
|
||||
|
||||
if group_request:
|
||||
group_request.delete()
|
||||
logger.info("User %s rejected group leave request from user %s for group %s" % (
|
||||
@@ -133,6 +251,10 @@ def group_leave_reject_request(request, group_request_id):
|
||||
message="Your request to leave %s has been rejected." % group_request.group)
|
||||
messages.success(request, 'Rejected application from %s to leave %s.' % (
|
||||
group_request.main_char, group_request.group))
|
||||
except PermissionDenied as p:
|
||||
logger.warning("User %s attempted to reject group leave request %s but permission was denied" %
|
||||
(request.user, group_request_id))
|
||||
raise p
|
||||
except:
|
||||
messages.error(request, 'An unhandled error occured while processing the application from %s to leave %s.' % (
|
||||
group_request.main_char, group_request.group))
|
||||
@@ -146,37 +268,16 @@ def group_leave_reject_request(request, group_request_id):
|
||||
@login_required
|
||||
def groups_view(request):
|
||||
logger.debug("groups_view called by user %s" % request.user)
|
||||
paired_list = []
|
||||
groups = []
|
||||
|
||||
for group in Group.objects.all():
|
||||
# Check if group is a corp
|
||||
if "Corp_" in group.name:
|
||||
pass
|
||||
elif "Alliance_" in group.name:
|
||||
pass
|
||||
elif settings.DEFAULT_AUTH_GROUP in group.name:
|
||||
pass
|
||||
elif settings.DEFAULT_BLUE_GROUP in group.name:
|
||||
pass
|
||||
elif HiddenGroup.objects.filter(group=group).exists():
|
||||
pass
|
||||
else:
|
||||
# Get the descriptionn
|
||||
group_desc = GroupDescription.objects.filter(group=group)
|
||||
for group in GroupManager.get_joinable_groups():
|
||||
# Exclude hidden
|
||||
if not group.authgroup.hidden:
|
||||
group_request = GroupRequest.objects.filter(user=request.user).filter(group=group)
|
||||
|
||||
if group_desc:
|
||||
if group_request:
|
||||
paired_list.append((group, group_desc[0], group_request[0]))
|
||||
else:
|
||||
paired_list.append((group, group_desc[0], ""))
|
||||
else:
|
||||
if group_request:
|
||||
paired_list.append((group, "", group_request[0]))
|
||||
else:
|
||||
paired_list.append((group, "", ""))
|
||||
groups.append({'group': group, 'request': group_request[0] if group_request else None})
|
||||
|
||||
render_items = {'pairs': paired_list}
|
||||
render_items = {'groups': groups}
|
||||
return render(request, 'registered/groups.html', context=render_items)
|
||||
|
||||
|
||||
@@ -184,11 +285,16 @@ def groups_view(request):
|
||||
def group_request_add(request, group_id):
|
||||
logger.debug("group_request_add called by user %s for group id %s" % (request.user, group_id))
|
||||
group = Group.objects.get(id=group_id)
|
||||
if OpenGroup.objects.filter(group=group).exists():
|
||||
if not GroupManager.joinable_group(group):
|
||||
logger.warning("User %s attempted to join group id %s but it is not a joinable group" %
|
||||
(request.user, group_id))
|
||||
messages.warning(request, "You cannot join that group")
|
||||
return redirect('auth_groups')
|
||||
if group.authgroup.open:
|
||||
logger.info("%s joining %s as is an open group" % (request.user, group))
|
||||
request.user.groups.add(group)
|
||||
return redirect("auth_groups")
|
||||
auth_info = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
auth_info = AuthServicesInfo.objects.get(user=request.user)
|
||||
grouprequest = GroupRequest()
|
||||
grouprequest.status = _('Pending')
|
||||
grouprequest.group = group
|
||||
@@ -205,11 +311,21 @@ def group_request_add(request, group_id):
|
||||
def group_request_leave(request, group_id):
|
||||
logger.debug("group_request_leave called by user %s for group id %s" % (request.user, group_id))
|
||||
group = Group.objects.get(id=group_id)
|
||||
if OpenGroup.objects.filter(group=group).exists():
|
||||
if not GroupManager.joinable_group(group):
|
||||
logger.warning("User %s attempted to leave group id %s but it is not a joinable group" %
|
||||
(request.user, group_id))
|
||||
messages.warning(request, "You cannot leave that group")
|
||||
return redirect('auth_groups')
|
||||
if group not in request.user.groups.all():
|
||||
logger.debug("User %s attempted to leave group id %s but they are not a member" %
|
||||
(request.user, group_id))
|
||||
messages.warning(request, "You are not a member of that group")
|
||||
return redirect('auth_groups')
|
||||
if group.authgroup.open:
|
||||
logger.info("%s leaving %s as is an open group" % (request.user, group))
|
||||
request.user.groups.remove(group)
|
||||
return redirect("auth_groups")
|
||||
auth_info = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
auth_info = AuthServicesInfo.objects.get(user=request.user)
|
||||
grouprequest = GroupRequest()
|
||||
grouprequest.status = _('Pending')
|
||||
grouprequest.group = group
|
||||
|
||||
@@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_application_test(user):
|
||||
auth, c = AuthServicesInfo.objects.get_or_create(user=user)
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
if auth.main_char_id:
|
||||
return True
|
||||
else:
|
||||
@@ -31,7 +31,7 @@ def hr_application_management_view(request):
|
||||
logger.debug("hr_application_management_view called by user %s" % request.user)
|
||||
corp_applications = []
|
||||
finished_corp_applications = []
|
||||
auth_info, c = AuthServicesInfo.objects.get_or_create(user=request.user)
|
||||
auth_info = AuthServicesInfo.objects.get(user=request.user)
|
||||
main_char = None
|
||||
if auth_info.main_char_id:
|
||||
try:
|
||||
|
||||
@@ -37,7 +37,7 @@ def add_optimer_view(request):
|
||||
# Get Current Time
|
||||
post_time = timezone.now()
|
||||
# Get character
|
||||
auth_info = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
auth_info = AuthServicesInfo.objects.get(user=request.user)
|
||||
character = EveManager.get_character_by_id(auth_info.main_char_id)
|
||||
# handle valid form
|
||||
op = optimer()
|
||||
@@ -88,7 +88,7 @@ def edit_optimer(request, optimer_id):
|
||||
form = opForm(request.POST)
|
||||
logger.debug("Received POST request containing update optimer form, is valid: %s" % form.is_valid())
|
||||
if form.is_valid():
|
||||
auth_info = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
auth_info = AuthServicesInfo.objects.get(user=request.user)
|
||||
character = EveManager.get_character_by_id(auth_info.main_char_id)
|
||||
op.doctrine = form.cleaned_data['doctrine']
|
||||
op.system = form.cleaned_data['system']
|
||||
|
||||
@@ -15,12 +15,16 @@ redis
|
||||
django>=1.10,<2.0
|
||||
django-bootstrap-form
|
||||
django-navhelper
|
||||
django-bootstrap-pagination
|
||||
django-redis>=4.4
|
||||
|
||||
# awating release for fix to celery/django-celery#447
|
||||
# django-celery
|
||||
git+https://github.com/celery/django-celery
|
||||
|
||||
git+git://github.com/nikdoof/python-ts3.git
|
||||
git+https://github.com/pyghassen/openfire-restapi
|
||||
|
||||
git+https://github.com/adarnof/adarnauth-eve-sso
|
||||
# awating pyghassen/openfire-restapi #1 to fix installation issues
|
||||
git+https://github.com/adarnof/openfire-restapi
|
||||
|
||||
git+https://github.com/adarnof/adarnauth-esi
|
||||
|
||||
@@ -23,9 +23,5 @@ def auth_settings(request):
|
||||
'MEMBER_API_MASK': settings.MEMBER_API_MASK,
|
||||
'MEMBER_API_ACCOUNT': settings.MEMBER_API_ACCOUNT,
|
||||
'JABBER_URL': settings.JABBER_URL,
|
||||
'ALLIANCE_NAME': settings.ALLIANCE_NAME,
|
||||
'ALLIANCE_ID': settings.ALLIANCE_ID,
|
||||
'CORP_NAME': settings.CORP_NAME,
|
||||
'CORP_ID': settings.CORP_ID,
|
||||
'IS_CORP': settings.IS_CORP,
|
||||
}
|
||||
'SITE_NAME': settings.SITE_NAME,
|
||||
}
|
||||
|
||||
@@ -319,16 +319,16 @@ class EveApiManager:
|
||||
def validate_api(api_id, api_key, user):
|
||||
try:
|
||||
info = EveApiManager.get_api_info(api_id, api_key).result
|
||||
chars = EveApiManager.get_characters_from_api(api_id, api_key).result
|
||||
except evelink.api.APIError as e:
|
||||
if int(e.code) == 222:
|
||||
raise EveApiManager.ApiInvalidError(api_id)
|
||||
raise e
|
||||
except (requests.exceptions.RequestExeception, HTTPError, URLError) as e:
|
||||
raise EveApiManager.ApiServerUnreachableError(e)
|
||||
auth, c = AuthServicesInfo.objects.get_or_create(user=user)
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
states = [auth.state]
|
||||
from authentication.tasks import determine_membership_by_character # circular import issue
|
||||
chars = info['characters']
|
||||
for char in chars:
|
||||
evechar = EveCharacter()
|
||||
evechar.character_name = chars[char]['name']
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
import requests
|
||||
import json
|
||||
|
||||
|
||||
class EveWhoManager:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_corporation_members(corpid):
|
||||
url = "http://evewho.com/api.php?type=corplist&id=%s" % corpid
|
||||
jsondata = requests.get(url).content
|
||||
data = json.loads(jsondata.decode())
|
||||
|
||||
members = {}
|
||||
page_count = 0
|
||||
while len(data["characters"]):
|
||||
for row in data["characters"]:
|
||||
members[int(row["character_id"])] = {"name": row["name"], "id": int(row["character_id"])}
|
||||
page_count += 1
|
||||
jsondata = requests.get(url + "&page=%i" % page_count).content
|
||||
data = json.loads(jsondata.decode())
|
||||
|
||||
return members
|
||||
@@ -34,20 +34,3 @@ class srpManager:
|
||||
return ship_type, ship_value
|
||||
else:
|
||||
raise ValueError("Invalid Kill ID")
|
||||
|
||||
@staticmethod
|
||||
def get_ship_name(ship_type):
|
||||
url = ("https://jetbalsa.com/api/json.php/invTypes/%s" % ship_type)
|
||||
headers = {
|
||||
'User-Agent': "%s Alliance Auth" % settings.DOMAIN,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
r = requests.get(url, headers=headers)
|
||||
result = r.json()
|
||||
if result:
|
||||
ship_name = result['typeName']
|
||||
logger.debug("ship type %s determined to be %s" % (ship_type, ship_name))
|
||||
return ship_name
|
||||
else:
|
||||
logger.debug("ship type %s is invalid" % ship_type)
|
||||
raise ValueError("Cannot get ship name")
|
||||
|
||||
@@ -30,7 +30,7 @@ def m2m_changed_user_groups(sender, instance, action, *args, **kwargs):
|
||||
|
||||
def trigger_service_group_update():
|
||||
logger.debug("Triggering service group update for %s" % instance)
|
||||
auth, c = AuthServicesInfo.objects.get_or_create(user=instance)
|
||||
auth = AuthServicesInfo.objects.get(user=instance)
|
||||
if auth.jabber_username:
|
||||
update_jabber_groups.delay(instance.pk)
|
||||
if auth.teamspeak3_uid:
|
||||
@@ -50,7 +50,7 @@ def m2m_changed_user_groups(sender, instance, action, *args, **kwargs):
|
||||
if auth.smf_username:
|
||||
update_smf_groups.delay(instance.pk)
|
||||
|
||||
if action == "post_add" or action == "post_remove" or action == "post_clear":
|
||||
if instance.pk and (action == "post_add" or action == "post_remove" or action == "post_clear"):
|
||||
logger.debug("Waiting for commit to trigger service group update for %s" % instance)
|
||||
transaction.on_commit(trigger_service_group_update)
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ def disable_market():
|
||||
def deactivate_services(user):
|
||||
change = False
|
||||
logger.debug("Deactivating services for user %s" % user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=user)
|
||||
if authinfo.mumble_username and authinfo.mumble_username != "":
|
||||
logger.debug("User %s has mumble account %s. Deleting." % (user, authinfo.mumble_username))
|
||||
MumbleManager.delete_user(authinfo.mumble_username)
|
||||
@@ -235,7 +235,7 @@ def validate_services(self, user, state):
|
||||
deactivate_services(user)
|
||||
return
|
||||
logger.debug('Ensuring user %s services are available to state %s' % (user, state))
|
||||
auth = AuthServicesInfo.objects.get_or_create(user=user)[0]
|
||||
auth = AuthServicesInfo.objects.get(user=user)
|
||||
if auth.mumble_username and not getattr(settings, 'ENABLE_%s_MUMBLE' % setting_string, False):
|
||||
MumbleManager.delete_user(auth.mumble_username)
|
||||
AuthServicesInfoManager.update_user_mumble_info("", user)
|
||||
@@ -489,7 +489,6 @@ def update_all_discord_nicknames():
|
||||
|
||||
|
||||
@task(bind=True)
|
||||
@only_one(key="Discourse", timeout=60*5)
|
||||
def update_discourse_groups(self, pk):
|
||||
user = User.objects.get(pk=pk)
|
||||
logger.debug("Updating discourse groups for user %s" % user)
|
||||
|
||||
@@ -142,7 +142,7 @@ def jabber_broadcast_view(request):
|
||||
@members_and_blues()
|
||||
def services_view(request):
|
||||
logger.debug("services_view called by user %s" % request.user)
|
||||
auth = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
auth = AuthServicesInfo.objects.get(user=request.user)
|
||||
char = None
|
||||
if auth.main_char_id:
|
||||
try:
|
||||
@@ -185,7 +185,7 @@ def superuser_test(user):
|
||||
@members_and_blues()
|
||||
def activate_forum(request):
|
||||
logger.debug("activate_forum called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
# Valid now we get the main characters
|
||||
character = EveManager.get_character_by_id(authinfo.main_char_id)
|
||||
logger.debug("Adding phpbb user for user %s with main character %s" % (request.user, character))
|
||||
@@ -213,7 +213,7 @@ def activate_forum(request):
|
||||
@members_and_blues()
|
||||
def deactivate_forum(request):
|
||||
logger.debug("deactivate_forum called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = Phpbb3Manager.disable_user(authinfo.forum_username)
|
||||
# false we failed
|
||||
if result:
|
||||
@@ -230,7 +230,7 @@ def deactivate_forum(request):
|
||||
@members_and_blues()
|
||||
def reset_forum_password(request):
|
||||
logger.debug("reset_forum_password called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = Phpbb3Manager.update_user_password(authinfo.forum_username, authinfo.main_char_id)
|
||||
# false we failed
|
||||
if result != "":
|
||||
@@ -252,7 +252,7 @@ def reset_forum_password(request):
|
||||
@members_and_blues()
|
||||
def activate_xenforo_forum(request):
|
||||
logger.debug("activate_xenforo_forum called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
character = EveManager.get_character_by_id(authinfo.main_char_id)
|
||||
logger.debug("Adding XenForo user for user %s with main character %s" % (request.user, character))
|
||||
result = XenForoManager.add_user(character.character_name, request.user.email)
|
||||
@@ -278,7 +278,7 @@ def activate_xenforo_forum(request):
|
||||
@members_and_blues()
|
||||
def deactivate_xenforo_forum(request):
|
||||
logger.debug("deactivate_xenforo_forum called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = XenForoManager.disable_user(authinfo.xenforo_username)
|
||||
if result.status_code == 200:
|
||||
AuthServicesInfoManager.update_user_xenforo_info("", request.user)
|
||||
@@ -293,7 +293,7 @@ def deactivate_xenforo_forum(request):
|
||||
@members_and_blues()
|
||||
def reset_xenforo_password(request):
|
||||
logger.debug("reset_xenforo_password called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = XenForoManager.reset_password(authinfo.xenforo_username)
|
||||
# Based on XenAPI's response codes
|
||||
if result['response']['status_code'] == 200:
|
||||
@@ -322,7 +322,7 @@ def set_xenforo_password(request):
|
||||
if form.is_valid():
|
||||
password = form.cleaned_data['password']
|
||||
logger.debug("Form contains password of length %s" % len(password))
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = XenForoManager.update_user_password(authinfo.xenforo_username, password)
|
||||
if result['response']['status_code'] == 200:
|
||||
logger.info("Successfully reset XenForo password for user %s" % request.user)
|
||||
@@ -344,7 +344,7 @@ def set_xenforo_password(request):
|
||||
@members_and_blues()
|
||||
def activate_ipboard_forum(request):
|
||||
logger.debug("activate_ipboard_forum called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
# Valid now we get the main characters
|
||||
character = EveManager.get_character_by_id(authinfo.main_char_id)
|
||||
logger.debug("Adding ipboard user for user %s with main character %s" % (request.user, character))
|
||||
@@ -371,7 +371,7 @@ def activate_ipboard_forum(request):
|
||||
@members_and_blues()
|
||||
def deactivate_ipboard_forum(request):
|
||||
logger.debug("deactivate_ipboard_forum called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = IPBoardManager.disable_user(authinfo.ipboard_username)
|
||||
# false we failed
|
||||
if result:
|
||||
@@ -388,7 +388,7 @@ def deactivate_ipboard_forum(request):
|
||||
@members_and_blues()
|
||||
def reset_ipboard_password(request):
|
||||
logger.debug("reset_ipboard_password called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = IPBoardManager.update_user_password(authinfo.ipboard_username, request.user.email)
|
||||
if result != "":
|
||||
logger.info("Successfully reset ipboard password for user %s" % request.user)
|
||||
@@ -409,7 +409,7 @@ def reset_ipboard_password(request):
|
||||
@members_and_blues()
|
||||
def activate_jabber(request):
|
||||
logger.debug("activate_jabber called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
character = EveManager.get_character_by_id(authinfo.main_char_id)
|
||||
logger.debug("Adding jabber user for user %s with main character %s" % (request.user, character))
|
||||
info = OpenfireManager.add_user(character.character_name)
|
||||
@@ -436,7 +436,7 @@ def activate_jabber(request):
|
||||
@members_and_blues()
|
||||
def deactivate_jabber(request):
|
||||
logger.debug("deactivate_jabber called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = OpenfireManager.delete_user(authinfo.jabber_username)
|
||||
# If our username is blank means we failed
|
||||
if result:
|
||||
@@ -453,7 +453,7 @@ def deactivate_jabber(request):
|
||||
@members_and_blues()
|
||||
def reset_jabber_password(request):
|
||||
logger.debug("reset_jabber_password called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = OpenfireManager.update_user_pass(authinfo.jabber_username)
|
||||
# If our username is blank means we failed
|
||||
if result != "":
|
||||
@@ -476,7 +476,7 @@ def reset_jabber_password(request):
|
||||
@members_and_blues()
|
||||
def activate_mumble(request):
|
||||
logger.debug("activate_mumble called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
character = EveManager.get_character_by_id(authinfo.main_char_id)
|
||||
ticker = character.corporation_ticker
|
||||
|
||||
@@ -513,7 +513,7 @@ def activate_mumble(request):
|
||||
@members_and_blues()
|
||||
def deactivate_mumble(request):
|
||||
logger.debug("deactivate_mumble called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
# if we successfully remove the user or the user is already removed
|
||||
if MumbleManager.delete_user(authinfo.mumble_username) or not MumbleManager.user_exists(authinfo.mumble_username):
|
||||
AuthServicesInfoManager.update_user_mumble_info("", request.user)
|
||||
@@ -529,7 +529,7 @@ def deactivate_mumble(request):
|
||||
@members_and_blues()
|
||||
def reset_mumble_password(request):
|
||||
logger.debug("reset_mumble_password called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = MumbleManager.update_user_password(authinfo.mumble_username)
|
||||
|
||||
# if blank we failed
|
||||
@@ -552,7 +552,7 @@ def reset_mumble_password(request):
|
||||
@members_and_blues()
|
||||
def activate_teamspeak3(request):
|
||||
logger.debug("activate_teamspeak3 called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
character = EveManager.get_character_by_id(authinfo.main_char_id)
|
||||
ticker = character.corporation_ticker
|
||||
|
||||
@@ -583,7 +583,7 @@ def activate_teamspeak3(request):
|
||||
@members_and_blues()
|
||||
def verify_teamspeak3(request):
|
||||
logger.debug("verify_teamspeak3 called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
if not authinfo.teamspeak3_uid:
|
||||
logger.warn("Unable to validate user %s teamspeak: no teamspeak data" % request.user)
|
||||
return redirect("auth_services")
|
||||
@@ -606,7 +606,7 @@ def verify_teamspeak3(request):
|
||||
@members_and_blues()
|
||||
def deactivate_teamspeak3(request):
|
||||
logger.debug("deactivate_teamspeak3 called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = Teamspeak3Manager.delete_user(authinfo.teamspeak3_uid)
|
||||
|
||||
# if false we failed
|
||||
@@ -624,7 +624,7 @@ def deactivate_teamspeak3(request):
|
||||
@members_and_blues()
|
||||
def reset_teamspeak3_perm(request):
|
||||
logger.debug("reset_teamspeak3_perm called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
character = EveManager.get_character_by_id(authinfo.main_char_id)
|
||||
logger.debug("Deleting TS3 user for user %s" % request.user)
|
||||
Teamspeak3Manager.delete_user(authinfo.teamspeak3_uid)
|
||||
@@ -643,7 +643,7 @@ def reset_teamspeak3_perm(request):
|
||||
if result != "":
|
||||
AuthServicesInfoManager.update_user_teamspeak3_info(result[0], result[1], request.user)
|
||||
logger.debug("Updated authserviceinfo for user %s with TS3 credentials. Updating groups." % request.user)
|
||||
update_teamspeak3_groups.delay(request.user)
|
||||
update_teamspeak3_groups.delay(request.user.pk)
|
||||
logger.info("Successfully reset TS3 permission key for user %s" % request.user)
|
||||
messages.success(request, 'Reset TeamSpeak3 permission key.')
|
||||
else:
|
||||
@@ -656,7 +656,7 @@ def reset_teamspeak3_perm(request):
|
||||
@members_and_blues()
|
||||
def deactivate_discord(request):
|
||||
logger.debug("deactivate_discord called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = DiscordOAuthManager.delete_user(authinfo.discord_uid)
|
||||
if result:
|
||||
AuthServicesInfoManager.update_user_discord_info("", request.user)
|
||||
@@ -672,7 +672,7 @@ def deactivate_discord(request):
|
||||
@members_and_blues()
|
||||
def reset_discord(request):
|
||||
logger.debug("reset_discord called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = DiscordOAuthManager.delete_user(authinfo.discord_uid)
|
||||
if result:
|
||||
AuthServicesInfoManager.update_user_discord_info("", request.user)
|
||||
@@ -729,7 +729,7 @@ def set_forum_password(request):
|
||||
if form.is_valid():
|
||||
password = form.cleaned_data['password']
|
||||
logger.debug("Form contains password of length %s" % len(password))
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = Phpbb3Manager.update_user_password(authinfo.forum_username, authinfo.main_char_id,
|
||||
password=password)
|
||||
if result != "":
|
||||
@@ -759,7 +759,7 @@ def set_mumble_password(request):
|
||||
if form.is_valid():
|
||||
password = form.cleaned_data['password']
|
||||
logger.debug("Form contains password of length %s" % len(password))
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = MumbleManager.update_user_password(authinfo.mumble_username, password=password)
|
||||
if result != "":
|
||||
logger.info("Successfully reset forum password for user %s" % request.user)
|
||||
@@ -788,7 +788,7 @@ def set_jabber_password(request):
|
||||
if form.is_valid():
|
||||
password = form.cleaned_data['password']
|
||||
logger.debug("Form contains password of length %s" % len(password))
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = OpenfireManager.update_user_pass(authinfo.jabber_username, password=password)
|
||||
if result != "":
|
||||
logger.info("Successfully set jabber password for user %s" % request.user)
|
||||
@@ -818,7 +818,7 @@ def set_ipboard_password(request):
|
||||
if form.is_valid():
|
||||
password = form.cleaned_data['password']
|
||||
logger.debug("Form contains password of length %s" % len(password))
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = IPBoardManager.update_user_password(authinfo.ipboard_username, request.user.email,
|
||||
plain_password=password)
|
||||
if result != "":
|
||||
@@ -841,7 +841,7 @@ def set_ipboard_password(request):
|
||||
@members_and_blues()
|
||||
def activate_ips4(request):
|
||||
logger.debug("activate_ips4 called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
# Valid now we get the main characters
|
||||
character = EveManager.get_character_by_id(authinfo.main_char_id)
|
||||
logger.debug("Adding IPS4 user for user %s with main character %s" % (request.user, character))
|
||||
@@ -869,7 +869,7 @@ def activate_ips4(request):
|
||||
@members_and_blues()
|
||||
def reset_ips4_password(request):
|
||||
logger.debug("reset_ips4_password called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = Ips4Manager.update_user_password(authinfo.ips4_username)
|
||||
# false we failed
|
||||
if result != "":
|
||||
@@ -898,7 +898,7 @@ def set_ips4_password(request):
|
||||
if form.is_valid():
|
||||
password = form.cleaned_data['password']
|
||||
logger.debug("Form contains password of length %s" % len(password))
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = Ips4Manager.update_custom_password(authinfo.ips4_username, plain_password=password)
|
||||
if result != "":
|
||||
logger.info("Successfully set IPS4 password for user %s" % request.user)
|
||||
@@ -920,7 +920,7 @@ def set_ips4_password(request):
|
||||
@members_and_blues()
|
||||
def deactivate_ips4(request):
|
||||
logger.debug("deactivate_ips4 called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = Ips4Manager.delete_user(authinfo.ips4_id)
|
||||
if result != "":
|
||||
AuthServicesInfoManager.update_user_ips4_info("", "", request.user)
|
||||
@@ -936,7 +936,7 @@ def deactivate_ips4(request):
|
||||
@members_and_blues()
|
||||
def activate_smf(request):
|
||||
logger.debug("activate_smf called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
# Valid now we get the main characters
|
||||
character = EveManager.get_character_by_id(authinfo.main_char_id)
|
||||
logger.debug("Adding smf user for user %s with main character %s" % (request.user, character))
|
||||
@@ -964,7 +964,7 @@ def activate_smf(request):
|
||||
@members_and_blues()
|
||||
def deactivate_smf(request):
|
||||
logger.debug("deactivate_smf called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = smfManager.disable_user(authinfo.smf_username)
|
||||
# false we failed
|
||||
if result:
|
||||
@@ -981,7 +981,7 @@ def deactivate_smf(request):
|
||||
@members_and_blues()
|
||||
def reset_smf_password(request):
|
||||
logger.debug("reset_smf_password called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = smfManager.update_user_password(authinfo.smf_username, authinfo.main_char_id)
|
||||
# false we failed
|
||||
if result != "":
|
||||
@@ -1010,7 +1010,7 @@ def set_smf_password(request):
|
||||
if form.is_valid():
|
||||
password = form.cleaned_data['password']
|
||||
logger.debug("Form contains password of length %s" % len(password))
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = smfManager.update_user_password(authinfo.smf_username, authinfo.main_char_id, password=password)
|
||||
if result != "":
|
||||
logger.info("Successfully set smf password for user %s" % request.user)
|
||||
@@ -1032,7 +1032,7 @@ def set_smf_password(request):
|
||||
@members_and_blues()
|
||||
def activate_market(request):
|
||||
logger.debug("activate_market called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
# Valid now we get the main characters
|
||||
character = EveManager.get_character_by_id(authinfo.main_char_id)
|
||||
logger.debug("Adding market user for user %s with main character %s" % (request.user, character))
|
||||
@@ -1060,7 +1060,7 @@ def activate_market(request):
|
||||
@members_and_blues()
|
||||
def deactivate_market(request):
|
||||
logger.debug("deactivate_market called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = marketManager.disable_user(authinfo.market_username)
|
||||
# false we failed
|
||||
if result:
|
||||
@@ -1077,7 +1077,7 @@ def deactivate_market(request):
|
||||
@members_and_blues()
|
||||
def reset_market_password(request):
|
||||
logger.debug("reset_market_password called by user %s" % request.user)
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = marketManager.update_user_password(authinfo.market_username)
|
||||
# false we failed
|
||||
if result != "":
|
||||
@@ -1106,7 +1106,7 @@ def set_market_password(request):
|
||||
if form.is_valid():
|
||||
password = form.cleaned_data['password']
|
||||
logger.debug("Form contains password of length %s" % len(password))
|
||||
authinfo = AuthServicesInfo.objects.get_or_create(user=request.user)[0]
|
||||
authinfo = AuthServicesInfo.objects.get(user=request.user)
|
||||
result = marketManager.update_custom_password(authinfo.market_username, password)
|
||||
if result != "":
|
||||
logger.info("Successfully reset market password for user %s" % request.user)
|
||||
@@ -1129,7 +1129,7 @@ def discourse_sso(request):
|
||||
|
||||
## Check if user has access
|
||||
|
||||
auth, c = AuthServicesInfo.objects.get_or_create(user=request.user)
|
||||
auth = AuthServicesInfo.objects.get(user=request.user)
|
||||
if not request.user.is_superuser:
|
||||
if not settings.ENABLE_AUTH_DISCOURSE and auth.state == MEMBER_STATE:
|
||||
messages.error(request, 'Members are not authorized to access Discourse.')
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user