Ejemplo n.º 1
0
 def log_change(event_type, dataset, message):
     log_permission_change(
         request.user,
         obj,
         event_type,
         serializers.serialize('python', [dataset])[0],
         message,
     )
Ejemplo n.º 2
0
    def save_model(self, request, obj, form, change):
        original_user_access_type = obj.user_access_type
        obj.user_access_type = (
            'REQUIRES_AUTHORIZATION'
            if form.cleaned_data['requires_authorization']
            else 'REQUIRES_AUTHENTICATION'
        )

        current_authorized_users = set(
            get_user_model().objects.filter(datasetuserpermission__dataset=obj)
        )

        authorized_users = set(
            form.cleaned_data.get('authorized_users', get_user_model().objects.none())
        )

        super().save_model(request, obj, form, change)

        changed_user_sso_ids = set()

        for user in authorized_users - current_authorized_users:
            DataSetUserPermission.objects.create(dataset=obj, user=user)
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_GRANTED_DATASET_PERMISSION,
                {'for_user_id': user.id},
                f"Added dataset {obj} permission",
            )
            changed_user_sso_ids.add(str(user.profile.sso_id))

        for user in current_authorized_users - authorized_users:
            DataSetUserPermission.objects.filter(dataset=obj, user=user).delete()
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_REVOKED_DATASET_PERMISSION,
                {'for_user_id': user.id},
                f"Removed dataset {obj} permission",
            )
            changed_user_sso_ids.add(str(user.profile.sso_id))

        if original_user_access_type != obj.user_access_type:
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_SET_DATASET_USER_ACCESS_TYPE,
                {"access_type": obj.user_access_type},
                f"user_access_type set to {obj.user_access_type}",
            )

        if isinstance(self, MasterDatasetAdmin):
            if changed_user_sso_ids:
                sync_quicksight_permissions.delay(
                    user_sso_ids_to_update=tuple(changed_user_sso_ids)
                )
            elif original_user_access_type != obj.user_access_type:
                sync_quicksight_permissions.delay()
Ejemplo n.º 3
0
def process_visualisation_catalogue_item_authorized_users_change(
    authorized_users,
    request_user,
    visualisation_catalogue_item,
    access_type_changed,
    authorized_email_domains_changed,
):
    current_authorized_users = set(get_user_model().objects.filter(
        visualisationuserpermission__visualisation=visualisation_catalogue_item
    ))

    changed_users = set()

    for user in authorized_users - current_authorized_users:
        VisualisationUserPermission.objects.create(
            visualisation=visualisation_catalogue_item, user=user)
        log_permission_change(
            request_user,
            visualisation_catalogue_item,
            EventLog.TYPE_GRANTED_VISUALISATION_PERMISSION,
            {"for_user_id": user.id},
            f"Added visualisation {visualisation_catalogue_item} permission",
        )
        changed_users.add(user)

    for user in current_authorized_users - authorized_users:
        VisualisationUserPermission.objects.filter(
            visualisation=visualisation_catalogue_item, user=user).delete()
        log_permission_change(
            request_user,
            visualisation_catalogue_item,
            EventLog.TYPE_REVOKED_VISUALISATION_PERMISSION,
            {"for_user_id": user.id},
            f"Removed visualisation {visualisation_catalogue_item} permission",
        )
        changed_users.add(user)

    if access_type_changed or authorized_email_domains_changed:
        log_permission_change(
            request_user,
            visualisation_catalogue_item,
            EventLog.TYPE_SET_DATASET_USER_ACCESS_TYPE if access_type_changed
            else EventLog.TYPE_CHANGED_AUTHORIZED_EMAIL_DOMAIN,
            {"access_type": visualisation_catalogue_item.user_access_type},
            f"user_access_type set to {visualisation_catalogue_item.user_access_type}",
        )

        # As the visualisation's access type has changed, clear cached credentials for all
        # users to ensure they either:
        #   - lose access if it went from REQUIRES_AUTHENTICATION/OPEN to REQUIRES_AUTHORIZATION
        #   - get access if it went from REQUIRES_AUTHORIZATION to REQUIRES_AUTHENTICATION/OPEN
        invalidate_superset_user_cached_credentials()
    else:
        for user in changed_users:
            remove_superset_user_cached_credentials(user)
