diff --git a/awx/api/serializers.py b/awx/api/serializers.py index b748d98a4c..027192125b 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1095,6 +1095,7 @@ class OAuth2TokenSerializer(BaseSerializer): refresh_token = serializers.SerializerMethodField() token = serializers.SerializerMethodField() + ALLOWED_SCOPES = ['read', 'write'] class Meta: model = OAuth2AccessToken @@ -1142,6 +1143,24 @@ class OAuth2TokenSerializer(BaseSerializer): except ObjectDoesNotExist: return '' + def _is_valid_scope(self, value): + if not value or (not isinstance(value, six.string_types)): + return False + words = value.split() + for word in words: + if words.count(word) > 1: + return False # do not allow duplicates + if word not in self.ALLOWED_SCOPES: + return False + return True + + def validate_scope(self, value): + if not self._is_valid_scope(value): + raise serializers.ValidationError(_( + 'Must be a simple space-separated string with allowed scopes {}.' + ).format(self.ALLOWED_SCOPES)) + return value + def create(self, validated_data): validated_data['user'] = self.context['request'].user validated_data['token'] = generate_token() diff --git a/awx/main/tests/unit/api/serializers/test_token_serializer.py b/awx/main/tests/unit/api/serializers/test_token_serializer.py new file mode 100644 index 0000000000..5ead166664 --- /dev/null +++ b/awx/main/tests/unit/api/serializers/test_token_serializer.py @@ -0,0 +1,14 @@ +import pytest + +from awx.api.serializers import OAuth2TokenSerializer + + +@pytest.mark.parametrize('scope, expect', [ + ('', False), + ('read', True), + ('read read', False), + ('write read', True), + ('read rainbow', False) +]) +def test_invalid_scopes(scope, expect): + assert OAuth2TokenSerializer()._is_valid_scope(scope) is expect