예제 #1
0
    def test_remove_users(self):
        """Remove users associated with a customer."""
        # Create Customer
        customer = None
        serializer = CustomerSerializer(data=self.customer_data[0])
        if serializer.is_valid(raise_exception=True):
            customer = serializer.save()
        customer_uuid = customer.uuid

        # Add another user
        group = Group.objects.get(name=customer.name)

        new_user_dict = self.gen_user_data()
        user_serializer = UserSerializer(data=new_user_dict)
        new_user = None
        if user_serializer.is_valid(raise_exception=True):
            new_user = user_serializer.save()
        group.user_set.add(new_user)

        manager = CustomerManager(customer_uuid)

        # Attempt to remove as customer owner
        with self.assertRaises(CustomerManagerPermissionError):
            manager.remove_users(customer.owner)

        # Attempt to remove as regular user
        with self.assertRaises(CustomerManagerPermissionError):
            manager.remove_users(new_user)

        # Attempt to remove as super user
        superuser = User.objects.filter(is_superuser=True).first()
        manager.remove_users(superuser)

        self.assertFalse(manager.get_users_for_customer())
예제 #2
0
    def test_get_providers_queryset_for_customer(self):
        """Verify all providers returned by a customer."""
        # Create Customer
        customer = None
        serializer = CustomerSerializer(data=self.customer_data[0])
        if serializer.is_valid(raise_exception=True):
            customer = serializer.save()

        # Verify no providers are returned
        self.assertFalse(ProviderManager.get_providers_queryset_for_customer(customer).exists())

        # Create Providers
        provider_1 = Provider.objects.create(name='provider1', created_by=customer.owner, customer=customer)
        provider_2 = Provider.objects.create(name='provider2', created_by=customer.owner, customer=customer)

        providers = ProviderManager.get_providers_queryset_for_customer(customer)
        # Verify providers are returned
        provider_1_found = False
        provider_2_found = False

        for provider in providers:
            if provider.uuid == provider_1.uuid:
                provider_1_found = True
            elif provider.uuid == provider_2.uuid:
                provider_2_found = True

        self.assertTrue(provider_1_found)
        self.assertTrue(provider_2_found)
        self.assertEqual((len(providers)), 2)
예제 #3
0
    def test_is_removable_by_user(self):
        """Can current user remove provider."""
        # Create Customer
        customer = None
        serializer = CustomerSerializer(data=self.customer_data[0])
        if serializer.is_valid(raise_exception=True):
            customer = serializer.save()

        # Create Provider
        provider = Provider.objects.create(name='providername', created_by=customer.owner, customer=customer)
        provider_uuid = provider.uuid

        # Create another user for negative tests
        # Add another user
        group = Group.objects.get(name=customer.name)

        new_user_dict = self.gen_user_data()
        user_serializer = UserSerializer(data=new_user_dict)
        new_user = None
        if user_serializer.is_valid(raise_exception=True):
            new_user = user_serializer.save()
        group.user_set.add(new_user)

        manager = ProviderManager(provider_uuid)

        self.assertTrue(manager.is_removable_by_user(customer.owner))
        self.assertFalse(manager.is_removable_by_user(new_user))

        superuser = User.objects.filter(is_superuser=True).first()
        self.assertFalse(manager.is_removable_by_user(superuser))
예제 #4
0
    def test_remove(self):
        """Remove provider."""
        # Create Customer
        customer = None
        serializer = CustomerSerializer(data=self.customer_data[0])
        if serializer.is_valid(raise_exception=True):
            customer = serializer.save()

        # Create Provider
        provider = Provider.objects.create(name='providername', created_by=customer.owner, customer=customer)
        provider_uuid = provider.uuid

        # Create another user for negative tests
        # Add another user
        group = Group.objects.get(name=customer.name)

        new_user_dict = self.gen_user_data()
        user_serializer = UserSerializer(data=new_user_dict)
        other_user = None
        if user_serializer.is_valid(raise_exception=True):
            other_user = user_serializer.save()
        group.user_set.add(other_user)

        manager = ProviderManager(provider_uuid)

        with self.assertRaises(ProviderManagerError):
            self.assertFalse(manager.remove(other_user))

        manager.remove(customer.owner)
        provider_query = Provider.objects.all().filter(uuid=provider_uuid)
        self.assertFalse(provider_query)
예제 #5
0
    def test_get_tenant(self):
        """Get the tenant object for a customer."""
        # Create customer.
        customer_name = 'test_customer_tenant'
        customer_json = {'name': customer_name,
                         'owner': self.gen_user_data()}

        customer = None
        serializer = CustomerSerializer(data=customer_json)
        if serializer.is_valid(raise_exception=True):
            customer = serializer.save()
        customer_uuid = customer.uuid

        # Create tenant
        customer_obj = Customer.objects.filter(name=customer_name).get()
        tenant = Tenant(schema_name=customer_obj.schema_name)
        tenant.save()

        # Get manager
        manager = CustomerManager(customer_uuid)
        customer_obj = manager.get_model()
        customer_schema_name = customer_obj.schema_name

        # Verify tenant is returned from manager
        tenant_obj = Tenant.objects.filter(schema_name=customer_schema_name).get()

        self.assertEqual(manager.get_tenant(), tenant_obj)
