Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
David Seaward
Keel - LDH Middleware
Commits
7bdc24d8
Commit
7bdc24d8
authored
Jan 19, 2018
by
David Seaward
Committed by
Gogs
Jan 19, 2018
Browse files
Merge branch 'int_collection_field' of david.seaward/purist_middleware into master
parents
644b07ef
5c8cb10a
Changes
18
Hide whitespace changes
Inline
Side-by-side
SETUP.md
View file @
7bdc24d8
...
...
@@ -25,7 +25,6 @@ Prerequisites
*
[
REST API enabled
](
https://docs.woocommerce.com/document/woocommerce-rest-api/
)
*
[
WooCommerce Subscriptions
](
https://woocommerce.com/products/woocommerce-subscriptions/
)
*
[
JWT Authentication for WP REST API
](
https://wordpress.org/plugins/jwt-authentication-for-wp-rest-api/
)
(pending)
*
OpenVPN instance (SSH access)
*
Including
[
openvpn-confgen
](
https://plan.puri.st/module/openvpn_confgen
)
*
Typically, the Nginx user (
`www-data`
) will need SSH access
...
...
@@ -88,16 +87,24 @@ Setup
*
`sudo service nginx restart`
*
`sudo service supervisor restart`
*
Check logs:
*
`/var/log/uwsgi/emperor.log`
*
`/var/log/uwsgi/app/purist_middleware.log`
*
`/var/log/nginx/error.log`
*
`/var/log/nginx/access.log`
*
`/var/log/
supervisor/supervis
or
d
.log`
*
`/var/log/
nginx/err
or.log`
*
`/var/log/purist/middleware/beat.log`
*
`/var/log/supervisor/supervisord.log`
*
`/var/log/uwsgi/emperor.log`
*
`/var/log/uwsgi/app/purist_middleware.log`
For more options and details see
<https://docs.djangoproject.com/en/1.11/#the-development-process>
Configure
---------
*
Log in to admin interface as superuser
*
Define intervals in Django_Celery_Beat > Intervals
*
Define periodic tasks in Django_Celery_Beat > Periodic tasks
*
Define known products in Limit Monitor > External bundles
Update
------
...
...
conf/etc/config.ini
View file @
7bdc24d8
...
...
@@ -11,6 +11,7 @@ DEBUG=True
DEBUG_ALL_ACCESS
=
True
DEBUG_CHANGE_PASSWORD
=
False
DEBUG_SKIP_ACTIVATION_COMMAND
=
True
DEBUG_SKIP_VALIDATE_ON_AUTHENTICATION
=
False
# change to false after initial setup
ALLOWED_HOSTS
=
localhost
STATIC_ROOT
=
/var/opt/purist/middleware/static
...
...
@@ -28,11 +29,10 @@ STATICFILES_DIRS=/var/opt/purist/brand,/var/opt/purist/downloads
WOO_URL
=
https://example.com
WOO_WP_API
=
True
WOO_VERSION
=
wc/v1
WOO_
PRODUCT_LIST
=
123,124
WOO
1_FIELD_LIST
=
Existing username,Username
WOO_
QUERY_STRING_AUTH
=
True
WOO
SUB1_PRODUCT_LIST
=
123,124
OVPN_HOSTNAME
=
ssh.example.com
OVPN_PORT
=
22
OVPN_USERNAME
=
username
OVPN_FILEPATH
=
"/path/to/{IDENTITY}/{IDENTITY}.ovpn"
SUBSCRIPTION_LINK
=
https://www.example.com
limitmonitor/jinja2/limitmonitor/userlimit.html
View file @
7bdc24d8
...
...
@@ -69,7 +69,7 @@ SPDX-License-Identifier: AGPL-3.0
{% for limit in limits %}
<tr>
<th>
{{ limit.service_label() }}
</th>
<th
class=
"row_header"
>
{{ limit.service_label() }}
</th>
<td>
{{ limit.active_label() }}
</td>
<td
title=
"{{ limit.endpoint_full_label() }}"
>
{{ limit.endpoint_short_label() }}
</td>
<td>
{{ limit.credit_label() }}
</td>
...
...
@@ -80,15 +80,6 @@ SPDX-License-Identifier: AGPL-3.0
</table>
<h2>
{% trans "Profile management" %}
</h2>
<ul>
{% if DEBUG_CHANGE_PASSWORD %}
<li><a
href=
"{% url 'auth_password_change' %}"
>
{% trans "Change password" %}
</a></li>
<li><a
href=
"{{ link_subscription }}"
target=
"_blank"
>
{% trans "Manage subscriptions" %}
</a></li>
{% endif %}
</ul>
<h2>
{% trans "Downloads" %}
</h2>
<ul>
...
...
@@ -103,6 +94,14 @@ SPDX-License-Identifier: AGPL-3.0
{% endif %}
</ul>
{% if DEBUG_CHANGE_PASSWORD %}
<h2>
{% trans "Profile management" %}
</h2>
<ul>
<li><a
href=
"{% url 'auth_password_change' %}"
>
{% trans "Change password" %}
</a></li>
</ul>
{% endif %}
</article>
<nav>
...
...
limitmonitor/migrations/0003_auto_20171208_1327.py
View file @
7bdc24d8
...
...
@@ -17,25 +17,25 @@ class Migration(migrations.Migration):
migrations
.
AlterField
(
model_name
=
'externalbundle'
,
name
=
'parser'
,
field
=
choicesenum
.
django
.
fields
.
EnumIntegerField
(
default
=
limitmonitor
.
models
.
ExternalParser
(
0
),
enum
=
limitmonitor
.
models
.
ExternalParser
),
field
=
choicesenum
.
django
.
fields
.
EnumIntegerField
(
default
=
limitmonitor
.
models
.
Legacy
(
0
),
enum
=
limitmonitor
.
models
.
Legacy
),
),
migrations
.
AlterField
(
model_name
=
'externalbundle'
,
name
=
'service'
,
field
=
choicesenum
.
django
.
fields
.
EnumIntegerField
(
default
=
limitmonitor
.
models
.
Service
(
0
),
enum
=
limitmonitor
.
models
.
Service
),
field
=
choicesenum
.
django
.
fields
.
EnumIntegerField
(
default
=
limitmonitor
.
models
.
Legacy
(
0
),
enum
=
limitmonitor
.
models
.
Legacy
),
),
migrations
.
AlterField
(
model_name
=
'externalcredit'
,
name
=
'parser'
,
field
=
choicesenum
.
django
.
fields
.
EnumIntegerField
(
default
=
limitmonitor
.
models
.
ExternalParser
(
0
),
enum
=
limitmonitor
.
models
.
ExternalParser
),
field
=
choicesenum
.
django
.
fields
.
EnumIntegerField
(
default
=
limitmonitor
.
models
.
Legacy
(
0
),
enum
=
limitmonitor
.
models
.
Legacy
),
),
migrations
.
AlterField
(
model_name
=
'limit'
,
name
=
'service'
,
field
=
choicesenum
.
django
.
fields
.
EnumIntegerField
(
default
=
limitmonitor
.
models
.
Service
(
0
),
enum
=
limitmonitor
.
models
.
Service
),
field
=
choicesenum
.
django
.
fields
.
EnumIntegerField
(
default
=
limitmonitor
.
models
.
Legacy
(
0
),
enum
=
limitmonitor
.
models
.
Legacy
),
),
]
limitmonitor/migrations/0004_auto_20180109_1139.py
0 → 100644
View file @
7bdc24d8
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-01-09 11:39
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'limitmonitor'
,
'0003_auto_20171208_1327'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'externalbundle'
,
name
=
'service'
,
field
=
models
.
IntegerField
(
choices
=
[(
0
,
'Undefined'
)],
default
=
0
),
),
migrations
.
AlterField
(
model_name
=
'limit'
,
name
=
'service'
,
field
=
models
.
IntegerField
(
choices
=
[(
0
,
'Undefined'
)],
default
=
0
),
),
]
limitmonitor/migrations/0005_auto_20180109_1211.py
0 → 100644
View file @
7bdc24d8
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-01-09 12:11
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'limitmonitor'
,
'0004_auto_20180109_1139'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'externalbundle'
,
name
=
'parser'
,
field
=
models
.
IntegerField
(
choices
=
[(
0
,
'Undefined'
)],
default
=
0
),
),
migrations
.
AlterField
(
model_name
=
'externalcredit'
,
name
=
'parser'
,
field
=
models
.
IntegerField
(
choices
=
[(
0
,
'Undefined'
)],
default
=
0
),
),
]
limitmonitor/models.py
View file @
7bdc24d8
from
choicesenum
import
ChoicesEnum
from
choicesenum.django.fields
import
EnumIntegerField
from
django.conf
import
settings
from
django.db
import
models
from
django.utils
import
timezone
class
ExternalParser
(
ChoicesEnum
):
class
Legacy
(
ChoicesEnum
):
UNDEFINED
=
0
,
"Undefined"
WOO1
=
1
,
"WooCommerce v1"
WOOSUB1
=
2
,
"WooCommerce Subscription v1"
class
Service
(
ChoicesEnum
):
UNDEFINED
=
0
,
"Undefined"
TUNNEL
=
1
,
"Tunnel"
COMMUNICATION
=
2
,
"Communication"
def
create_missing_user_limits
(
user
):
for
code
,
label
in
Service
.
choice
s
():
is_
un
defined
=
code
=
=
Service
.
UNDEFINED
.
value
for
code
in
settings
.
LM_SERVICES
.
MAP
.
key
s
():
is_defined
=
code
!
=
settings
.
LM_SERVICES
.
UNDEFINED
is_exists
=
Limit
.
objects
.
filter
(
user
=
user
,
service
=
code
).
exists
()
if
not
is_
un
defined
and
not
is_exists
:
if
is_defined
and
not
is_exists
:
Limit
(
user
=
user
,
service
=
code
).
save
()
class
Limit
(
models
.
Model
):
user
=
models
.
ForeignKey
(
settings
.
AUTH_USER_MODEL
)
service
=
EnumIntegerField
(
enum
=
Service
,
default
=
Service
.
UNDEFINED
)
service
=
models
.
IntegerField
(
default
=
settings
.
LM_SERVICES
.
UNDEFINED
,
choices
=
settings
.
LM_SERVICES
.
choices
())
renewal_date
=
models
.
DateTimeField
(
default
=
None
,
blank
=
True
,
null
=
True
)
expiry_date
=
models
.
DateTimeField
(
default
=
None
,
blank
=
True
,
null
=
True
)
volume_total
=
models
.
DecimalField
(
default
=
0
,
decimal_places
=
2
,
max_digits
=
6
)
...
...
@@ -38,12 +30,7 @@ class Limit(models.Model):
def
service_label
(
self
):
label
=
"None"
for
key
,
value
in
Service
.
choices
():
if
self
.
service
==
key
:
label
=
value
return
label
return
settings
.
LM_SERVICES
.
get_name_by_code
(
self
.
service
)
def
active_label
(
self
):
...
...
@@ -77,7 +64,7 @@ class Limit(models.Model):
def
credit_label
(
self
):
if
self
.
service
==
Service
.
TUNNEL
:
if
self
.
service
==
settings
.
LM_SERVICES
.
TUNNEL
:
return
self
.
days_credit_label
()
else
:
return
self
.
days_credit_label
()
...
...
@@ -118,9 +105,11 @@ class Limit(models.Model):
class
ExternalBundle
(
models
.
Model
):
parser
=
EnumIntegerField
(
enum
=
ExternalParser
,
default
=
ExternalParser
.
UNDEFINED
)
parser
=
models
.
IntegerField
(
default
=
settings
.
LM_PARSERS
.
UNDEFINED
,
choices
=
settings
.
LM_PARSERS
.
choices
())
external_key
=
models
.
CharField
(
max_length
=
30
)
service
=
EnumIntegerField
(
enum
=
Service
,
default
=
Service
.
UNDEFINED
)
service
=
models
.
IntegerField
(
default
=
settings
.
LM_SERVICES
.
UNDEFINED
,
choices
=
settings
.
LM_SERVICES
.
choices
())
time_credit
=
models
.
DecimalField
(
default
=
0
,
decimal_places
=
2
,
max_digits
=
6
)
volume_credit
=
models
.
DecimalField
(
default
=
0
,
decimal_places
=
2
,
max_digits
=
6
)
created_date
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
...
...
@@ -128,7 +117,8 @@ class ExternalBundle(models.Model):
class
ExternalCredit
(
models
.
Model
):
parser
=
EnumIntegerField
(
enum
=
ExternalParser
,
default
=
ExternalParser
.
UNDEFINED
)
parser
=
models
.
IntegerField
(
default
=
settings
.
LM_PARSERS
.
UNDEFINED
,
choices
=
settings
.
LM_PARSERS
.
choices
())
external_key
=
models
.
CharField
(
max_length
=
30
)
label
=
models
.
CharField
(
max_length
=
30
)
bundle_key
=
models
.
CharField
(
max_length
=
30
)
...
...
@@ -141,13 +131,17 @@ class ExternalCredit(models.Model):
created_date
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
updated_date
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
@
property
def
parser_name
(
self
):
return
settings
.
LM_PARSERS
.
get_name_by_code
(
self
.
parser
)
@
property
def
external_code
(
self
):
return
str
(
self
.
parser
)
+
":"
+
str
(
self
.
external_key
)
return
self
.
parser
_name
+
":"
+
str
(
self
.
external_key
)
@
property
def
external_bundle
(
self
):
return
str
(
self
.
parser
)
+
":"
+
str
(
self
.
external_key
)
return
self
.
parser
_name
+
":"
+
str
(
self
.
external_key
)
class
Credit
(
models
.
Model
):
...
...
limitmonitor/task_resources/common.py
View file @
7bdc24d8
...
...
@@ -8,7 +8,7 @@ from django.db import transaction
from
django.utils
import
timezone
from
woocommerce
import
API
as
WOO_API
from
..models
import
ExternalCredit
,
ExternalBundle
,
Limit
,
Service
from
..models
import
ExternalCredit
,
ExternalBundle
,
Limit
logger
=
get_task_logger
(
__name__
)
...
...
@@ -20,9 +20,25 @@ def get_woo_connection():
consumer_secret
=
settings
.
WOO_CONSUMER_SECRET
,
wp_api
=
settings
.
WOO_WP_API
,
version
=
settings
.
WOO_VERSION
,
query_string_auth
=
settings
.
WOO_QUERY_STRING_AUTH
,
)
def
get_username_from_woo_customer_id
(
customer_id
,
woo
=
None
):
if
woo
is
None
:
woo
=
get_woo_connection
()
try
:
query
=
"customers/"
+
str
(
customer_id
)
result
=
woo
.
get
(
query
).
json
()
return
result
[
"username"
]
+
"@"
+
settings
.
SITE_DOMAIN
except
Exception
as
e
:
logger
.
exception
(
"Could not retrieve username for customer_id "
+
str
(
customer_id
))
return
"invalid"
return
account
def
get_openvpn_ssh_connection
():
# make ssh connection to OpenVPN server
# (uses system host keys, warns if host is not recognised)
...
...
@@ -101,7 +117,7 @@ def activate(ssh, limit, credit_timedelta=None, renewal_date=None):
# otherwise, activate the limit before saving
if
limit
.
service
==
Service
.
TUNNEL
:
if
limit
.
service
==
settings
.
LM_SERVICES
.
TUNNEL
:
user_identity
=
limit
.
user
.
get_identity
()
filepath
=
settings
.
OVPN_FILEPATH
.
replace
(
"{USER_IDENTITY}"
,
user_identity
)
is_file
=
pathlib
.
Path
(
filepath
).
is_file
()
...
...
@@ -117,31 +133,13 @@ def activate(ssh, limit, credit_timedelta=None, renewal_date=None):
def
deactivate
(
ssh
,
limit
):
if
limit
.
service
==
Service
.
TUNNEL
:
if
limit
.
service
==
settings
.
LM_SERVICES
.
TUNNEL
:
managed_exec
(
ssh
,
"./create_new_ovpn_config --revoke %s"
%
(
limit
.
user
.
get_identity
(),))
limit
.
is_active
=
False
limit
.
save
()
def
get_account_from_woo_meta
(
meta_list
):
account
=
"invalid"
for
meta_item
in
meta_list
:
if
meta_item
[
"key"
]
in
settings
.
WOO1_FIELD_LIST
:
# for example, "Existing username,"
account
=
meta_item
[
"value"
]
at_count
=
account
.
count
(
"@"
)
if
at_count
==
0
:
# nodomain
account
+=
"@"
+
settings
.
SITE_DOMAIN
# corrected to nodomain@example.com
elif
at_count
==
1
:
pass
# valid account format
else
:
# at_count > 1, for example bad@user@example.com
account
=
account
.
replace
(
"@"
,
".AT."
)
# force invalid name bad.AT.user.AT.example.com
return
account
def
get_limit_objects
(
credit
):
# get and validate local username
...
...
@@ -174,22 +172,22 @@ def store_credit_and_update_limit(ssh, credit, next_renewal=None):
if
credit
.
parser
==
"WOO1"
:
from
.tunnel_credit
import
update_limit_woo1
update_limit_woo1
(
ssh
,
credit
)
elif
credit
.
parser
==
"
WOOSUB
1"
:
elif
credit
.
parser
==
2
:
# FIXME: this is
WOO
_
SUB
SCRIPTION_V1 from purist.limitmonitor
from
.tunnel_subscription
import
update_limit_woosub1
update_limit_woosub1
(
ssh
,
credit
,
next_renewal
)
else
:
raise
Exception
(
"Unrecognised
p
arser "
+
credit
.
parser
)
raise
Exception
(
"Unrecognised
P
arser "
+
str
(
credit
.
parser
)
)
credit
.
is_converted
=
True
credit
.
error_message
=
""
except
Exception
as
e
:
message
=
"Skipped adding credit
"
+
credit
.
parser
+
":"
+
credit
.
external_key
+
". "
message
=
"Skipped adding credit
Parser "
+
str
(
credit
.
parser
)
+
":
"
+
credit
.
external_key
+
". "
logger
.
exception
(
message
)
credit
.
error_message
=
message
+
repr
(
e
)
finally
:
credit
.
save
()
state
=
"converted"
if
credit
.
is_converted
else
"skipped"
logger
.
info
(
"Stored "
+
state
+
" credit
"
+
credit
.
parser
+
":"
+
credit
.
external_key
)
logger
.
info
(
"Stored "
+
state
+
" credit
Parser "
+
str
(
credit
.
parser
)
+
":
"
+
credit
.
external_key
)
def
deactivate_all_expired_limits
():
...
...
limitmonitor/task_resources/tunnel_credit.py
View file @
7bdc24d8
...
...
@@ -17,7 +17,7 @@ def parse_woo1(json_entry, product_id):
product_id
=
line_item
[
"product_id"
]
product_label
=
line_item
[
"name"
]
quantity
=
line_item
[
"quantity"
]
account
=
get_account_from_woo_meta
(
line_item
[
"meta"
])
account
=
None
# FIXME: need to get the account
external_key
=
str
(
order_id
)
+
":"
+
str
(
item_id
)
external_label
=
str
(
order_name
)
...
...
limitmonitor/task_resources/tunnel_subscription.py
View file @
7bdc24d8
...
...
@@ -23,16 +23,19 @@ def parse_woosub1(json_entry):
next_renewal_naive
=
datetime
.
datetime
.
strptime
(
json_entry
[
"next_payment_date"
],
"%Y-%m-%dT%H:%M:%S"
)
next_renewal
=
timezone
.
make_aware
(
next_renewal_naive
)
# get account name
username
=
get_username_from_woo_customer_id
(
json_entry
[
"customer_id"
])
# create result
result
=
{
"parser"
:
"
WOOSUB
1"
,
"parser"
:
2
,
# FIXME: this is
WOO
_
SUB
SCRIPTION_V1 from purist.limitmonitor
"external_key"
:
subscription_id
,
"label"
:
json_entry
[
"number"
],
"bundle_key"
:
str
(
line_item
[
"product_id"
]),
"bundle_label"
:
str
(
line_item
[
"name"
]),
"quantity"
:
1
,
"account"
:
get_account_from_woo_meta
(
line_item
[
"meta"
])
,
"account"
:
username
,
"next_renewal"
:
next_renewal
,
}
...
...
@@ -52,7 +55,9 @@ def monitor_woosub1_new_subscriptions():
# parse recent subscriptions and store results
for
json_entry
in
latest_subscription_json
:
try
:
result_list
.
extend
(
parse_woosub1
(
json_entry
))
product_id
=
int
(
json_entry
[
"line_items"
][
0
][
"product_id"
])
if
product_id
in
settings
.
WOOSUB1_PRODUCT_LIST
:
result_list
.
extend
(
parse_woosub1
(
json_entry
))
except
Exception
as
e
:
logger
.
exception
(
"Skipping JSON entry "
+
str
(
json_entry
))
...
...
limitmonitor/views.py
View file @
7bdc24d8
...
...
@@ -15,7 +15,8 @@ def userlimit(request):
has_limit
=
{}
none_limit
=
True
for
limit
in
limits
:
has_limit
[
limit
.
service
]
=
limit
.
is_active
label
=
limit
.
service_label
().
upper
()
has_limit
[
label
]
=
limit
.
is_active
if
limit
.
is_active
:
none_limit
=
False
...
...
@@ -31,7 +32,6 @@ def userlimit(request):
"site_provider_link"
:
settings
.
SITE_PROVIDER_LINK
,
"limits"
:
limits
,
"has_limit"
:
has_limit
,
"link_subscription"
:
settings
.
LINK_SUBSCRIPTION
,
"link_profile_ordered_dict"
:
settings
.
LINK_PROFILE_ORDERED_DICT
,
}
...
...
middleware/settings.py
View file @
7bdc24d8
...
...
@@ -3,6 +3,7 @@ import strictyaml
from
decouple
import
Config
,
Csv
,
RepositoryIni
from
django_auth_ldap.config
import
LDAPSearch
import
purist.limitmonitor
from
.settings_original
import
*
#
...
...
@@ -28,9 +29,12 @@ SECRET_KEY = secret_config("DJANGO_SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG
=
config
(
"DEBUG"
,
cast
=
bool
)
# it is safe to use these flags in production
DEBUG_ALL_ACCESS
=
config
(
"DEBUG_ALL_ACCESS"
,
cast
=
bool
)
DEBUG_CHANGE_PASSWORD
=
config
(
"DEBUG_CHANGE_PASSWORD"
,
cast
=
bool
)
DEBUG_SKIP_ACTIVATION_COMMAND
=
config
(
"DEBUG_SKIP_ACTIVATION_COMMAND"
,
cast
=
bool
)
DEBUG_SKIP_VALIDATE_ON_AUTHENTICATION
=
config
(
"DEBUG_SKIP_VALIDATE_ON_AUTHENTICATION"
,
cast
=
bool
)
# Required if DEBUG is False
ALLOWED_HOSTS
=
config
(
"ALLOWED_HOSTS"
,
cast
=
Csv
())
...
...
@@ -133,8 +137,6 @@ SITE_DOMAIN = config("SITE_DOMAIN")
SITE_PROVIDER
=
config
(
"SITE_PROVIDER"
)
SITE_PROVIDER_LINK
=
config
(
"SITE_PROVIDER_LINK"
)
LINK_SUBSCRIPTION
=
config
(
"LINK_SUBSCRIPTION"
)
#
# WOOCOMMERCE
#
...
...
@@ -142,17 +144,16 @@ LINK_SUBSCRIPTION = config("LINK_SUBSCRIPTION")
WOO_URL
=
config
(
"WOO_URL"
)
WOO_WP_API
=
config
(
"WOO_WP_API"
,
cast
=
bool
)
WOO_VERSION
=
config
(
"WOO_VERSION"
)
WOO_QUERY_STRING_AUTH
=
config
(
"WOO_QUERY_STRING_AUTH"
,
cast
=
bool
)
# required for OAuth over HTTPS
WOO_CONSUMER_KEY
=
secret_config
(
"WOO_CONSUMER_KEY"
)
WOO_CONSUMER_SECRET
=
secret_config
(
"WOO_CONSUMER_SECRET"
)
WOO_PRODUCT_LIST
=
config
(
"WOO_PRODUCT_LIST"
,
cast
=
Csv
(
int
))
#
# WOO1 PARSER
# WOO
SUB
1 PARSER
#
WOO
1_FIELD
_LIST
=
config
(
"WOO
1_FIELD
_LIST"
,
cast
=
Csv
())
WOO
SUB1_PRODUCT
_LIST
=
config
(
"WOO
SUB1_PRODUCT
_LIST"
,
cast
=
Csv
(
int
))
#
# SSH CONNECTION TO OPENVPN SERVER
...
...
@@ -163,6 +164,13 @@ OVPN_PORT = config("OVPN_PORT", cast=int)
OVPN_USERNAME
=
config
(
"OVPN_USERNAME"
)
OVPN_FILEPATH
=
config
(
"OVPN_FILEPATH"
)
#
# LIMIT MONITOR
#
LM_SERVICES
=
purist
.
limitmonitor
.
ServicesContainer
LM_PARSERS
=
purist
.
limitmonitor
.
ParserContainer
#
# LOGGING
#
...
...
purist/custom.py
View file @
7bdc24d8
import
logging
from
django.conf
import
settings
from
django.contrib.auth.password_validation
import
MinimumLengthValidator
as
BaseValidator
from
django.core
import
validators
from
django.utils.deconstruct
import
deconstructible
from
django.utils.translation
import
ugettext_lazy
as
_
from
django_auth_ldap.backend
import
LDAPBackend
as
BaseBackend
from
woocommerce
import
API
as
WOOCOMMERCE_API
from
.models
import
User
,
UsernameValidator
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -14,21 +16,81 @@ class AuthenticationBackend(BaseBackend):
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
AuthenticationBackend
,
self
).
__init__
(
*
args
,
**
kwargs
)
def
is_valid_jwt
(
self
,
username
=
None
,
password
=
None
):
is_valid
=
False
try
:
jwt_wcapi
=
WOOCOMMERCE_API
(
url
=
settings
.
WOO_URL
,
consumer_key
=
settings
.
WOO_CONSUMER_KEY
,
consumer_secret
=
settings
.
WOO_CONSUMER_SECRET
,
wp_api
=
True
,
version
=
"jwt-auth/v1"
,
# required for JWT Authentication
query_string_auth
=
settings
.
WOO_QUERY_STRING_AUTH
,
)
jwt_response
=
jwt_wcapi
.
post
(
"token"
,
{
"username"
:
username
,
"password"
:
password
})
jwt_status
=
jwt_response
.
status_code
jwt_token
=
jwt_response
.
json
().
get
(
"token"
,
None
)
jwt_code
=
jwt_response
.
json
().
get
(
"code"
,
None
)
known_codes
=
[
"[jwt_auth] incorrect_password"
,
"[jwt_auth] invalid_username"
]
if
jwt_status
==
200
and
jwt_token
is
not
None
:
is_valid
=
True
elif
jwt_status
==
403
and
jwt_code
in
known_codes
:
# recognised authentication failure
is_valid
=
False
else
:
# raise exception for an unrecognised failure
raise
Exception
(
"Unrecognised JWT response: %s"
%
(
jwt_response
.
json
(),))
except
Exception
as
e
:
logging
.
exception
(
"JWT authentication failed with an unrecognised error: %s"
%
(
e
,))
finally
:
return
is_valid
def
authenticate
(
self
,
request
=
None
,
username
=
None
,
password
=
None
,
**
kwargs
):
model
=
self
.
get_user_model
()
username
=
model
.
normalize_username
(
username
)
user_model
=
User
normalized_username
=
user_model
.
normalize_username
(
username
)
# first, validate username (even if it exists, username must be valid)
if
not
settings
.
DEBUG_SKIP_VALIDATE_ON_AUTHENTICATION
:
validator
=
UsernameValidator
()
validator
(
username
)
# second, attempt LDAP authentication (with early exit on success)
user
=
super
(
AuthenticationBackend
,
self
).
authenticate
(
request
,
normalized_username
,
password
,
**
kwargs
)
if
user
is
not
None
:
return
user
# third, attempt WooCommerce/JWT authentication
# (if successful, create and return LDAP user, otherwise return None)
if
self
.
is_valid_jwt
(
normalized_username
,
password
):
# TODO: also validate, so that existing but invalid usernames are not permitted?
# try to get a preexisting user object
# otherwise create a new one
return
super
(
AuthenticationBackend
,
self
).
authenticate
(
request
,
username
,
password
,
**
kwargs
)
try
:
user
=
user_model
.
objects
.
get
(
username
=
username
)
except
user_model
.
DoesNotExist
:
user
=
user_model
(
username
=
username
,
email
=
None
)
# update/set user details
user
.
email
=
user
.
get_identity
()
user
.
set_password
(
password
)