automatically encrypt/decrypt main_oauth2application.client_secret

see: https://github.com/ansible/awx/issues/1416
This commit is contained in:
Ryan Petrello 2018-04-03 16:51:34 -04:00
parent c2446beb6e
commit 5f01d26224
No known key found for this signature in database
GPG Key ID: F2AA5F2122351777
4 changed files with 66 additions and 0 deletions

View File

@ -42,6 +42,7 @@ from rest_framework import serializers
# AWX
from awx.main.utils.filters import SmartFilter
from awx.main.utils.encryption import encrypt_value, decrypt_value, get_encryption_key
from awx.main.validators import validate_ssh_private_key
from awx.main.models.rbac import batch_role_ancestor_rebuilding, Role
from awx.main import utils
@ -821,3 +822,16 @@ class AskForField(models.BooleanField):
# self.name will be set by the model metaclass, not this field
raise Exception('Corresponding allows_field cannot be accessed until model is initialized.')
return self._allows_field
class OAuth2ClientSecretField(models.CharField):
def get_db_prep_value(self, value, connection, prepared=False):
return super(OAuth2ClientSecretField, self).get_db_prep_value(
encrypt_value(value), connection, prepared
)
def from_db_value(self, value, expression, connection, context):
if value.startswith('$encrypted$'):
return decrypt_value(get_encryption_key('value', pk=None), value)
return value

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.11 on 2018-04-03 20:48
from __future__ import unicode_literals
import awx.main.fields
from django.db import migrations
import oauth2_provider.generators
class Migration(migrations.Migration):
dependencies = [
('main', '0028_v330_modify_application'),
]
operations = [
migrations.AlterField(
model_name='oauth2application',
name='client_secret',
field=awx.main.fields.OAuth2ClientSecretField(blank=True, db_index=True, default=oauth2_provider.generators.generate_client_secret, max_length=1024),
),
]

View File

@ -9,6 +9,9 @@ from django.utils.translation import ugettext_lazy as _
# Django OAuth Toolkit
from oauth2_provider.models import AbstractApplication, AbstractAccessToken
from oauth2_provider.generators import generate_client_secret
from awx.main.fields import OAuth2ClientSecretField
DATA_URI_RE = re.compile(r'.*') # FIXME
@ -39,6 +42,10 @@ class OAuth2Application(AbstractApplication):
null=True,
)
client_secret = OAuth2ClientSecretField(
max_length=1024, blank=True, default=generate_client_secret, db_index=True
)
class OAuth2AccessToken(AbstractAccessToken):

View File

@ -1,6 +1,9 @@
import pytest
import base64
from django.db import connection
from awx.main.utils.encryption import decrypt_value, get_encryption_key
from awx.api.versioning import reverse, drf_reverse
from awx.main.models.oauth import (OAuth2Application as Application,
OAuth2AccessToken as AccessToken,
@ -65,6 +68,26 @@ def test_oauth_application_update(oauth_application, organization, patch, admin,
assert updated_app.organization == organization
@pytest.mark.django_db
def test_oauth_application_encryption(admin, organization, post):
response = post(
reverse('api:o_auth2_application_list'), {
'name': 'test app',
'organization': organization.pk,
'client_type': 'confidential',
'authorization_grant_type': 'password',
}, admin, expect=201
)
pk = response.data.get('id')
secret = response.data.get('client_secret')
with connection.cursor() as cursor:
encrypted = cursor.execute(
'SELECT client_secret FROM main_oauth2application WHERE id={}'.format(pk)
).fetchone()[0]
assert encrypted.startswith('$encrypted$')
assert decrypt_value(get_encryption_key('value', pk=None), encrypted) == secret
@pytest.mark.django_db
def test_oauth_token_create(oauth_application, get, post, admin):
response = post(