Ejemplo n.º 4
0
    def save_model(self, request, obj, form, change):
        if obj.visualisation_template and not obj.name:
            obj.name = obj.visualisation_template.nice_name

        original_user_access_type = obj.user_access_type
        obj.user_access_type = (
            'REQUIRES_AUTHORIZATION'
            if form.cleaned_data['requires_authorization']
            else 'REQUIRES_AUTHENTICATION'
        )

        current_authorized_users = set(
            get_user_model().objects.filter(
                visualisationuserpermission__visualisation=obj
            )
        )

        authorized_users = set(
            form.cleaned_data.get('authorized_users', get_user_model().objects.none())
        )

        super().save_model(request, obj, form, change)

        for user in authorized_users - current_authorized_users:
            VisualisationUserPermission.objects.create(visualisation=obj, user=user)
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_GRANTED_VISUALISATION_PERMISSION,
                {'for_user_id': user.id},
                f"Added visualisation {obj} permission",
            )

        for user in current_authorized_users - authorized_users:
            VisualisationUserPermission.objects.filter(
                visualisation=obj, user=user
            ).delete()
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_REVOKED_VISUALISATION_PERMISSION,
                {'for_user_id': user.id},
                f"Removed visualisation {obj} permission",
            )

        if original_user_access_type != obj.user_access_type:
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_SET_DATASET_USER_ACCESS_TYPE,
                {"access_type": obj.user_access_type},
                f"user_access_type set to {obj.user_access_type}",
            )
Ejemplo n.º 5
0
 def log_change(event_type, permission, message):
     log_permission_change(request.user, obj, event_type,
                           {'permission': permission}, message)