예제 #6
0
    def test_create_provider_with_exception(self):
        """Test creating a provider with a provider exception."""
        iam_arn = 'arn:aws:s3:::my_s3_bucket'
        bucket_name = 'my_s3_bucket'
        provider = {
            'name': 'test_provider',
            'type': Provider.PROVIDER_AWS,
            'authentication': {
                'provider_resource_name': iam_arn
            },
            'billing_source': {
                'bucket': bucket_name
            }
        }
        new_cust = None
        serializer = CustomerSerializer(data=self.customer_data[0])
        if serializer.is_valid(raise_exception=True):
            new_cust = serializer.save()
        request = Mock()
        request.user = new_cust.owner
        context = {'request': request}

        with patch.object(ProviderAccessor,
                          'cost_usage_source_ready',
                          side_effect=serializers.ValidationError):
            ProviderSerializer(data=provider, context=context)
예제 #7
0
    def test_removing_tenant(self):
        """Remove the tenant object for a customer."""
        # Create customer.
        customer_name = 'test_customer_tenant'
        customer_json = {'name': customer_name,
                         'owner': self.gen_user_data()}

        customer = None
        serializer = CustomerSerializer(data=customer_json)
        if serializer.is_valid(raise_exception=True):
            customer = serializer.save()
        customer_uuid = customer.uuid

        # Create tenant
        customer_obj = Customer.objects.filter(name=customer_name).get()
        tenant = Tenant(schema_name=customer_obj.schema_name)
        tenant.save()

        # Get manager
        manager = CustomerManager(customer_uuid)
        customer_obj = manager.get_model()
        customer_schema_name = customer_obj.schema_name

        # Verify tenant is returned from manager
        tenant_obj = Tenant.objects.filter(schema_name=customer_schema_name).get()

        self.assertEqual(manager.get_tenant(), tenant_obj)

        # Attempt to remove as customer owner
        with self.assertRaises(CustomerManagerPermissionError):
            manager.remove_tenant(customer_obj.owner)

        # Attempt to remove tenant as super user
        superuser = User.objects.filter(is_superuser=True).first()
        manager.remove_tenant(superuser)
예제 #8
0
    def test_get_users_for_customer(self):
        """Can current user remove provider."""
        # Create Customer
        customer = None
        serializer = CustomerSerializer(data=self.customer_data[0])
        if serializer.is_valid(raise_exception=True):
            customer = serializer.save()
        customer_uuid = customer.uuid

        # Add another user
        group = Group.objects.get(name=customer.name)

        new_user_dict = self.gen_user_data()
        user_serializer = UserSerializer(data=new_user_dict)
        new_user = None
        if user_serializer.is_valid(raise_exception=True):
            new_user = user_serializer.save()
        group.user_set.add(new_user)

        manager = CustomerManager(customer_uuid)
        users = manager.get_users_for_customer()

        self.assertEqual(len(users), 2)

        new_user_found = False
        owner_found = False
        for found_user in users:
            if found_user.username == new_user_dict['username']:
                new_user_found = True
            elif found_user.username == customer.owner.username:
                owner_found = True

        self.assertTrue(new_user_found)
        self.assertTrue(owner_found)
예제 #9
0
    def test_uuid_field(self):
        """Test that a uuid is generated."""
        # create the customer
        instance = None
        serializer = CustomerSerializer(data=self.customer_data[0])
        if serializer.is_valid(raise_exception=True):
            instance = serializer.save()

        self.assertIsInstance(instance.uuid, uuid.UUID)
예제 #10
0
    def test_get_name(self):
        """Can the customer name be returned."""
        # Create Customer
        customer = None
        serializer = CustomerSerializer(data=self.customer_data[0])
        if serializer.is_valid(raise_exception=True):
            customer = serializer.save()
        customer_uuid = customer.uuid

        manager = CustomerManager(customer_uuid)
        self.assertEqual(manager.get_name(), self.customer_data[0]['name'])
예제 #11
0
    def test_create_customer(self):
        """Test creating a customer."""
        # create the customers
        for customer in self.customer_data:
            instance = None
            serializer = CustomerSerializer(data=customer)
            if serializer.is_valid(raise_exception=True):
                instance = serializer.save()

            self.assertEqual(customer['name'], instance.name)
            self.assertTrue(
                ResetToken.objects.filter(user=instance.owner).exists)
예제 #12
0
 def test_provider_org_fail(self, check_org_access):
     """Test creating a provider with AWS org access failure."""
     check_org_access.return_value = False
     iam_arn = 'arn:aws:s3:::my_s3_bucket'
     bucket_name = 'my_s3_bucket'
     access_key_id, secret_access_key, session_token = _get_sts_access(
         iam_arn)
     s3_resource = boto3.resource(
         's3',
         aws_access_key_id=access_key_id,
         aws_secret_access_key=secret_access_key,
         aws_session_token=session_token,
     )
     s3_resource.create_bucket(Bucket=bucket_name)
     provider = {'name': 'test_provider',
                 'type': Provider.PROVIDER_AWS,
                 'authentication': {
                     'provider_resource_name': iam_arn
                 },
                 'billing_source': {
                     'bucket': bucket_name
                 }}
     new_cust = None
     serializer = CustomerSerializer(data=self.customer_data[0])
     if serializer.is_valid(raise_exception=True):
         new_cust = serializer.save()
     request = Mock()
     request.user = new_cust.owner
     context = {'request': request}
     serializer = ProviderSerializer(data=provider, context=context)
     if serializer.is_valid(raise_exception=True):
         serializer.save()
         check_org_access.assert_called_once()
