Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove /api/v1 and deprecated credential fields #3413

Merged
merged 6 commits into from
Jun 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 0 additions & 47 deletions awx/api/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,6 @@
# AWX
from awx.main.utils import get_type_for_model, to_python_boolean
from awx.main.utils.db import get_all_field_names
from awx.main.models.credential import CredentialType


class V1CredentialFilterBackend(BaseFilterBackend):
'''
For /api/v1/ requests, filter out v2 (custom) credentials
'''

def filter_queryset(self, request, queryset, view):
# TODO: remove in 3.3
from awx.api.versioning import get_request_version
if get_request_version(request) == 1:
queryset = queryset.filter(credential_type__managed_by_tower=True)
return queryset


class TypeFilterBackend(BaseFilterBackend):
Expand Down Expand Up @@ -292,39 +278,6 @@ def filter_queryset(self, request, queryset, view):
key = key[5:]
q_not = True

# Make legacy v1 Job/Template fields work for backwards compatability
# TODO: remove after API v1 deprecation period
if queryset.model._meta.object_name in ('JobTemplate', 'Job') and key in (
'credential', 'vault_credential', 'cloud_credential', 'network_credential'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to check that the UI TEMPLATES view isn't showing any of these keys, and it seems that it does not, so good there.

) or queryset.model._meta.object_name in ('InventorySource', 'InventoryUpdate') and key == 'credential':
key = 'credentials'

# Make legacy v1 Credential fields work for backwards compatability
# TODO: remove after API v1 deprecation period
#
# convert v1 `Credential.kind` queries to `Credential.credential_type__pk`
if queryset.model._meta.object_name == 'Credential' and key == 'kind':
key = key.replace('kind', 'credential_type')

if 'ssh' in values:
# In 3.2, SSH and Vault became separate credential types, but in the v1 API,
# they're both still "kind=ssh"
# under the hood, convert `/api/v1/credentials/?kind=ssh` to
# `/api/v1/credentials/?or__credential_type=<ssh_pk>&or__credential_type=<vault_pk>`
values = set(values)
values.add('vault')
values = list(values)
q_or = True

for i, kind in enumerate(values):
if kind == 'vault':
type_ = CredentialType.objects.get(kind=kind)
else:
type_ = CredentialType.from_v1_kind(kind)
if type_ is None:
raise ParseError(_('cannot filter on kind %s') % kind)
values[i] = type_.pk

# Convert value(s) to python and add to the appropriate list.
for value in values:
if q_int:
Expand Down
30 changes: 8 additions & 22 deletions awx/api/generics.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
# AWX
from awx.api.filters import FieldLookupBackend
from awx.main.models import (
UnifiedJob, UnifiedJobTemplate, User, Role
UnifiedJob, UnifiedJobTemplate, User, Role, Credential
)
from awx.main.access import access_registry
from awx.main.utils import (
Expand All @@ -46,7 +46,7 @@
)
from awx.main.utils.db import get_all_field_names
from awx.api.serializers import ResourceAccessListElementSerializer, CopySerializer, UserSerializer
from awx.api.versioning import URLPathVersioning, get_request_version
from awx.api.versioning import URLPathVersioning
from awx.api.metadata import SublistAttachDetatchMetadata, Metadata

__all__ = ['APIView', 'GenericAPIView', 'ListAPIView', 'SimpleListAPIView',
Expand Down Expand Up @@ -288,12 +288,6 @@ def get_description(self, request, html=False):
template_list.append('api/%s.md' % template_basename)
context = self.get_description_context()

# "v2" -> 2
default_version = int(settings.REST_FRAMEWORK['DEFAULT_VERSION'].lstrip('v'))
request_version = get_request_version(self.request)
if request_version is not None and request_version < default_version:
context['deprecated'] = True

description = render_to_string(template_list, context)
if context.get('deprecated') and context.get('swagger_method') is None:
# render deprecation messages at the very top
Expand Down Expand Up @@ -842,10 +836,6 @@ class CopyAPIView(GenericAPIView):
new_in_330 = True
new_in_api_v2 = True

def v1_not_allowed(self):
return Response({'detail': 'Action only possible starting with v2 API.'},
status=status.HTTP_404_NOT_FOUND)

def _get_copy_return_serializer(self, *args, **kwargs):
if not self.copy_return_serializer_class:
return self.get_serializer(*args, **kwargs)
Expand All @@ -859,15 +849,15 @@ def _get_copy_return_serializer(self, *args, **kwargs):
def _decrypt_model_field_if_needed(obj, field_name, field_val):
if field_name in getattr(type(obj), 'REENCRYPTION_BLACKLIST_AT_COPY', []):
return field_val
if isinstance(field_val, dict):
if isinstance(obj, Credential) and field_name == 'inputs':
for secret in obj.credential_type.secret_fields:
if secret in field_val:
field_val[secret] = decrypt_field(obj, secret)
elif isinstance(field_val, dict):
for sub_field in field_val:
if isinstance(sub_field, str) \
and isinstance(field_val[sub_field], str):
try:
field_val[sub_field] = decrypt_field(obj, field_name, sub_field)
except AttributeError:
# Catching the corner case with v1 credential fields
field_val[sub_field] = decrypt_field(obj, sub_field)
field_val[sub_field] = decrypt_field(obj, field_name, sub_field)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are some 🐉 s here. Was there really a different means of encryption for credentials created from /api/v1/credentials/? If so, what's the behavior of those old credentials that came over from the migration? The current level of testing might not have captured those. Since there's no v1 / v2 distinction of the models, the users can't tell the difference between credentials created from one or the other.

Copy link
Contributor Author

@ryanpetrello ryanpetrello May 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐉🐉🐉🐉🐉 indeed, and this is definitely the type of thing where I'd like to see what falls out to integration.

So up until now, we've relied on decrypt_field using getattr(cred, 'field'). In this PR, I've removed the special __getattr__ that facilitates this - you always have to specify cred.inputs[...].

I can't recall the details now (I wrote this in ~March), but I ended up having to do this as a way to make this work now that __getattr__ is gone from Credential.

It's possible there's a better way to do this, though.

elif isinstance(field_val, str):
try:
field_val = decrypt_field(obj, field_name)
Expand Down Expand Up @@ -952,8 +942,6 @@ def copy_model_obj(old_parent, new_parent, model, obj, creater, copy_name='', cr
return ret

def get(self, request, *args, **kwargs):
if get_request_version(request) < 2:
return self.v1_not_allowed()
obj = self.get_object()
if not request.user.can_access(obj.__class__, 'read', obj):
raise PermissionDenied()
Expand All @@ -968,8 +956,6 @@ def get(self, request, *args, **kwargs):
return Response({'can_copy': can_copy})

def post(self, request, *args, **kwargs):
if get_request_version(request) < 2:
return self.v1_not_allowed()
obj = self.get_object()
create_kwargs = self._build_create_dict(obj)
create_kwargs_check = {}
Expand Down
13 changes: 0 additions & 13 deletions awx/api/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,19 +232,6 @@ def determine_metadata(self, request, view):
return metadata


# TODO: Tower 3.3 remove class and all uses in views.py when API v1 is removed
class JobTypeMetadata(Metadata):
def get_field_info(self, field):
res = super(JobTypeMetadata, self).get_field_info(field)

if field.field_name == 'job_type':
res['choices'] = [
choice for choice in res['choices']
if choice[0] != 'scan'
]
return res


class SublistAttachDetatchMetadata(Metadata):

def determine_actions(self, request, view):
Expand Down
Loading