Ejemplo n.º 6
0
    def save_model(self, request, obj, form, change):
        obj.username = form.cleaned_data['email']

        def log_change(event_type, permission, message):
            log_permission_change(request.user, obj, event_type,
                                  {'permission': permission}, message)

        start_all_applications_permission = Permission.objects.get(
            codename='start_all_applications',
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )
        develop_visualisations_permission = Permission.objects.get(
            codename='develop_visualisations',
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )
        access_appstream_permission = Permission.objects.get(
            codename='access_appstream',
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )

        if 'can_start_all_applications' in form.cleaned_data:
            if (form.cleaned_data['can_start_all_applications']
                    and start_all_applications_permission
                    not in obj.user_permissions.all()):
                obj.user_permissions.add(start_all_applications_permission)
                log_change(
                    EventLog.TYPE_GRANTED_USER_PERMISSION,
                    'can_start_all_applications',
                    'Added can_start_all_applications permission',
                )
            elif (not form.cleaned_data['can_start_all_applications']
                  and start_all_applications_permission
                  in obj.user_permissions.all()):
                obj.user_permissions.remove(start_all_applications_permission)
                log_change(
                    EventLog.TYPE_REVOKED_USER_PERMISSION,
                    'can_start_all_applications',
                    'Removed can_start_all_applications permission',
                )

        if 'can_develop_visualisations' in form.cleaned_data:
            if (form.cleaned_data['can_develop_visualisations']
                    and develop_visualisations_permission
                    not in obj.user_permissions.all()):
                obj.user_permissions.add(develop_visualisations_permission)
                log_change(
                    EventLog.TYPE_GRANTED_USER_PERMISSION,
                    'can_develop_visualisations',
                    'Added can_develop_visualisations permission',
                )
            elif (not form.cleaned_data['can_develop_visualisations']
                  and develop_visualisations_permission
                  in obj.user_permissions.all()):
                obj.user_permissions.remove(develop_visualisations_permission)
                log_change(
                    EventLog.TYPE_REVOKED_USER_PERMISSION,
                    'can_develop_visualisations',
                    'Removed can_develop_visualisations permission',
                )

        if 'can_access_appstream' in form.cleaned_data:
            if (form.cleaned_data['can_access_appstream']
                    and access_appstream_permission
                    not in obj.user_permissions.all()):
                obj.user_permissions.add(access_appstream_permission)
                log_change(
                    EventLog.TYPE_GRANTED_USER_PERMISSION,
                    'can_access_appstream',
                    'Added can_access_appstream permission',
                )
            elif (not form.cleaned_data['can_access_appstream'] and
                  access_appstream_permission in obj.user_permissions.all()):
                obj.user_permissions.remove(access_appstream_permission)
                log_change(
                    EventLog.TYPE_REVOKED_USER_PERMISSION,
                    'can_access_appstream',
                    'Removed can_access_appstream permission',
                )

        current_datasets = set(
            DataSet.objects.live().filter(datasetuserpermission__user=obj))
        authorized_datasets = set(
            form.cleaned_data.get('authorized_master_datasets',
                                  DataSet.objects.none()).union(
                                      form.cleaned_data.get(
                                          'authorized_data_cut_datasets',
                                          DataSet.objects.none())))

        for dataset in authorized_datasets - current_datasets:
            DataSetUserPermission.objects.create(dataset=dataset, user=obj)
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_GRANTED_DATASET_PERMISSION,
                serializers.serialize('python', [dataset])[0],
                f"Added dataset {dataset} permission",
            )

        for dataset in current_datasets - authorized_datasets:
            DataSetUserPermission.objects.filter(dataset=dataset,
                                                 user=obj).delete()
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_REVOKED_DATASET_PERMISSION,
                serializers.serialize('python', [dataset])[0],
                f"Removed dataset {dataset} permission",
            )

        if 'authorized_visualisations' in form.cleaned_data:
            current_visualisations = ApplicationTemplate.objects.filter(
                application_type='VISUALISATION',
                applicationtemplateuserpermission__user=obj,
            )
            for application_template in form.cleaned_data[
                    'authorized_visualisations']:
                if application_template not in current_visualisations.all():
                    ApplicationTemplateUserPermission.objects.create(
                        application_template=application_template, user=obj)
                    log_permission_change(
                        request.user,
                        obj,
                        EventLog.TYPE_GRANTED_VISUALISATION_PERMISSION,
                        serializers.serialize('python',
                                              [application_template])[0],
                        f"Added application {application_template} permission",
                    )
            for application_template in current_visualisations:
                if (application_template
                        not in form.cleaned_data['authorized_visualisations']):
                    ApplicationTemplateUserPermission.objects.filter(
                        application_template=application_template,
                        user=obj).delete()
                    log_permission_change(
                        request.user,
                        obj,
                        EventLog.TYPE_REVOKED_VISUALISATION_PERMISSION,
                        serializers.serialize('python',
                                              [application_template])[0],
                        f"Removed application {application_template} permission",
                    )

        super().save_model(request, obj, form, change)