예제 #13
0
 def test_provider_s3_fail(self, check_s3_access):
     """Test creating a provider with AWS s3 bucket doesn't exist."""
     check_s3_access.return_value = False
     iam_arn = 'arn:aws:s3:::my_s3_bucket'
     bucket_name = 'my_s3_bucket'
     provider = {'name': 'test_provider',
                 'type': Provider.PROVIDER_AWS,
                 'authentication': {
                     'provider_resource_name': iam_arn
                 },
                 'billing_source': {
                     'bucket': bucket_name
                 }}
     new_cust = None
     serializer = CustomerSerializer(data=self.customer_data[0])
     if serializer.is_valid(raise_exception=True):
         new_cust = serializer.save()
     request = Mock()
     request.user = new_cust.owner
     context = {'request': request}
     serializer = ProviderSerializer(data=provider, context=context)
     if serializer.is_valid(raise_exception=True):
         with self.assertRaises(serializers.ValidationError):
             serializer.save()
             check_s3_access.assert_called_once(iam_arn)
예제 #14
0
    def test_create_customer(self):
        """Test creating a customer."""
        # create the customers
        customer = self._create_customer_data()
        instance = None
        serializer = CustomerSerializer(data=customer)
        if serializer.is_valid(raise_exception=True):
            instance = serializer.save()

        schema_name = serializer.data.get('schema_name')

        self.assertIsNone(schema_name)
        self.assertFalse('schema_name' in serializer.data)
        self.assertEqual(customer['account_id'], instance.account_id)
        self.assertIsInstance(instance.uuid, uuid.UUID)
예제 #15
0
class ProviderSerializer(serializers.ModelSerializer):
    """Serializer for the Provider  model."""

    uuid = serializers.UUIDField(read_only=True)
    name = serializers.CharField(max_length=256, required=True,
                                 allow_null=False, allow_blank=False)
    type = serializers.ChoiceField(choices=Provider.PROVIDER_CHOICES)
    authentication = ProviderAuthenticationSerializer()
    billing_source = ProviderBillingSourceSerializer()
    customer = CustomerSerializer(read_only=True)
    created_by = UserSerializer(read_only=True)

    class Meta:
        """Metadata for the serializer."""

        model = Provider
        fields = ('uuid', 'name', 'type', 'authentication', 'billing_source',
                  'customer', 'created_by')

    @transaction.atomic
    def create(self, validated_data):
        """Create a provider from validated data."""
        authentication = validated_data.pop('authentication')
        billing_source = validated_data.pop('billing_source')

        user = None
        customer = None
        request = self.context.get('request')
        if request and hasattr(request, 'user'):
            user = User.objects.get(pk=request.user.id)
            if user.groups.count() == 1:
                group = user.groups.first()
                customer = Customer.objects.get(pk=group.id)
            else:
                key = 'customer'
                message = 'Group for requesting user could not be found.'
                raise serializers.ValidationError(error_obj(key, message))
        else:
            key = 'created_by'
            message = 'Requesting user could not be found.'
            raise serializers.ValidationError(error_obj(key, message))

        provider_resource_name = authentication.get('provider_resource_name')
        bucket = billing_source.get('bucket')

        provider_type = validated_data['type']
        interface = ProviderAccessor(provider_type)
        interface.cost_usage_source_ready(provider_resource_name, bucket)

        auth = ProviderAuthentication.objects.create(**authentication)
        bill = ProviderBillingSource.objects.create(**billing_source)
        auth.save()
        bill.save()
        provider = Provider.objects.create(**validated_data)
        provider.customer = customer
        provider.created_by = user
        provider.authentication = auth
        provider.billing_source = bill
        provider.save()
        return provider
예제 #16
0
    def test_get_tenant_not_found(self):
        """Try to get a missing tenant object for a customer."""
        # Create customer.
        customer_name = 'test_customer_tenant'
        customer_json = {'name': customer_name, 'owner': self.gen_user_data()}

        customer = None
        serializer = CustomerSerializer(data=customer_json)
        if serializer.is_valid(raise_exception=True):
            customer = serializer.save()
        customer_uuid = customer.uuid

        # Get manager
        manager = CustomerManager(customer_uuid)

        self.assertEqual(manager.get_tenant(), None)
예제 #17
0
    def test_create_provider(self):
        """Test creating a provider."""
        iam_arn = 'arn:aws:s3:::my_s3_bucket'
        bucket_name = 'my_s3_bucket'
        provider = {'name': 'test_provider',
                    'type': Provider.PROVIDER_AWS,
                    'authentication': {
                        'provider_resource_name': iam_arn
                    },
                    'billing_source': {
                        'bucket': bucket_name
                    }}
        new_cust = None
        serializer = CustomerSerializer(data=self.customer_data[0])
        if serializer.is_valid(raise_exception=True):
            new_cust = serializer.save()
        request = Mock()
        request.user = new_cust.owner
        context = {'request': request}
        instance = None

        with patch.object(ProviderAccessor, 'cost_usage_source_ready', returns=True):
            serializer = ProviderSerializer(data=provider, context=context)
            if serializer.is_valid(raise_exception=True):
                instance = serializer.save()

        schema_name = serializer.data['customer'].get('schema_name')
        self.assertIsInstance(instance.uuid, uuid.UUID)
        self.assertIsNone(schema_name)
        self.assertFalse('schema_name' in serializer.data['customer'])
