0001-add-compatibility-layer-for-support-of-Django-native.patch
src/authentic2/apps.py | ||
---|---|---|
3 | 3 |
from django.apps import AppConfig |
4 | 4 |
from django.views import debug |
5 | 5 | |
6 |
from . import plugins |
|
6 |
from django.db import connection |
|
7 |
from django.db.models.signals import post_migrate |
|
8 | ||
9 |
from . import plugins, compat |
|
7 | 10 | |
8 | 11 | |
9 | 12 |
class Authentic2Config(AppConfig): |
10 | 13 |
name = 'authentic2' |
11 | 14 |
verbose_name = 'Authentic2' |
12 | 15 | |
16 |
def post_migrate_update_json_column(self, sender, **kwargs): |
|
17 |
# adapted from https://github.com/kbussell/django-jsonfield-compat/blob/4f6ac4bfaea2224559b174b6d16d846b93d125c6/jsonfield_compat/convert.py |
|
18 |
# MIT License, kbussel |
|
19 |
if connection.vendor != 'postgresql': |
|
20 |
return |
|
21 | ||
22 |
if compat.has_postgresql_support(): |
|
23 |
expected_type = 'JSONB' |
|
24 |
else: |
|
25 |
expected_type = 'TEXT' |
|
26 | ||
27 | ||
28 |
def convert_column_to_json(model, column_name): |
|
29 |
table_name = model._meta.db_table |
|
30 | ||
31 |
with connection.cursor() as cursor: |
|
32 |
cursor.execute( |
|
33 |
"select data_type from information_schema.columns " |
|
34 |
"where table_name = %s and column_name = %s;", |
|
35 |
[table_name, column_name]) |
|
36 | ||
37 |
current_type = cursor.fetchone()[0].upper() |
|
38 | ||
39 |
if current_type != expected_type: |
|
40 |
print("{app}: Converting {col} to use native {type} field".format( |
|
41 |
app=model._meta.app_label, col=column_name, type=expected_type)) |
|
42 | ||
43 |
cursor.execute( |
|
44 |
"ALTER TABLE {table} ALTER COLUMN {col} " |
|
45 |
"TYPE {type} USING {col}::{type};".format( |
|
46 |
table=table_name, col=column_name, type=expected_type |
|
47 |
) |
|
48 |
) |
|
49 | ||
50 |
def convert_model_json_fields(model): |
|
51 |
json_fields = [f for f in model._meta.fields if f.__class__ == compat.JSONField] |
|
52 |
for field in json_fields: |
|
53 |
_, column_name = field.get_attname_column() |
|
54 |
convert_column_to_json(model, column_name) |
|
55 | ||
56 |
for model in list(sender.get_models()): |
|
57 |
convert_model_json_fields(model) |
|
58 | ||
13 | 59 |
def ready(self): |
14 | 60 |
plugins.init() |
15 | 61 |
debug.HIDDEN_SETTINGS = re.compile( |
16 | 62 |
'API|TOKEN|KEY|SECRET|PASS|PROFANITIES_LIST|SIGNATURE|LDAP') |
63 |
post_migrate.connect(self.post_migrate_update_json_column) |
src/authentic2/compat.py | ||
---|---|---|
1 | 1 |
from datetime import datetime |
2 |
import inspect |
|
2 | 3 | |
4 |
import django |
|
3 | 5 |
from django.conf import settings |
6 |
from django.db import connection |
|
7 | ||
8 |
from django.contrib.auth.tokens import PasswordResetTokenGenerator |
|
4 | 9 | |
5 | 10 |
try: |
6 | 11 |
from django.contrib.auth import get_user_model |
... | ... | |
14 | 19 |
except ImportError: |
15 | 20 |
from django.db.transaction import commit_on_success |
16 | 21 | |
17 |
from . import app_settings, utils |
|
18 | ||
19 | 22 |
user_model_label = getattr(settings, 'AUTH_USER_MODEL', 'auth.User') |
20 | 23 | |
21 |
from django.contrib.auth.tokens import PasswordResetTokenGenerator |
|
22 | ||
23 | 24 |
default_token_generator = PasswordResetTokenGenerator() |
25 | ||
26 | ||
27 |
def has_postgresql_support(): |
|
28 |
if not settings.DATABASES['default'].get('NAME'): |
|
29 |
return False |
|
30 |
return connection.vendor == 'postgresql' and connection.pg_version > 90400 |
|
31 | ||
32 | ||
33 |
def use_django_native_field(): |
|
34 |
return has_postgresql_support() and django.VERSION >= (1, 11) |
|
35 | ||
36 | ||
37 |
class JSONField(object): |
|
38 |
__dj11_field = None |
|
39 |
__jsonfield_field = None |
|
40 |
__name = None |
|
41 | ||
42 |
def __init__(self, *args, **kwargs): |
|
43 |
self.__args = args |
|
44 |
self.__kwargs = kwargs |
|
45 |
if django.VERSION >= (1, 11): |
|
46 |
from django.contrib.postgres.fields import JSONField |
|
47 |
self.__dj11_field = JSONField(*args, **kwargs) |
|
48 |
try: |
|
49 |
from jsonfield.fields import JSONField |
|
50 |
self.__jsonfield_field = JSONField(*args, **kwargs) |
|
51 |
except ImportError: |
|
52 |
pass |
|
53 | ||
54 |
def __real_field__(self): |
|
55 |
if use_django_native_field(): |
|
56 |
assert self.__dj11_field |
|
57 |
return self.__dj11_field |
|
58 |
assert self.__jsonfield_field |
|
59 |
return self.__jsonfield_field |
|
60 | ||
61 |
def __getattr__(self, key): |
|
62 |
return getattr(self.__real_field__(), key) |
|
63 | ||
64 |
def __setattr__(self, key, value): |
|
65 |
if key.startswith('_JSONField__'): |
|
66 |
super(JSONField, self).__setattr__(key, value) |
|
67 |
else: |
|
68 |
setattr(self.__real__field(), key, value) |
|
69 | ||
70 |
# we need to implement contribute_to_class so that the direct |
|
71 |
# implementation from the two sub-fields is not used directly |
|
72 |
def contribute_to_class(self, cls, name, private_only=False, virtual_only=False, **kwargs): |
|
73 |
assert not virtual_only and not private_only, 'virtual_only / private_only are not supported' |
|
74 |
assert not kwargs, 'new arguments to contribute_to_class not supported' |
|
75 |
self.__name = name |
|
76 |
if self.__dj11_field: |
|
77 |
self.__dj11_field.set_attributes_from_name(name) |
|
78 |
self.__dj11_field.model = cls |
|
79 |
if self.__jsonfield_field: |
|
80 |
self.__jsonfield_field.set_attributes_from_name(name) |
|
81 |
self.__jsonfield_field.model = cls |
|
82 |
cls._meta.add_field(self) |
|
83 | ||
84 |
# the next two methods are useful for compatibilit with the migration engine |
|
85 |
# inspect is used because migration autodetector cannot recognize this class |
|
86 |
# as a subclass of models.Field. |
|
87 |
def deconstruct(self): |
|
88 |
d = (self.__name, 'authentic2.compat.JSONField', self.__args, self.__kwargs) |
|
89 |
previous_frame = inspect.currentframe().f_back |
|
90 |
if inspect.getframeinfo(previous_frame)[2] in ('serialize', 'deep_deconstruct'): |
|
91 |
d = d[1:] |
|
92 |
return d |
|
93 | ||
94 |
def clone(self): |
|
95 |
from copy import copy |
|
96 |
new = copy(self) |
|
97 |
if self.__dj11_field: |
|
98 |
new.__dj11_field = new.__dj11_field.clone() |
|
99 |
if self.__jsonfield_field: |
|
100 |
new.__jsonfield_field = new.__jsonfield_field.clone() |
|
101 |
return new |
|
102 | ||
103 | ||
104 |
try: |
|
105 |
from jsonfield import fields |
|
106 |
except ImportError: |
|
107 |
pass |
|
108 |
else: |
|
109 |
# prevent django-jsonfield from modifying postgresql connection when we are |
|
110 |
# not using it |
|
111 |
def configure_database_connection(connection, **kwargs): |
|
112 |
if django.VERSION < (1, 11): |
|
113 |
fields.configure_database_connection(connection, **kwargs) |
|
114 |
fields.connection_created.disconnect(fields.configure_database_connection) |
|
115 |
fields.connection_created.connect(configure_database_connection) |
src/authentic2_auth_oidc/migrations/0007_auto_20181219_0005.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# Generated by Django 1.11.17 on 2018-12-18 23:05 |
|
3 |
from __future__ import unicode_literals |
|
4 | ||
5 |
import authentic2.compat |
|
6 |
import authentic2_auth_oidc.models |
|
7 |
from django.db import migrations |
|
8 | ||
9 | ||
10 |
class Migration(migrations.Migration): |
|
11 | ||
12 |
dependencies = [ |
|
13 |
('authentic2_auth_oidc', '0006_oidcprovider_claims_parameter_supported'), |
|
14 |
] |
|
15 | ||
16 |
operations = [ |
|
17 |
migrations.AlterField( |
|
18 |
model_name='oidcprovider', |
|
19 |
name='jwkset_json', |
|
20 |
field=authentic2.compat.JSONField(blank=True, null=True, validators=[authentic2_auth_oidc.models.validate_jwkset], verbose_name='JSON WebKey set'), |
|
21 |
), |
|
22 |
] |
src/authentic2_auth_oidc/models.py | ||
---|---|---|
6 | 6 |
from django.conf import settings |
7 | 7 |
from django.core.exceptions import ValidationError |
8 | 8 | |
9 |
from jsonfield import JSONField |
|
10 | 9 | |
11 | 10 |
from jwcrypto.jwk import JWKSet, InvalidJWKValue, JWK |
12 | 11 | |
13 | 12 |
from django_rbac.utils import get_ou_model_name |
14 | 13 | |
14 |
from authentic2 import compat |
|
15 | ||
15 | 16 |
from . import managers |
16 | 17 | |
17 | 18 | |
... | ... | |
89 | 90 |
max_length=128, |
90 | 91 |
blank=True, |
91 | 92 |
verbose_name=_('scopes')) |
92 |
jwkset_json = JSONField( |
|
93 |
jwkset_json = compat.JSONField(
|
|
93 | 94 |
verbose_name=_('JSON WebKey set'), |
94 | 95 |
null=True, |
95 | 96 |
blank=True, |
tox.ini | ||
---|---|---|
31 | 31 |
dj111: django<2.0 |
32 | 32 |
dj111: django-tables<2.0 |
33 | 33 |
pg: psycopg2-binary |
34 |
dj111: psycopg2-binary |
|
34 | 35 |
coverage |
35 | 36 |
pytest-cov |
36 | 37 |
pytest-django |
37 |
- |