Ejemplo n.º 7
0
    def save_model(self, request, obj, form, change):
        obj.username = form.cleaned_data['email']

        def log_change(event_type, permission, message):
            log_permission_change(request.user, obj, event_type,
                                  {'permission': permission}, message)

        start_all_applications_permission = Permission.objects.get(
            codename='start_all_applications',
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )
        develop_visualisations_permission = Permission.objects.get(
            codename='develop_visualisations',
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )
        access_appstream_permission = Permission.objects.get(
            codename='access_appstream',
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )
        access_quicksight_permission = Permission.objects.get(
            codename='access_quicksight',
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )

        if 'can_start_all_applications' in form.cleaned_data:
            if (form.cleaned_data['can_start_all_applications']
                    and start_all_applications_permission
                    not in obj.user_permissions.all()):
                obj.user_permissions.add(start_all_applications_permission)

                if not obj.profile.tools_access_role_arn:
                    create_tools_access_iam_role_task.delay(obj.id)

                log_change(
                    EventLog.TYPE_GRANTED_USER_PERMISSION,
                    'can_start_all_applications',
                    'Added can_start_all_applications permission',
                )
            elif (not form.cleaned_data['can_start_all_applications']
                  and start_all_applications_permission
                  in obj.user_permissions.all()):
                obj.user_permissions.remove(start_all_applications_permission)
                log_change(
                    EventLog.TYPE_REVOKED_USER_PERMISSION,
                    'can_start_all_applications',
                    'Removed can_start_all_applications permission',
                )

        if 'can_develop_visualisations' in form.cleaned_data:
            if (form.cleaned_data['can_develop_visualisations']
                    and develop_visualisations_permission
                    not in obj.user_permissions.all()):
                obj.user_permissions.add(develop_visualisations_permission)
                log_change(
                    EventLog.TYPE_GRANTED_USER_PERMISSION,
                    'can_develop_visualisations',
                    'Added can_develop_visualisations permission',
                )
            elif (not form.cleaned_data['can_develop_visualisations']
                  and develop_visualisations_permission
                  in obj.user_permissions.all()):
                obj.user_permissions.remove(develop_visualisations_permission)
                log_change(
                    EventLog.TYPE_REVOKED_USER_PERMISSION,
                    'can_develop_visualisations',
                    'Removed can_develop_visualisations permission',
                )

        if 'can_access_appstream' in form.cleaned_data:
            if (form.cleaned_data['can_access_appstream']
                    and access_appstream_permission
                    not in obj.user_permissions.all()):
                obj.user_permissions.add(access_appstream_permission)
                log_change(
                    EventLog.TYPE_GRANTED_USER_PERMISSION,
                    'can_access_appstream',
                    'Added can_access_appstream permission',
                )
            elif (not form.cleaned_data['can_access_appstream'] and
                  access_appstream_permission in obj.user_permissions.all()):
                obj.user_permissions.remove(access_appstream_permission)
                log_change(
                    EventLog.TYPE_REVOKED_USER_PERMISSION,
                    'can_access_appstream',
                    'Removed can_access_appstream permission',
                )

        if 'can_access_quicksight' in form.cleaned_data:
            if (form.cleaned_data['can_access_quicksight']
                    and access_quicksight_permission
                    not in obj.user_permissions.all()):
                obj.user_permissions.add(access_quicksight_permission)
                log_change(
                    EventLog.TYPE_GRANTED_USER_PERMISSION,
                    'can_access_quicksight',
                    'Added can_access_quicksight permission',
                )
            elif (not form.cleaned_data['can_access_quicksight'] and
                  access_quicksight_permission in obj.user_permissions.all()):
                obj.user_permissions.remove(access_quicksight_permission)
                log_change(
                    EventLog.TYPE_REVOKED_USER_PERMISSION,
                    'can_access_quicksight',
                    'Removed can_access_quicksight permission',
                )

        current_datasets = set(
            DataSet.objects.live().filter(datasetuserpermission__user=obj))
        authorized_datasets = set(
            form.cleaned_data.get('authorized_master_datasets',
                                  DataSet.objects.none()).union(
                                      form.cleaned_data.get(
                                          'authorized_data_cut_datasets',
                                          DataSet.objects.none())))

        update_quicksight_permissions = False
        for dataset in authorized_datasets - current_datasets:
            DataSetUserPermission.objects.create(dataset=dataset, user=obj)
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_GRANTED_DATASET_PERMISSION,
                serializers.serialize('python', [dataset])[0],
                f"Added dataset {dataset} permission",
            )
            if dataset.type == DataSetType.MASTER.value:
                update_quicksight_permissions = True

        for dataset in current_datasets - authorized_datasets:
            DataSetUserPermission.objects.filter(dataset=dataset,
                                                 user=obj).delete()
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_REVOKED_DATASET_PERMISSION,
                serializers.serialize('python', [dataset])[0],
                f"Removed dataset {dataset} permission",
            )
            if dataset.type == DataSetType.MASTER.value:
                update_quicksight_permissions = True

        if 'authorized_visualisations' in form.cleaned_data:
            current_visualisations = VisualisationCatalogueItem.objects.filter(
                visualisationuserpermission__user=obj)
            for visualisation_catalogue_item in form.cleaned_data[
                    'authorized_visualisations']:
                if visualisation_catalogue_item not in current_visualisations.all(
                ):
                    VisualisationUserPermission.objects.create(
                        visualisation=visualisation_catalogue_item, user=obj)
                    log_permission_change(
                        request.user,
                        obj,
                        EventLog.TYPE_GRANTED_VISUALISATION_PERMISSION,
                        serializers.serialize(
                            'python', [visualisation_catalogue_item])[0],
                        f"Added application {visualisation_catalogue_item} permission",
                    )
            for visualisation_catalogue_item in current_visualisations:
                if (visualisation_catalogue_item
                        not in form.cleaned_data['authorized_visualisations']):
                    VisualisationUserPermission.objects.filter(
                        visualisation=visualisation_catalogue_item,
                        user=obj).delete()
                    log_permission_change(
                        request.user,
                        obj,
                        EventLog.TYPE_REVOKED_VISUALISATION_PERMISSION,
                        serializers.serialize(
                            'python', [visualisation_catalogue_item])[0],
                        f"Removed application {visualisation_catalogue_item} permission",
                    )

        if 'home_directory_efs_access_point_id' in form.cleaned_data:
            obj.profile.home_directory_efs_access_point_id = form.cleaned_data[
                'home_directory_efs_access_point_id']

        if 'tools_access_role_arn' in form.cleaned_data:
            obj.profile.tools_access_role_arn = form.cleaned_data[
                'tools_access_role_arn']

        super().save_model(request, obj, form, change)

        if update_quicksight_permissions:
            sync_quicksight_permissions.delay(
                user_sso_ids_to_update=(str(obj.profile.sso_id), ))