예제 #18
0
    def test_get_name(self):
        """Can the provider name be returned."""
        # Create Customer
        customer = None
        serializer = CustomerSerializer(data=self.customer_data[0])
        if serializer.is_valid(raise_exception=True):
            customer = serializer.save()

        # Create Provider
        provider_name = 'sample_provider'
        provider = Provider.objects.create(name=provider_name, created_by=customer.owner, customer=customer)

        # Get Provider UUID
        provider_uuid = provider.uuid

        # Get Provider Manager
        manager = ProviderManager(provider_uuid)
        self.assertEqual(manager.get_name(), provider_name)
예제 #19
0
    def test_remove(self):
        """Remove provider."""
        # Create Customer
        customer = None
        serializer = CustomerSerializer(data=self.customer_data[0])
        if serializer.is_valid(raise_exception=True):
            customer = serializer.save()

        # Create Provider
        provider_authentication = ProviderAuthentication.objects.create(provider_resource_name='arn:aws:iam::2:role/mg')
        provider_billing = ProviderBillingSource.objects.create(bucket='my_s3_bucket')
        provider = Provider.objects.create(name='providername',
                                           created_by=customer.owner,
                                           customer=customer,
                                           authentication=provider_authentication,
                                           billing_source=provider_billing)
        provider_uuid = provider.uuid

        # Create another user for negative tests
        # Add another user
        group = Group.objects.get(name=customer.name)

        new_user_dict = self.gen_user_data()
        user_serializer = UserSerializer(data=new_user_dict)
        other_user = None
        if user_serializer.is_valid(raise_exception=True):
            other_user = user_serializer.save()
        group.user_set.add(other_user)

        manager = ProviderManager(provider_uuid)

        with self.assertRaises(ProviderManagerError):
            self.assertFalse(manager.remove(other_user))

        manager.remove(customer.owner)
        provider_query = Provider.objects.all().filter(uuid=provider_uuid)
        self.assertFalse(provider_query)
예제 #20
0
class ProviderSerializer(serializers.ModelSerializer):
    """Serializer for the Provider model."""

    uuid = serializers.UUIDField(read_only=True)
    name = serializers.CharField(max_length=256, required=True,
                                 allow_null=False, allow_blank=False)
    type = serializers.ChoiceField(choices=Provider.PROVIDER_CHOICES)
    created_timestamp = serializers.DateTimeField(read_only=True)
    customer = CustomerSerializer(read_only=True)
    created_by = UserSerializer(read_only=True)

    # pylint: disable=too-few-public-methods
    class Meta:
        """Metadata for the serializer."""

        model = Provider
        fields = ('uuid', 'name', 'type', 'authentication', 'billing_source',
                  'customer', 'created_by', 'created_timestamp')

    def __init__(self, instance=None, data=empty, **kwargs):
        """Initialize the Provider Serializer.

        Here we ensure we use the appropriate serializer to validate the
        authentication and billing_source parameters.
        """
        super().__init__(instance, data, **kwargs)

        provider_type = None
        if data and data != empty:
            provider_type = data.get('type')

        if provider_type and provider_type.lower() not in PROVIDER_CHOICE_LIST:
            key = 'type'
            message = f'{provider_type} is not a valid source type.'
            raise serializers.ValidationError(error_obj(key, message))

        if provider_type:
            self.fields['authentication'] = AUTHENTICATION_SERIALIZERS.get(provider_type)()
            self.fields['billing_source'] = BILLING_SOURCE_SERIALIZERS.get(provider_type)(
                default={'bucket': '', 'data_source': {'bucket': ''}}
            )
        else:
            self.fields['authentication'] = ProviderAuthenticationSerializer()
            self.fields['billing_source'] = ProviderBillingSourceSerializer()

    @transaction.atomic
    def create(self, validated_data):
        """Create a provider from validated data."""
        user = None
        customer = None
        request = self.context.get('request')
        if request and hasattr(request, 'user'):
            user = request.user
            if user.customer:
                customer = user.customer
            else:
                key = 'customer'
                message = 'Customer for requesting user could not be found.'
                raise serializers.ValidationError(error_obj(key, message))
        else:
            key = 'created_by'
            message = 'Requesting user could not be found.'
            raise serializers.ValidationError(error_obj(key, message))

        if 'billing_source' in validated_data:
            billing_source = validated_data.pop('billing_source')
            data_source = billing_source.get('data_source', {})
            bucket = data_source.get('bucket')
        else:
            # Because of a unique together constraint, this is done
            # to allow for this field to be non-required for OCP
            # but will still have a blank no-op entry in the DB
            billing_source = {'bucket': '', 'data_source': {}}
            data_source = None

        authentication = validated_data.pop('authentication')
        credentials = authentication.get('credentials')
        provider_resource_name = credentials.get('provider_resource_name')
        provider_type = validated_data['type']
        interface = ProviderAccessor(provider_type)

        if credentials and data_source and provider_type not in ['AWS', 'OCP']:
            interface.cost_usage_source_ready(credentials, data_source)
        else:
            interface.cost_usage_source_ready(provider_resource_name, bucket)

        bill, __ = ProviderBillingSource.objects.get_or_create(**billing_source)
        auth, __ = ProviderAuthentication.objects.get_or_create(**authentication)

        # We can re-use a billing source or a auth, but not the same combination.
        unique_count = Provider.objects.filter(authentication=auth)\
            .filter(billing_source=bill).count()
        if unique_count != 0:
            existing_provider = Provider.objects.filter(authentication=auth)\
                .filter(billing_source=bill).first()
            if existing_provider.type in ('AWS', 'OCP'):
                sources_auth = {'resource_name': provider_resource_name}
            elif existing_provider.type in ('AZURE', ):
                sources_auth = {'credentials': auth.credentials}
            else:
                sources_auth = {}
            source_query = Sources.objects.filter(authentication=sources_auth)
            if source_query.exists():
                source_obj = source_query.first()
                source_obj.koku_uuid = existing_provider.uuid
                source_obj.save()
                return existing_provider
            error = {'Error': 'A Provider already exists with that Authentication and Billing Source'}
            raise serializers.ValidationError(error)

        provider = Provider.objects.create(**validated_data)
        provider.customer = customer
        provider.created_by = user
        provider.authentication = auth
        provider.billing_source = bill
        provider.save()
        return provider

    @transaction.atomic
    def update(self, instance, validated_data):
        """Update a Provider instance from validated data."""
        provider_type = validated_data['type']
        interface = ProviderAccessor(provider_type)

        authentication = validated_data.pop('authentication')
        credentials = authentication.get('credentials')
        provider_resource_name = credentials.get('provider_resource_name')

        billing_source = validated_data.pop('billing_source')
        data_source = billing_source.get('data_source')
        bucket = billing_source.get('bucket')

        if credentials and data_source and provider_type not in ['AWS', 'OCP']:
            interface.cost_usage_source_ready(credentials, data_source)
        else:
            interface.cost_usage_source_ready(provider_resource_name, bucket)

        bill, __ = ProviderBillingSource.objects.get_or_create(**billing_source)
        auth, __ = ProviderAuthentication.objects.get_or_create(**authentication)

        for key in validated_data.keys():
            setattr(instance, key, validated_data[key])

        instance.authentication = auth
        instance.billing_source = bill
        try:
            instance.save()
        except IntegrityError:
            error = {'Error': 'A Provider already exists with that Authentication and Billing Source'}
            raise serializers.ValidationError(error)
        return instance