Ejemplo n.º 8
0
def process_dataset_authorized_users_change(
    authorized_users,
    request_user,
    dataset,
    access_type_changed,
    authorized_email_domains_changed,
    is_master_dataset,
):
    current_authorized_users = set(get_user_model().objects.filter(
        datasetuserpermission__dataset=dataset))

    changed_users = set()

    for user in authorized_users - current_authorized_users:
        DataSetUserPermission.objects.create(dataset=dataset, user=user)
        log_permission_change(
            request_user,
            dataset,
            EventLog.TYPE_GRANTED_DATASET_PERMISSION,
            {"for_user_id": user.id},
            f"Added dataset {dataset} permission",
        )
        changed_users.add(user)
        clear_schema_info_cache_for_user(user)

    for user in current_authorized_users - authorized_users:
        DataSetUserPermission.objects.filter(dataset=dataset,
                                             user=user).delete()
        log_permission_change(
            request_user,
            dataset,
            EventLog.TYPE_REVOKED_DATASET_PERMISSION,
            {"for_user_id": user.id},
            f"Removed dataset {dataset} permission",
        )
        changed_users.add(user)
        clear_schema_info_cache_for_user(user)

    if access_type_changed or authorized_email_domains_changed:
        log_permission_change(
            request_user,
            dataset,
            EventLog.TYPE_SET_DATASET_USER_ACCESS_TYPE if access_type_changed
            else EventLog.TYPE_CHANGED_AUTHORIZED_EMAIL_DOMAIN,
            {"access_type": dataset.user_access_type},
            f"user_access_type set to {dataset.user_access_type}",
        )

        # As the dataset's access type has changed, clear cached credentials for all
        # users to ensure they either:
        #   - lose access if it went from REQUIRES_AUTHENTICATION/OPEN to REQUIRES_AUTHORIZATION
        #   - get access if it went from REQUIRES_AUTHORIZATION to REQUIRES_AUTHENTICATION/OPEN
        invalidate_data_explorer_user_cached_credentials()
        invalidate_superset_user_cached_credentials()
    else:
        for user in changed_users:
            remove_data_explorer_user_cached_credentials(user)
            remove_superset_user_cached_credentials(user)

    if is_master_dataset:

        if changed_users:
            # If we're changing permissions for loads of users, let's just do a full quicksight re-sync.
            # Makes fewer AWS calls and probably completes as quickly if not quicker.
            if len(changed_users) >= 50:
                sync_quicksight_permissions.delay()
            else:
                changed_user_sso_ids = [
                    str(u.profile.sso_id) for u in changed_users
                ]
                sync_quicksight_permissions.delay(
                    user_sso_ids_to_update=tuple(changed_user_sso_ids))
        elif access_type_changed:
            sync_quicksight_permissions.delay()
Ejemplo n.º 9
0
    def save_model(self, request, obj, form, change):
        original_user_access_type = obj.user_access_type
        obj.user_access_type = ('REQUIRES_AUTHORIZATION'
                                if form.cleaned_data['requires_authorization']
                                else 'REQUIRES_AUTHENTICATION')

        current_authorized_users = set(get_user_model().objects.filter(
            datasetuserpermission__dataset=obj))

        authorized_users = set(
            form.cleaned_data.get('authorized_users',
                                  get_user_model().objects.none()))

        super().save_model(request, obj, form, change)

        changed_user_sso_ids = set()

        clear_schema_info_cache = False
        for user in authorized_users - current_authorized_users:
            DataSetUserPermission.objects.create(dataset=obj, user=user)
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_GRANTED_DATASET_PERMISSION,
                {'for_user_id': user.id},
                f"Added dataset {obj} permission",
            )
            changed_user_sso_ids.add(str(user.profile.sso_id))
            clear_schema_info_cache = True

        for user in current_authorized_users - authorized_users:
            DataSetUserPermission.objects.filter(dataset=obj,
                                                 user=user).delete()
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_REVOKED_DATASET_PERMISSION,
                {'for_user_id': user.id},
                f"Removed dataset {obj} permission",
            )
            changed_user_sso_ids.add(str(user.profile.sso_id))
            clear_schema_info_cache = True

        if clear_schema_info_cache:
            clear_schema_info_cache_for_user(user)

        if original_user_access_type != obj.user_access_type:
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_SET_DATASET_USER_ACCESS_TYPE,
                {"access_type": obj.user_access_type},
                f"user_access_type set to {obj.user_access_type}",
            )

        if isinstance(self, MasterDatasetAdmin):
            if changed_user_sso_ids:
                # If we're changing permissions for loads of users, let's just do a full quicksight re-sync.
                # Makes fewer AWS calls and probably completes as quickly if not quicker.
                if len(changed_user_sso_ids) >= 50:
                    sync_quicksight_permissions.delay()
                else:
                    sync_quicksight_permissions.delay(
                        user_sso_ids_to_update=tuple(changed_user_sso_ids))
            elif original_user_access_type != obj.user_access_type:
                sync_quicksight_permissions.delay()