예제 #21
0
class ProviderSerializer(serializers.ModelSerializer):
    """Serializer for the Provider model."""

    uuid = serializers.UUIDField(allow_null=True, required=False)
    name = serializers.CharField(max_length=256,
                                 required=True,
                                 allow_null=False,
                                 allow_blank=False)
    type = serializers.ChoiceField(choices=LCASE_PROVIDER_CHOICE_LIST)
    created_timestamp = serializers.DateTimeField(read_only=True)
    customer = CustomerSerializer(read_only=True)
    created_by = UserSerializer(read_only=True)
    active = serializers.BooleanField(read_only=True)
    paused = serializers.BooleanField(required=False)

    class Meta:
        """Metadata for the serializer."""

        model = Provider
        fields = (
            "uuid",
            "name",
            "type",
            "authentication",
            "billing_source",
            "customer",
            "created_by",
            "created_timestamp",
            "active",
            "paused",
        )

    def __init__(self, instance=None, data=empty, **kwargs):
        """Initialize the Provider Serializer.

        Here we ensure we use the appropriate serializer to validate the
        authentication and billing_source parameters.
        """
        super().__init__(instance, data, **kwargs)

        provider_type = None
        if data and data != empty:
            provider_type = data.get("type")

        if provider_type and provider_type.lower(
        ) not in LCASE_PROVIDER_CHOICE_LIST:
            key = "type"
            message = f"{provider_type} is not a valid source type."
            raise serializers.ValidationError(error_obj(key, message))

        if provider_type:
            provider_type = provider_type.lower()
            self.fields["authentication"] = AUTHENTICATION_SERIALIZERS.get(
                Provider.PROVIDER_CASE_MAPPING.get(provider_type))()
            self.fields["billing_source"] = BILLING_SOURCE_SERIALIZERS.get(
                Provider.PROVIDER_CASE_MAPPING.get(provider_type))()
        else:
            self.fields["authentication"] = ProviderAuthenticationSerializer()
            self.fields["billing_source"] = ProviderBillingSourceSerializer()

    @property
    def demo_credentials(self):
        """Build formatted credentials for our nise-populator demo accounts."""
        creds_by_source_type = defaultdict(list)
        for account, cred_dict in settings.DEMO_ACCOUNTS.items():
            for cred, info in cred_dict.items():
                if info.get("source_type") == Provider.PROVIDER_AWS:
                    creds_by_source_type[Provider.PROVIDER_AWS].append(
                        {"role_arn": cred})
                elif info.get("source_type") == Provider.PROVIDER_AZURE:
                    creds_by_source_type[Provider.PROVIDER_AZURE].append(
                        {"client_id": cred})
                elif info.get("source_type") == Provider.PROVIDER_GCP:
                    creds_by_source_type[Provider.PROVIDER_GCP].append(
                        {"project_id": cred})
        return creds_by_source_type

    def get_request_info(self):
        """Obtain request information like user and customer context."""
        user = self.context.get("user")
        customer = self.context.get("customer")
        if user and customer:
            return user, customer

        request = self.context.get("request")
        if request and hasattr(request, "user"):
            user = request.user
            if user.customer:
                customer = user.customer
            else:
                key = "customer"
                message = "Customer for requesting user could not be found."
                raise serializers.ValidationError(error_obj(key, message))
        else:
            key = "created_by"
            message = "Requesting user could not be found."
            raise serializers.ValidationError(error_obj(key, message))
        return user, customer

    @transaction.atomic
    def create(self, validated_data):
        """Create a provider from validated data."""
        user, customer = self.get_request_info()
        provider_type = validated_data["type"].lower()
        provider_type = Provider.PROVIDER_CASE_MAPPING.get(provider_type)
        validated_data["type"] = provider_type
        interface = ProviderAccessor(provider_type)
        authentication = validated_data.pop("authentication")
        credentials = authentication.get("credentials")
        billing_source = validated_data.pop("billing_source")
        data_source = billing_source.get("data_source")

        if self._is_demo_account(provider_type, credentials):
            LOG.info(
                "Customer account is a DEMO account. Skipping cost_usage_source_ready check."
            )
        else:
            interface.cost_usage_source_ready(credentials, data_source)

        bill, __ = ProviderBillingSource.objects.get_or_create(
            **billing_source)
        auth, __ = ProviderAuthentication.objects.get_or_create(
            **authentication)

        # We can re-use a billing source or a auth, but not the same combination.
        dup_queryset = (Provider.objects.filter(authentication=auth).filter(
            billing_source=bill).filter(customer=customer))
        if dup_queryset.count() != 0:
            conflict_provider = dup_queryset.first()
            message = (
                f"Cost management does not allow duplicate accounts. "
                f"{conflict_provider.name} already exists. Edit source settings to configure a new source."
            )
            LOG.warn(message)
            raise serializers.ValidationError(
                error_obj(ProviderErrors.DUPLICATE_AUTH, message))

        provider = Provider.objects.create(**validated_data)
        provider.customer = customer
        provider.created_by = user
        provider.authentication = auth
        provider.billing_source = bill
        provider.active = True

        provider.save()

        customer.date_updated = DateHelper().now_utc
        customer.save()

        return provider

    def update(self, instance, validated_data):
        """Update a Provider instance from validated data."""
        _, customer = self.get_request_info()
        provider_type = validated_data["type"].lower()
        provider_type = Provider.PROVIDER_CASE_MAPPING.get(provider_type)
        validated_data["type"] = provider_type
        interface = ProviderAccessor(provider_type)
        authentication = validated_data.pop("authentication")
        credentials = authentication.get("credentials")
        billing_source = validated_data.pop("billing_source")
        data_source = billing_source.get("data_source")

        # updating `paused` must happen regardless of Provider availabilty
        instance.paused = validated_data.pop("paused", instance.paused)

        try:
            if self._is_demo_account(provider_type, credentials):
                LOG.info(
                    "Customer account is a DEMO account. Skipping cost_usage_source_ready check."
                )
            else:
                interface.cost_usage_source_ready(credentials, data_source)
        except serializers.ValidationError as validation_error:
            instance.active = False
            instance.save()
            raise validation_error

        with transaction.atomic():
            bill, __ = ProviderBillingSource.objects.get_or_create(
                **billing_source)
            auth, __ = ProviderAuthentication.objects.get_or_create(
                **authentication)
            if instance.billing_source != bill or instance.authentication != auth:
                dup_queryset = (Provider.objects.filter(
                    authentication=auth).filter(billing_source=bill).filter(
                        customer=customer))
                if dup_queryset.count() != 0:
                    conflict_provder = dup_queryset.first()
                    message = (
                        f"Cost management does not allow duplicate accounts. "
                        f"{conflict_provder.name} already exists. Edit source settings to configure a new source."
                    )
                    LOG.warn(message)
                    raise serializers.ValidationError(
                        error_obj(ProviderErrors.DUPLICATE_AUTH, message))

            for key in validated_data.keys():
                setattr(instance, key, validated_data[key])

            instance.authentication = auth
            instance.billing_source = bill
            instance.active = True

            instance.save()

            customer.date_updated = DateHelper().now_utc
            customer.save()

            return instance

    def _is_demo_account(self, provider_type, credentials):
        """Test whether this source is a demo account."""
        key_types = {
            Provider.PROVIDER_AWS: "role_arn",
            Provider.PROVIDER_AZURE: "client_id",
            Provider.PROVIDER_GCP: "project_id",
        }

        key_to_check = key_types.get(provider_type, "")
        creds_to_check = self.demo_credentials.get(provider_type, [])
        for cred in creds_to_check:
            if credentials.get(key_to_check,
                               True) == cred.get(key_to_check, False):
                return True
        return False
예제 #22
0
class ProviderSerializer(serializers.ModelSerializer):
    """Serializer for the Provider model."""

    uuid = serializers.UUIDField(allow_null=True, required=False)
    name = serializers.CharField(max_length=256,
                                 required=True,
                                 allow_null=False,
                                 allow_blank=False)
    type = serializers.ChoiceField(choices=LCASE_PROVIDER_CHOICE_LIST)
    created_timestamp = serializers.DateTimeField(read_only=True)
    customer = CustomerSerializer(read_only=True)
    created_by = UserSerializer(read_only=True)
    active = serializers.BooleanField(read_only=True)

    # pylint: disable=too-few-public-methods
    class Meta:
        """Metadata for the serializer."""

        model = Provider
        fields = (
            "uuid",
            "name",
            "type",
            "authentication",
            "billing_source",
            "customer",
            "created_by",
            "created_timestamp",
            "active",
        )

    def __init__(self, instance=None, data=empty, **kwargs):
        """Initialize the Provider Serializer.

        Here we ensure we use the appropriate serializer to validate the
        authentication and billing_source parameters.
        """
        super().__init__(instance, data, **kwargs)

        provider_type = None
        if data and data != empty:
            provider_type = data.get("type")

        if provider_type and provider_type.lower(
        ) not in LCASE_PROVIDER_CHOICE_LIST:
            key = "type"
            message = f"{provider_type} is not a valid source type."
            raise serializers.ValidationError(error_obj(key, message))

        if provider_type:
            provider_type = provider_type.lower()
            self.fields["authentication"] = AUTHENTICATION_SERIALIZERS.get(
                Provider.PROVIDER_CASE_MAPPING.get(provider_type))()
            self.fields["billing_source"] = BILLING_SOURCE_SERIALIZERS.get(
                Provider.PROVIDER_CASE_MAPPING.get(provider_type))(default={
                    "bucket": "",
                    "data_source": {
                        "bucket": ""
                    }
                })
        else:
            self.fields["authentication"] = ProviderAuthenticationSerializer()
            self.fields["billing_source"] = ProviderBillingSourceSerializer()

    def get_request_info(self):
        """Obtain request information like user and customer context."""
        user = None
        customer = None
        request = self.context.get("request")
        if request and hasattr(request, "user"):
            user = request.user
            if user.customer:
                customer = user.customer
            else:
                key = "customer"
                message = "Customer for requesting user could not be found."
                raise serializers.ValidationError(error_obj(key, message))
        else:
            key = "created_by"
            message = "Requesting user could not be found."
            raise serializers.ValidationError(error_obj(key, message))
        return request, user, customer

    @transaction.atomic
    def create(self, validated_data):
        """Create a provider from validated data."""
        _, user, customer = self.get_request_info()

        if "billing_source" in validated_data:
            billing_source = validated_data.pop("billing_source")
            data_source = billing_source.get("data_source", {})
            bucket = data_source.get("bucket")
        else:
            # Because of a unique together constraint, this is done
            # to allow for this field to be non-required for OCP
            # but will still have a blank no-op entry in the DB
            billing_source = {"bucket": "", "data_source": {}}
            data_source = None

        authentication = validated_data.pop("authentication")
        credentials = authentication.get("credentials")
        provider_resource_name = credentials.get("provider_resource_name")
        provider_type = validated_data["type"]
        provider_type = Provider.PROVIDER_CASE_MAPPING.get(provider_type)
        validated_data["type"] = provider_type
        interface = ProviderAccessor(provider_type)

        if customer.account_id not in settings.DEMO_ACCOUNTS:
            if credentials and data_source and provider_type not in [
                    Provider.PROVIDER_AWS, Provider.PROVIDER_OCP
            ]:
                interface.cost_usage_source_ready(credentials, data_source)
            else:
                interface.cost_usage_source_ready(provider_resource_name,
                                                  bucket)

        bill, __ = ProviderBillingSource.objects.get_or_create(
            **billing_source)
        auth, __ = ProviderAuthentication.objects.get_or_create(
            **authentication)

        # We can re-use a billing source or a auth, but not the same combination.
        unique_count = (Provider.objects.filter(authentication=auth).filter(
            billing_source=bill).filter(customer=customer).count())
        if unique_count != 0:
            error = {
                "Error":
                "A Provider already exists with that Authentication and Billing Source"
            }
            raise serializers.ValidationError(error)

        provider = Provider.objects.create(**validated_data)
        provider.customer = customer
        provider.created_by = user
        provider.authentication = auth
        provider.billing_source = bill
        provider.active = True

        provider.save()
        return provider

    def update(self, instance, validated_data):
        """Update a Provider instance from validated data."""
        _, _, customer = self.get_request_info()
        provider_type = validated_data["type"].lower()
        provider_type = Provider.PROVIDER_CASE_MAPPING.get(provider_type)
        validated_data["type"] = provider_type
        if instance.type != provider_type:
            error = {
                "Error":
                "The Provider Type cannot be changed with a PUT request."
            }
            raise serializers.ValidationError(error)
        interface = ProviderAccessor(provider_type)
        authentication = validated_data.pop("authentication")
        credentials = authentication.get("credentials")
        provider_resource_name = credentials.get("provider_resource_name")
        billing_source = validated_data.pop("billing_source")
        data_source = billing_source.get("data_source")
        bucket = billing_source.get("bucket")

        try:
            if customer.account_id not in settings.DEMO_ACCOUNTS:
                if credentials and data_source and provider_type not in [
                        Provider.PROVIDER_AWS, Provider.PROVIDER_OCP
                ]:
                    interface.cost_usage_source_ready(credentials, data_source)
                else:
                    interface.cost_usage_source_ready(provider_resource_name,
                                                      bucket)
        except serializers.ValidationError as validation_error:
            instance.active = False
            instance.save()
            raise validation_error

        with transaction.atomic():
            bill, __ = ProviderBillingSource.objects.get_or_create(
                **billing_source)
            auth, __ = ProviderAuthentication.objects.get_or_create(
                **authentication)

            for key in validated_data.keys():
                setattr(instance, key, validated_data[key])

            instance.authentication = auth
            instance.billing_source = bill
            instance.active = True

            try:
                instance.save()
            except IntegrityError:
                error = {
                    "Error":
                    "A Provider already exists with that Authentication and Billing Source"
                }
                raise serializers.ValidationError(error)
            return instance