Ejemplo n.º 10
0
    def save_model(self, request, obj, form, change):
        obj.username = form.cleaned_data["email"]

        def log_change(event_type, permission, message):
            log_permission_change(request.user, obj, event_type,
                                  {"permission": permission}, message)

        start_all_applications_permission = Permission.objects.get(
            codename="start_all_applications",
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )
        develop_visualisations_permission = Permission.objects.get(
            codename="develop_visualisations",
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )
        access_appstream_permission = Permission.objects.get(
            codename="access_appstream",
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )
        access_quicksight_permission = Permission.objects.get(
            codename="access_quicksight",
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )

        if "can_start_all_applications" in form.cleaned_data:
            if (form.cleaned_data["can_start_all_applications"]
                    and start_all_applications_permission
                    not in obj.user_permissions.all()):
                obj.user_permissions.add(start_all_applications_permission)

                if not obj.profile.tools_access_role_arn:
                    create_tools_access_iam_role_task.delay(obj.id)

                log_change(
                    EventLog.TYPE_GRANTED_USER_PERMISSION,
                    "can_start_all_applications",
                    "Added can_start_all_applications permission",
                )
            elif (not form.cleaned_data["can_start_all_applications"]
                  and start_all_applications_permission
                  in obj.user_permissions.all()):
                obj.user_permissions.remove(start_all_applications_permission)
                log_change(
                    EventLog.TYPE_REVOKED_USER_PERMISSION,
                    "can_start_all_applications",
                    "Removed can_start_all_applications permission",
                )

        if "can_develop_visualisations" in form.cleaned_data:
            if (form.cleaned_data["can_develop_visualisations"]
                    and develop_visualisations_permission
                    not in obj.user_permissions.all()):
                obj.user_permissions.add(develop_visualisations_permission)
                log_change(
                    EventLog.TYPE_GRANTED_USER_PERMISSION,
                    "can_develop_visualisations",
                    "Added can_develop_visualisations permission",
                )
            elif (not form.cleaned_data["can_develop_visualisations"]
                  and develop_visualisations_permission
                  in obj.user_permissions.all()):
                obj.user_permissions.remove(develop_visualisations_permission)
                log_change(
                    EventLog.TYPE_REVOKED_USER_PERMISSION,
                    "can_develop_visualisations",
                    "Removed can_develop_visualisations permission",
                )

        if "can_access_appstream" in form.cleaned_data:
            if (form.cleaned_data["can_access_appstream"]
                    and access_appstream_permission
                    not in obj.user_permissions.all()):
                try:
                    add_user_access_profile(obj, "appstream")
                except SSOApiException as e:
                    messages.error(
                        request,
                        "Unable to give user access to appstream via SSO API: %s"
                        % e,
                    )

                obj.user_permissions.add(access_appstream_permission)
                log_change(
                    EventLog.TYPE_GRANTED_USER_PERMISSION,
                    "can_access_appstream",
                    "Added can_access_appstream permission",
                )
            elif (not form.cleaned_data["can_access_appstream"] and
                  access_appstream_permission in obj.user_permissions.all()):
                try:
                    remove_user_access_profile(obj, "appstream")
                except SSOApiException as e:
                    messages.error(
                        request,
                        "Unable to revoke user access to appstream via SSO API: %s"
                        % e,
                    )

                obj.user_permissions.remove(access_appstream_permission)
                log_change(
                    EventLog.TYPE_REVOKED_USER_PERMISSION,
                    "can_access_appstream",
                    "Removed can_access_appstream permission",
                )

        if "can_access_quicksight" in form.cleaned_data:
            if (form.cleaned_data["can_access_quicksight"]
                    and access_quicksight_permission
                    not in obj.user_permissions.all()):
                try:
                    add_user_access_profile(obj, "quicksight")
                except SSOApiException as e:
                    messages.error(
                        request,
                        "Unable to give user access to quicksight via SSO API: %s"
                        % e,
                    )

                obj.user_permissions.add(access_quicksight_permission)
                log_change(
                    EventLog.TYPE_GRANTED_USER_PERMISSION,
                    "can_access_quicksight",
                    "Added can_access_quicksight permission",
                )
            elif (not form.cleaned_data["can_access_quicksight"] and
                  access_quicksight_permission in obj.user_permissions.all()):
                try:
                    remove_user_access_profile(obj, "quicksight")
                except SSOApiException as e:
                    messages.error(
                        request,
                        "Unable to revoke user access to quicksight via SSO API: %s"
                        % e,
                    )

                obj.user_permissions.remove(access_quicksight_permission)
                log_change(
                    EventLog.TYPE_REVOKED_USER_PERMISSION,
                    "can_access_quicksight",
                    "Removed can_access_quicksight permission",
                )

        current_datasets = set(
            DataSet.objects.live().filter(datasetuserpermission__user=obj))
        authorized_datasets = set(
            form.cleaned_data.get("authorized_master_datasets",
                                  DataSet.objects.none()).union(
                                      form.cleaned_data.get(
                                          "authorized_data_cut_datasets",
                                          DataSet.objects.none())))

        update_quicksight_permissions = False
        clear_schema_info_and_credentials_cache = False
        for dataset in authorized_datasets - current_datasets:
            DataSetUserPermission.objects.create(dataset=dataset, user=obj)
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_GRANTED_DATASET_PERMISSION,
                serializers.serialize("python", [dataset])[0],
                f"Added dataset {dataset} permission",
            )
            if dataset.type == DataSetType.MASTER:
                update_quicksight_permissions = True
            clear_schema_info_and_credentials_cache = True

        for dataset in current_datasets - authorized_datasets:
            DataSetUserPermission.objects.filter(dataset=dataset,
                                                 user=obj).delete()
            log_permission_change(
                request.user,
                obj,
                EventLog.TYPE_REVOKED_DATASET_PERMISSION,
                serializers.serialize("python", [dataset])[0],
                f"Removed dataset {dataset} permission",
            )
            if dataset.type == DataSetType.MASTER:
                update_quicksight_permissions = True
            clear_schema_info_and_credentials_cache = True

        if clear_schema_info_and_credentials_cache and obj.pk:
            clear_schema_info_cache_for_user(obj)
            remove_data_explorer_user_cached_credentials(obj)

        if "authorized_visualisations" in form.cleaned_data:
            current_visualisations = VisualisationCatalogueItem.objects.filter(
                visualisationuserpermission__user=obj)
            for visualisation_catalogue_item in form.cleaned_data[
                    "authorized_visualisations"]:
                if visualisation_catalogue_item not in current_visualisations.all(
                ):
                    VisualisationUserPermission.objects.create(
                        visualisation=visualisation_catalogue_item, user=obj)
                    log_permission_change(
                        request.user,
                        obj,
                        EventLog.TYPE_GRANTED_VISUALISATION_PERMISSION,
                        serializers.serialize(
                            "python", [visualisation_catalogue_item])[0],
                        f"Added application {visualisation_catalogue_item} permission",
                    )
            for visualisation_catalogue_item in current_visualisations:
                if (visualisation_catalogue_item
                        not in form.cleaned_data["authorized_visualisations"]):
                    VisualisationUserPermission.objects.filter(
                        visualisation=visualisation_catalogue_item,
                        user=obj).delete()
                    log_permission_change(
                        request.user,
                        obj,
                        EventLog.TYPE_REVOKED_VISUALISATION_PERMISSION,
                        serializers.serialize(
                            "python", [visualisation_catalogue_item])[0],
                        f"Removed application {visualisation_catalogue_item} permission",
                    )

        if "home_directory_efs_access_point_id" in form.cleaned_data:
            obj.profile.home_directory_efs_access_point_id = form.cleaned_data[
                "home_directory_efs_access_point_id"]

        if "tools_access_role_arn" in form.cleaned_data:
            obj.profile.tools_access_role_arn = form.cleaned_data[
                "tools_access_role_arn"]

        super().save_model(request, obj, form, change)

        if update_quicksight_permissions:
            sync_quicksight_permissions.delay(
                user_sso_ids_to_update=(str(obj.profile.sso_id), ))