예제 #23
0
 def get_users_for_customer(self):
     """List of users that belong to the customer."""
     group = CustomerSerializer.get_authentication_group_for_customer(
         self.model)
     users = CustomerSerializer.get_users_for_group(group)
     return users
예제 #24
0
class ProviderSerializer(serializers.ModelSerializer):
    """Serializer for the Provider  model."""

    uuid = serializers.UUIDField(read_only=True)
    name = serializers.CharField(max_length=256,
                                 required=True,
                                 allow_null=False,
                                 allow_blank=False)
    type = serializers.ChoiceField(choices=Provider.PROVIDER_CHOICES)
    authentication = ProviderAuthenticationSerializer()
    billing_source = ProviderBillingSourceSerializer(default={'bucket': ''})
    customer = CustomerSerializer(read_only=True)
    created_by = UserSerializer(read_only=True)

    class Meta:
        """Metadata for the serializer."""

        model = Provider
        fields = ('uuid', 'name', 'type', 'authentication', 'billing_source',
                  'customer', 'created_by')

    @transaction.atomic
    def create(self, validated_data):
        """Create a provider from validated data."""
        user = None
        customer = None
        request = self.context.get('request')
        if request and hasattr(request, 'user'):
            user = request.user
            if user.customer:
                customer = user.customer
            else:
                key = 'customer'
                message = 'Customer for requesting user could not be found.'
                raise serializers.ValidationError(error_obj(key, message))
        else:
            key = 'created_by'
            message = 'Requesting user could not be found.'
            raise serializers.ValidationError(error_obj(key, message))
        if 'billing_source' in validated_data:
            billing_source = validated_data.pop('billing_source')
            bucket = billing_source.get('bucket')
        else:
            # Because of a unique together constraint, this is done
            # to allow for this field to be non-required for OCP
            # but will still have a blank no-op entry in the DB
            billing_source = {'bucket': ''}
        authentication = validated_data.pop('authentication')
        provider_resource_name = authentication.get('provider_resource_name')
        provider_type = validated_data['type']
        interface = ProviderAccessor(provider_type)
        interface.cost_usage_source_ready(provider_resource_name, bucket)

        bill, __ = ProviderBillingSource.objects.get_or_create(
            **billing_source)

        auth, __ = ProviderAuthentication.objects.get_or_create(
            **authentication)

        # We can re-use a billing source or a auth, but not the same combination.
        unique_count = Provider.objects.filter(authentication=auth)\
            .filter(billing_source=bill).count()
        if unique_count != 0:
            error = {
                'Error':
                'A Provider already exists with that Authentication and Billing Source'
            }
            raise serializers.ValidationError(error)

        provider = Provider.objects.create(**validated_data)
        provider.customer = customer
        provider.created_by = user
        provider.authentication = auth
        provider.billing_source = bill
        provider.save()
        return provider
예제 #25
0
class ProviderSerializer(serializers.ModelSerializer):
    """Serializer for the Provider  model."""

    uuid = serializers.UUIDField(read_only=True)
    name = serializers.CharField(max_length=256,
                                 required=True,
                                 allow_null=False,
                                 allow_blank=False)
    type = serializers.ChoiceField(choices=Provider.PROVIDER_CHOICES)
    authentication = ProviderAuthenticationSerializer()
    billing_source = ProviderBillingSourceSerializer()
    customer = CustomerSerializer(read_only=True)
    created_by = UserSerializer(read_only=True)

    class Meta:
        """Metadata for the serializer."""

        model = Provider
        fields = ('uuid', 'name', 'type', 'authentication', 'billing_source',
                  'customer', 'created_by')

    @transaction.atomic
    def create(self, validated_data):
        """Create a user from validated data."""
        authentication = validated_data.pop('authentication')
        billing_source = validated_data.pop('billing_source')

        user = None
        customer = None
        request = self.context.get('request')
        if request and hasattr(request, 'user'):
            user = User.objects.get(pk=request.user.id)
            if user.groups.count() == 1:
                group = user.groups.first()
                customer = Customer.objects.get(pk=group.id)
            else:
                key = 'customer'
                message = 'Group for requesting user could not be found.'
                raise serializers.ValidationError(error_obj(key, message))
        else:
            key = 'created_by'
            message = 'Requesting user could not be found.'
            raise serializers.ValidationError(error_obj(key, message))

        provider_resource_name = authentication.get('provider_resource_name')
        bucket = billing_source.get('bucket')
        access_key_id, secret_access_key, session_token = _get_sts_access(
            provider_resource_name)
        if (access_key_id is None or access_key_id is None
                or session_token is None):
            key = 'provider_resource_name'
            message = 'Unable to obtain credentials with using {}.'.format(
                provider_resource_name)
            raise serializers.ValidationError(error_obj(key, message))

        s3_exists = _check_s3_access(access_key_id, secret_access_key,
                                     session_token, bucket)
        if not s3_exists:
            key = 'bucket'
            message = 'Bucket {} could not be found with {}.'.format(
                bucket, provider_resource_name)
            raise serializers.ValidationError(error_obj(key, message))

        org_access = _check_org_access(access_key_id, secret_access_key,
                                       session_token)
        if not org_access:
            key = 'provider_resource_name'
            message = 'Unable to obtain organization data with {}.'.format(
                provider_resource_name)
            LOG.info(message)

        auth = ProviderAuthentication.objects.create(**authentication)
        bill = ProviderBillingSource.objects.create(**billing_source)
        auth.save()
        bill.save()
        provider = Provider.objects.create(**validated_data)
        provider.customer = customer
        provider.created_by = user
        provider.authentication = auth
        provider.billing_source = bill
        provider.save()
        return provider