Exemplo n.º 1
0
class UserSettingsUpdateSerializer(UserSettingsSerializer):
    id = IDField(source='_id', required=True)
    two_factor_enabled = ser.BooleanField(write_only=True, required=False)
    two_factor_verification = ser.IntegerField(write_only=True, required=False)
    subscribe_osf_general_email = ser.BooleanField(read_only=False, required=False)
    subscribe_osf_help_email = ser.BooleanField(read_only=False, required=False)

    # Keys represent field names values represent the human readable names stored in DB.
    MAP_MAIL = {
        'subscribe_osf_help_email': OSF_HELP_LIST,
        'subscribe_osf_general_email': MAILCHIMP_GENERAL_LIST,
    }

    def update_email_preferences(self, instance, attr, value):
        if self.MAP_MAIL[attr] == OSF_HELP_LIST:
            update_osf_help_mails_subscription(user=instance, subscribe=value)
        else:
            update_mailchimp_subscription(instance, self.MAP_MAIL[attr], value)
        instance.save()

    def update_two_factor(self, instance, value, two_factor_addon):
        if value:
            if not two_factor_addon:
                two_factor_addon = instance.get_or_add_addon('twofactor')
                two_factor_addon.save()
        else:
            auth = get_user_auth(self.context['request'])
            instance.delete_addon('twofactor', auth=auth)

        return two_factor_addon

    def verify_two_factor(self, instance, value, two_factor_addon):
        if not two_factor_addon:
            raise exceptions.ValidationError(detail='Two-factor authentication is not enabled.')
        if two_factor_addon.verify_code(value):
            two_factor_addon.is_confirmed = True
        else:
            raise exceptions.PermissionDenied(detail='The two-factor verification code you provided is invalid.')
        two_factor_addon.save()

    def to_representation(self, instance):
        """
        Overriding to_representation allows using different serializers for the request and response.
        """
        context = self.context
        return UserSettingsSerializer(instance=instance, context=context).data

    def update(self, instance, validated_data):

        for attr, value in validated_data.items():
            if 'two_factor_enabled' == attr:
                two_factor_addon = instance.get_addon('twofactor')
                self.update_two_factor(instance, value, two_factor_addon)
            elif 'two_factor_verification' == attr:
                two_factor_addon = instance.get_addon('twofactor')
                self.verify_two_factor(instance, value, two_factor_addon)
            elif attr in self.MAP_MAIL.keys():
                self.update_email_preferences(instance, attr, value)

        return instance
Exemplo n.º 2
0
class UserIdentitiesSerializer(JSONAPISerializer):
    id = IDField(source='_id', read_only=True)
    type = TypeField()
    external_id = ser.CharField(read_only=True)
    status = ser.CharField(read_only=True)

    links = LinksField({
        'self': 'get_absolute_url',
    })

    def get_absolute_url(self, obj):
        return absolute_reverse(
            'users:user-identities-detail',
            kwargs={
                'user_id':
                self.context['request'].parser_context['kwargs']['user_id'],
                'version':
                self.context['request'].parser_context['kwargs']['version'],
                'identity_id':
                obj['_id'],
            },
        )

    class Meta:
        type_ = 'external-identities'
Exemplo n.º 3
0
class UserSettingsSerializer(JSONAPISerializer):
    id = IDField(source='_id', read_only=True)
    type = TypeField()
    two_factor_enabled = ser.SerializerMethodField()
    subscribe_osf_general_email = ser.SerializerMethodField()
    subscribe_osf_help_email = ser.SerializerMethodField()

    def get_two_factor_enabled(self, obj):
        try:
            two_factor = TwoFactorUserSettings.objects.get(owner_id=obj.id)
            return not two_factor.deleted
        except TwoFactorUserSettings.DoesNotExist:
            return False

    def get_subscribe_osf_general_email(self, obj):
        return obj.mailchimp_mailing_lists.get(MAILCHIMP_GENERAL_LIST, False)

    def get_subscribe_osf_help_email(self, obj):
        return obj.osf_mailing_lists.get(OSF_HELP_LIST, False)

    links = LinksField({
        'self': 'get_absolute_url',
    })

    def get_absolute_url(self, obj):
        return absolute_reverse(
            'users:user_settings',
            kwargs={
                'user_id': obj._id,
                'version': self.context['request'].parser_context['kwargs']['version'],
            },
        )

    class Meta:
        type_ = 'user_settings'
Exemplo n.º 4
0
class NodeContributorDetailSerializer(NodeContributorsSerializer):
    """
    Overrides node contributor serializer to add additional methods
    """
    id = IDField(required=True, source='_id')
    index = ser.IntegerField(required=False, read_only=False, source='_order')

    def update(self, instance, validated_data):
        index = None
        if '_order' in validated_data:
            index = validated_data.pop('_order')

        auth = Auth(self.context['request'].user)
        node = self.context['view'].get_node()

        if 'bibliographic' in validated_data:
            bibliographic = validated_data.get('bibliographic')
        else:
            bibliographic = node.get_visible(instance.user)
        permission = validated_data.get('permission') or instance.permission
        try:
            if index is not None:
                node.move_contributor(instance.user, auth, index, save=True)
            node.update_contributor(instance.user,
                                    permission,
                                    bibliographic,
                                    auth,
                                    save=True)
        except NodeStateError as e:
            raise exceptions.ValidationError(detail=e.message)
        except ValueError as e:
            raise exceptions.ValidationError(detail=e.message)
        instance.refresh_from_db()
        return instance
class ApiOAuth2ApplicationDetailSerializer(ApiOAuth2ApplicationSerializer):
    """
    Overrides ApiOAuth2ApplicationSerializer to make id required
    and client_secret writable to reset the secret via API patch request
    """

    id = IDField(
        source='client_id',
        required=True,
        help_text='The client ID for this application (automatically generated)'
    )

    client_secret = ser.CharField(
        help_text=
        'The client secret for this application (automatically generated)',
        allow_null=True,
        required=False,
    )

    def update(self, instance, validated_data):
        assert isinstance(
            instance,
            ApiOAuth2Application), 'instance must be an ApiOAuth2Application'
        client_secret = validated_data.pop('client_secret', None)
        if client_secret == '':
            instance.reset_secret(save=True)
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        try:
            instance.save()
        except ValidationError as e:
            detail = format_validation_error(e)
            raise exceptions.ValidationError(detail=detail)
        return instance
Exemplo n.º 6
0
class ApiOAuthApplicationBaseSerializer(JSONAPISerializer):
    """Base serializer class for OAuth2 applications """
    id = IDField(source='client_id', read_only=True, help_text='The client ID for this application (automatically generated)')

    type = TypeField()

    client_id = ser.CharField(help_text='The client ID for this application (automatically generated)',
                              read_only=True)

    client_secret = ser.CharField(help_text='The client secret for this application (automatically generated)',
                                  read_only=True)  # TODO: May change this later

    links = LinksField({
        'html': 'absolute_url',
        'reset': 'reset_url'
    })

    def absolute_url(self, obj):
        return obj.absolute_url

    def get_absolute_url(self, obj):
        return obj.get_absolute_url()

    def reset_url(self, obj):
        return absolute_reverse('applications:application-reset', kwargs={
            'client_id': obj.client_id,
            'version': self.context['request'].parser_context['kwargs']['version']
        })

    class Meta:
        type_ = 'applications'
Exemplo n.º 7
0
class PreprintCreateSerializer(PreprintSerializer):
    # Overrides PreprintSerializer to make id nullable, adds `create`
    id = IDField(source='_id', required=False, allow_null=True)

    def create(self, validated_data):
        node = validated_data.pop('node', None)
        if not node:
            raise exceptions.NotFound('Unable to find Node with specified id.')
        elif node.is_deleted:
            raise exceptions.ValidationError('Cannot create a preprint from a deleted node.')

        auth = get_user_auth(self.context['request'])
        if not node.has_permission(auth.user, permissions.ADMIN):
            raise exceptions.PermissionDenied

        primary_file = validated_data.pop('primary_file', None)
        if not primary_file:
            raise exceptions.ValidationError(detail='You must specify a valid primary_file to create a preprint.')

        provider = validated_data.pop('provider', None)
        if not provider:
            raise exceptions.ValidationError(detail='You must specify a valid provider to create a preprint.')

        if PreprintService.find(Q('node', 'eq', node) & Q('provider', 'eq', provider)).count():
            conflict = PreprintService.find_one(Q('node', 'eq', node) & Q('provider', 'eq', provider))
            raise Conflict('Only one preprint per provider can be submitted for a node. Check `meta[existing_resource_id]`.', meta={'existing_resource_id': conflict._id})

        preprint = PreprintService(node=node, provider=provider)
        self.set_field(preprint.set_primary_file, primary_file, auth)
        preprint.save()
        preprint.node._has_abandoned_preprint = True
        preprint.node.save()

        return self.update(preprint, validated_data)
Exemplo n.º 8
0
class ViewOnlyLinkDetailSerializer(JSONAPISerializer):
    key = ser.CharField(read_only=True)
    id = IDField(source='_id', read_only=True)
    date_created = DateByVersion(read_only=True)
    anonymous = ser.BooleanField(required=False)
    name = ser.CharField(required=False)

    nodes = RelationshipField(
        related_view='view-only-links:view-only-link-nodes',
        related_view_kwargs={'link_id': '<_id>'},
        self_view='view-only-links:view-only-link-nodes-relationships',
        self_view_kwargs={'link_id': '<_id>'})

    creator = RelationshipField(
        related_view='users:user-detail',
        related_view_kwargs={'user_id': '<creator._id>'},
    )

    def get_absolute_url(self, obj):
        return absolute_reverse(
            'nodes:node-view-only-link-detail',
            kwargs={
                'link_id':
                obj._id,
                'version':
                self.context['request'].parser_context['kwargs']['version']
            })

    class Meta:
        type_ = 'view-only-links'
Exemplo n.º 9
0
class DraftRegistrationDetailSerializer(DraftRegistrationSerializer):
    """
    Overrides DraftRegistrationSerializer to make id and registration_metadata required.
    registration_supplement cannot be changed after draft has been created.

    Also makes registration_supplement read-only.
    """
    id = IDField(source='_id', required=True)
    registration_metadata = ser.DictField(required=True)
    registration_supplement = ser.CharField(read_only=True,
                                            source='registration_schema._id')

    def update(self, draft, validated_data):
        """
        Update draft instance with the validated metadata.
        """
        metadata = validated_data.pop('registration_metadata', None)
        reviewer = is_prereg_admin_not_project_admin(self.context['request'],
                                                     draft)
        if metadata:
            try:
                # Required fields are only required when creating the actual registration, not updating the draft.
                draft.validate_metadata(metadata=metadata,
                                        reviewer=reviewer,
                                        required_fields=False)
            except ValidationError as e:
                raise exceptions.ValidationError(e.message)
            draft.update_metadata(metadata)
            draft.save()
        return draft
Exemplo n.º 10
0
class DraftRegistrationSerializer(JSONAPISerializer):

    id = IDField(source='_id', read_only=True)
    type = TypeField()
    registration_supplement = ser.CharField(source='registration_schema._id',
                                            required=True)
    registration_metadata = ser.DictField(required=False)
    datetime_initiated = VersionedDateTimeField(read_only=True)
    datetime_updated = VersionedDateTimeField(read_only=True)

    branched_from = RelationshipField(
        related_view='nodes:node-detail',
        related_view_kwargs={'node_id': '<branched_from._id>'})

    initiator = RelationshipField(
        related_view='users:user-detail',
        related_view_kwargs={'user_id': '<initiator._id>'},
    )

    registration_schema = RelationshipField(
        related_view='metaschemas:metaschema-detail',
        related_view_kwargs={'metaschema_id': '<registration_schema._id>'})

    links = LinksField({'html': 'get_absolute_url'})

    def get_absolute_url(self, obj):
        return obj.absolute_url

    def create(self, validated_data):
        node = validated_data.pop('node')
        initiator = validated_data.pop('initiator')
        metadata = validated_data.pop('registration_metadata', None)

        schema_id = validated_data.pop('registration_schema').get('_id')
        schema = get_object_or_error(MetaSchema, schema_id,
                                     self.context['request'])
        if schema.schema_version != LATEST_SCHEMA_VERSION or not schema.active:
            raise exceptions.ValidationError(
                'Registration supplement must be an active schema.')

        draft = DraftRegistration.create_from_node(node=node,
                                                   user=initiator,
                                                   schema=schema)
        reviewer = is_prereg_admin_not_project_admin(self.context['request'],
                                                     draft)

        if metadata:
            try:
                # Required fields are only required when creating the actual registration, not updating the draft.
                draft.validate_metadata(metadata=metadata,
                                        reviewer=reviewer,
                                        required_fields=False)
            except ValidationError as e:
                raise exceptions.ValidationError(e.message)
            draft.update_metadata(metadata)
            draft.save()
        return draft

    class Meta:
        type_ = 'draft_registrations'
Exemplo n.º 11
0
class ApiOAuth2PersonalTokenSerializer(JSONAPISerializer):
    """Serialize data about a registered personal access token"""

    id = IDField(source='_id', read_only=True, help_text='The object ID for this token (automatically generated)')
    type = TypeField()

    name = ser.CharField(help_text='A short, descriptive name for this token',
                         required=True)

    owner = ser.CharField(help_text='The user who owns this token',
                          read_only=True,  # Don't let user register a token in someone else's name
                          source='owner._id')

    scopes = ser.CharField(help_text='Governs permissions associated with this token',
                           required=True)

    token_id = ser.CharField(read_only=True, allow_blank=True)

    class Meta:
        type_ = 'tokens'

    links = LinksField({
        'html': 'absolute_url'
    })

    def absolute_url(self, obj):
        return obj.absolute_url

    def to_representation(self, obj, envelope='data'):
        data = super(ApiOAuth2PersonalTokenSerializer, self).to_representation(obj, envelope=envelope)
        # Make sure users only see token_id on create
        if not self.context['request'].method == 'POST':
            if 'data' in data:
                data['data']['attributes'].pop('token_id')
            else:
                data['attributes'].pop('token_id')

        return data

    def create(self, validated_data):
        validate_requested_scopes(validated_data)
        instance = ApiOAuth2PersonalToken(**validated_data)
        instance.save()
        return instance

    def update(self, instance, validated_data):
        validate_requested_scopes(validated_data)
        assert isinstance(instance, ApiOAuth2PersonalToken), 'instance must be an ApiOAuth2PersonalToken'

        instance.deactivate(save=False)  # This will cause CAS to revoke the existing token but still allow it to be used in the future, new scopes will be updated properly at that time.
        instance.reload()

        for attr, value in validated_data.iteritems():
            if attr == 'token_id':  # Do not allow user to update token_id
                continue
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance
class ApiOAuth2ApplicationSerializer(ApiOAuthApplicationBaseSerializer):
    """Serialize data about a registered OAuth2 application"""

    id = IDField(
        source='client_id',
        read_only=True,
        help_text='The client ID for this application (automatically generated)'
    )

    type = TypeField()

    name = ser.CharField(
        help_text='A short, descriptive name for this application',
        required=True,
    )

    description = ser.CharField(
        help_text=
        'An optional description displayed to all users of this application',
        required=False,
        allow_blank=True,
    )
    home_url = ser.CharField(
        help_text="The full URL to this application's homepage.",
        required=True,
        validators=[URLValidator()],
        label='Home URL',
    )

    callback_url = ser.CharField(
        help_text=
        'The callback URL for this application (refer to OAuth documentation)',
        required=True,
        validators=[URLValidator()],
        label='Callback URL',
    )

    owner = ser.CharField(
        help_text='The id of the user who owns this application',
        read_only=
        True,  # Don't let user register an application in someone else's name
        source='owner._id',
    )

    date_created = VersionedDateTimeField(
        source='created',
        help_text=
        'The date this application was generated (automatically filled in)',
        read_only=True,
    )

    def create(self, validated_data):
        instance = ApiOAuth2Application(**validated_data)
        try:
            instance.save()
        except ValidationError as e:
            detail = format_validation_error(e)
            raise exceptions.ValidationError(detail=detail)
        return instance
Exemplo n.º 13
0
class NodeViewOnlyLinkSerializer(JSONAPISerializer):
    filterable_fields = frozenset([
        'anonymous',
        'name',
        'date_created'
    ])

    key = ser.CharField(read_only=True)
    id = IDField(source='_id', read_only=True)
    date_created = DateByVersion(read_only=True)
    anonymous = ser.BooleanField(required=False, default=False)
    name = ser.CharField(required=False, default='Shared project link')

    links = LinksField({
        'self': 'get_absolute_url'
    })

    creator = RelationshipField(
        related_view='users:user-detail',
        related_view_kwargs={'user_id': '<creator._id>'},
    )

    nodes = RelationshipField(
        related_view='view-only-links:view-only-link-nodes',
        related_view_kwargs={'link_id': '<_id>'},
        self_view='view-only-links:view-only-link-nodes-relationships',
        self_view_kwargs={'link_id': '<_id>'}
    )

    def create(self, validated_data):
        name = validated_data.pop('name')
        user = get_user_auth(self.context['request']).user
        anonymous = validated_data.pop('anonymous')
        node = self.context['view'].get_node()

        try:
            view_only_link = new_private_link(
                name=name,
                user=user,
                nodes=[node],
                anonymous=anonymous
            )
        except ValidationValueError:
            raise exceptions.ValidationError('Invalid link name.')

        return view_only_link

    def get_absolute_url(self, obj):
        return absolute_reverse(
            'nodes:node-view-only-link-detail',
            kwargs={
                'link_id': obj._id,
                'node_id': self.context['request'].parser_context['kwargs']['node_id'],
                'version': self.context['request'].parser_context['kwargs']['version']
            }
        )

    class Meta:
        type_ = 'view_only_links'
Exemplo n.º 14
0
class CollectedMetaSerializer(TaxonomizableSerializerMixin, JSONAPISerializer):
    class Meta:
        type_ = 'collected-metadata'

    filterable_fields = frozenset([
        'id',
        'collected_type',
        'date_created',
        'date_modified',
        'subjects',
        'status',
    ])
    id = IDField(source='guid._id', read_only=True)
    type = TypeField()

    creator = RelationshipField(
        related_view='users:user-detail',
        related_view_kwargs={'user_id': '<creator._id>'},
    )
    collection = RelationshipField(
        related_view='collections:collection-detail',
        related_view_kwargs={'collection_id': '<collection._id>'},
    )
    guid = RelationshipField(
        related_view='guids:guid-detail',
        related_view_kwargs={'guids': '<guid._id>'},
        always_embed=True,
    )
    collected_type = ser.CharField(required=False)
    status = ser.CharField(required=False)

    def get_absolute_url(self, obj):
        return absolute_reverse(
            'collected-metadata:collected-metadata-detail',
            kwargs={
                'collection_id':
                obj.collection._id,
                'cgm_id':
                obj.guid._id,
                'version':
                self.context['request'].parser_context['kwargs']['version']
            })

    def update(self, obj, validated_data):
        if validated_data and 'subjects' in validated_data:
            auth = get_user_auth(self.context['request'])
            subjects = validated_data.pop('subjects', None)
            try:
                obj.set_subjects(subjects, auth)
            except PermissionsError as e:
                raise exceptions.PermissionDenied(detail=e.message)
            except (ValueError, NodeStateError) as e:
                raise exceptions.ValidationError(detail=e.message)
        if 'status' in validated_data:
            obj.status = validated_data.pop('status')
        if 'collected_type' in validated_data:
            obj.collected_type = validated_data.pop('collected_type')
        obj.save()
        return obj
class DraftRegistrationContributorDetailSerializer(NodeContributorDetailSerializer, DraftRegistrationContributorsSerializer):
    """
    Overrides NodeContributorDetailSerializer to set the draft registration instead of the node

    id and index redefined because of the two serializers we've inherited
    """
    id = IDField(required=True, source='_id')
    index = ser.IntegerField(required=False, read_only=False, source='_order')
class MeetingSerializer(JSONAPISerializer):

    filterable_fields = frozenset([
        'name',
        'location',
    ])

    id = IDField(source='endpoint', read_only=True)
    type = TypeField()
    name = ser.CharField(read_only=True)
    location = ser.CharField(read_only=True)
    start_date = VersionedDateTimeField(read_only=True)
    end_date = VersionedDateTimeField(read_only=True)
    info_url = ser.URLField(read_only=True)
    logo_url = ser.URLField(read_only=True)
    field_names = ser.DictField(read_only=True)
    submissions_count = ser.SerializerMethodField()
    active = ser.BooleanField(read_only=True)
    type_one_submission_email = ser.SerializerMethodField()
    type_two_submission_email = ser.SerializerMethodField()
    is_accepting_type_one = ser.BooleanField(source='poster', read_only=True)
    is_accepting_type_two = ser.BooleanField(source='talk', read_only=True)

    submissions = RelationshipField(
        related_view='meetings:meeting-submissions',
        related_view_kwargs={'meeting_id': '<endpoint>'},
        related_meta={'count': 'get_submissions_count'},
    )

    links = LinksField({
        'self': 'get_absolute_url',
        'html': 'get_absolute_html_url',
    })

    def format_submission_email(self, obj, submission_field):
        if obj.active:
            return '{}-{}@osf.io'.format(obj.endpoint,
                                         obj.field_names.get(submission_field))
        return ''

    def get_type_one_submission_email(self, obj):
        return self.format_submission_email(obj, 'submission1')

    def get_type_two_submission_email(self, obj):
        return self.format_submission_email(obj, 'submission2')

    def get_absolute_url(self, obj):
        return absolute_reverse('meetings:meeting-detail',
                                kwargs={'meeting_id': obj.endpoint})

    def get_submissions_count(self, obj):
        if getattr(obj, 'submissions_count', None):
            return obj.submissions_count
        else:
            return obj.valid_submissions.count()

    class Meta:
        type_ = 'meetings'
Exemplo n.º 17
0
class CommentSerializer(JSONAPISerializer):

    filterable_fields = frozenset(
        ['deleted', 'date_created', 'date_modified', 'target'])

    id = IDField(source='_id', read_only=True)
    type = TypeField()
    content = AuthorizedCharField(source='get_content')

    target = TargetField(link_type='related', meta={'type': 'get_target_type'})
    user = RelationshipField(related_view='users:user-detail',
                             related_view_kwargs={'user_id': '<user._id>'})
    node = RelationshipField(related_view='nodes:node-detail',
                             related_view_kwargs={'node_id': '<node._id>'})
    replies = RelationshipField(self_view='comments:comment-replies',
                                self_view_kwargs={'comment_id': '<pk>'})
    reports = RelationshipField(related_view='comments:comment-reports',
                                related_view_kwargs={'comment_id': '<pk>'})

    date_created = ser.DateTimeField(read_only=True)
    date_modified = ser.DateTimeField(read_only=True)
    modified = ser.BooleanField(read_only=True, default=False)
    deleted = ser.BooleanField(read_only=True,
                               source='is_deleted',
                               default=False)

    # LinksField.to_representation adds link to "self"
    links = LinksField({})

    class Meta:
        type_ = 'comments'

    def update(self, comment, validated_data):
        assert isinstance(comment, Comment), 'comment must be a Comment'
        auth = Auth(self.context['request'].user)
        if validated_data:
            if 'get_content' in validated_data:
                comment.edit(validated_data['get_content'],
                             auth=auth,
                             save=True)
            if validated_data.get('is_deleted', None) is True:
                comment.delete(auth, save=True)
            elif comment.is_deleted:
                comment.undelete(auth, save=True)
        return comment

    def get_target_type(self, obj):
        if isinstance(obj, Node):
            return 'nodes'
        elif isinstance(obj, Comment):
            return 'comments'
        else:
            raise InvalidModelValueError(source={
                'pointer':
                '/data/relationships/target/links/related/meta/type'
            },
                                         detail='Invalid comment target type.')
Exemplo n.º 18
0
class NodeLinksSerializer(JSONAPISerializer):

    id = IDField(source='_id')
    type = TypeField()
    target_type = TargetTypeField(target_type='nodes')

    # TODO: We don't show the title because the current user may not have access to this node. We may want to conditionally
    # include this field in the future.
    # title = ser.CharField(read_only=True, source='node.title', help_text='The title of the node that this Node Link '
    #                                                                      'points to')

    target_node = RelationshipField(
        related_view='nodes:node-detail',
        related_view_kwargs={'node_id': '<pk>'},
        always_embed=True

    )
    class Meta:
        type_ = 'node_links'

    links = LinksField({
        'self': 'get_absolute_url'
    })

    def get_absolute_url(self, obj):
        node_id = self.context['request'].parser_context['kwargs']['node_id']
        return absolute_reverse(
            'nodes:node-pointer-detail',
            kwargs={
                'node_id': node_id,
                'node_link_id': obj._id
            }
        )

    def create(self, validated_data):
        request = self.context['request']
        user = request.user
        auth = Auth(user)
        node = self.context['view'].get_node()
        target_node_id = validated_data['_id']
        pointer_node = Node.load(target_node_id)
        if not pointer_node or pointer_node.is_folder:
            raise InvalidModelValueError(
                source={'pointer': '/data/relationships/node_links/data/id'},
                detail='Target Node \'{}\' not found.'.format(target_node_id)
            )
        try:
            pointer = node.add_pointer(pointer_node, auth, save=True)
            return pointer
        except ValueError:
            raise InvalidModelValueError(
                source={'pointer': '/data/relationships/node_links/data/id'},
                detail='Target Node \'{}\' already pointed to by \'{}\'.'.format(target_node_id, node._id)
            )

    def update(self, instance, validated_data):
        pass
Exemplo n.º 19
0
class ApiOAuth2ApplicationSerializer(ApiOAuthApplicationBaseSerializer):
    """Serialize data about a registered OAuth2 application"""

    id = IDField(
        source='client_id',
        read_only=True,
        help_text='The client ID for this application (automatically generated)'
    )

    type = TypeField()

    name = ser.CharField(
        help_text='A short, descriptive name for this application',
        required=True)

    description = ser.CharField(
        help_text=
        'An optional description displayed to all users of this application',
        required=False,
        allow_blank=True)
    home_url = ser.CharField(
        help_text="The full URL to this application's homepage.",
        required=True,
        validators=[URLValidator()],
        label='Home URL')

    callback_url = ser.CharField(
        help_text=
        'The callback URL for this application (refer to OAuth documentation)',
        required=True,
        validators=[URLValidator()],
        label='Callback URL')

    owner = ser.CharField(
        help_text='The id of the user who owns this application',
        read_only=
        True,  # Don't let user register an application in someone else's name
        source='owner._id')

    date_created = DateByVersion(
        help_text=
        'The date this application was generated (automatically filled in)',
        read_only=True)

    def create(self, validated_data):
        instance = ApiOAuth2Application(**validated_data)
        instance.save()
        return instance

    def update(self, instance, validated_data):
        assert isinstance(
            instance,
            ApiOAuth2Application), 'instance must be an ApiOAuth2Application'
        for attr, value in validated_data.iteritems():
            setattr(instance, attr, value)
        instance.save()
        return instance
Exemplo n.º 20
0
class GuidSerializer(JSONAPISerializer):
    class Meta:
        type_ = 'guids'

    filterable_fields = tuple()

    id = IDField(source='_id', read_only=True)
    type = TypeField()

    referent = RelationshipField(
        related_view=get_related_view,
        related_view_kwargs=get_related_view_kwargs,
        related_meta={
            'type': 'get_type',
        },
    )
    links = LinksField({
        'self': 'get_absolute_url',
        'html': 'get_absolute_html_url',
    })

    def get_type(self, guid):
        return get_type(guid.referent)

    def get_absolute_url(self, obj):
        return absolute_reverse(
            'guids:guid-detail',
            kwargs={
                'guids':
                obj._id,
                'version':
                self.context['request'].parser_context['kwargs']['version'],
            },
        )

    def get_absolute_html_url(self, obj):
        if not isinstance(obj.referent, BaseFileNode):
            return obj.referent.absolute_url
        return urlparse.urljoin(website_settings.DOMAIN,
                                '/{}/'.format(obj._id))

    def to_representation(self, obj):
        if self.context['view'].kwargs.get('is_embedded'):
            # Force the referent to serialize instead.
            obj = obj.referent
            ser = resolve(
                reverse(
                    get_related_view(obj),
                    kwargs={
                        'node_id': obj._id,
                        'version':
                        self.context['view'].kwargs.get('version', '2')
                    },
                )).func.cls.serializer_class()
            [ser.context.update({k: v}) for k, v in self.context.iteritems()]
            return ser.to_representation(obj)
        return super(GuidSerializer, self).to_representation(obj)
Exemplo n.º 21
0
class CommentReportSerializer(JSONAPISerializer):
    id = IDField(source='_id', read_only=True)
    type = TypeField()
    category = ser.ChoiceField(
        choices=[
            ('spam', 'Spam or advertising'),
            ('hate', 'Hate speech'),
            ('violence', 'Violence or harmful behavior'),
        ],
        required=True,
    )
    message = ser.CharField(source='text', required=False, allow_blank=True)
    links = LinksField({'self': 'get_absolute_url'})

    class Meta:
        @staticmethod
        def get_type(request):
            return get_kebab_snake_case_field(request.version,
                                              'comment-reports')

    def get_absolute_url(self, obj):
        return absolute_reverse(
            'comments:report-detail',
            kwargs={
                'user_id':
                obj._id,
                'comment_id':
                self.context['request'].parser_context['kwargs']['comment_id'],
                'version':
                self.context['request'].parser_context['kwargs']['version'],
            },
        )

    def create(self, validated_data):
        user = self.context['request'].user
        comment = self.context['view'].get_comment()
        if user._id in comment.reports and not comment.reports[user._id].get(
                'retracted', True):
            raise ValidationError('Comment already reported.')
        try:
            comment.report_abuse(user, save=True, **validated_data)
        except ValueError:
            raise ValidationError('You cannot report your own comment.')
        return CommentReport(user._id, **validated_data)

    def update(self, comment_report, validated_data):
        user = self.context['request'].user
        comment = self.context['view'].get_comment()
        if user._id != comment_report._id:
            raise ValidationError(
                'You cannot report a comment on behalf of another user.')
        try:
            comment.report_abuse(user, save=True, **validated_data)
        except ValueError:
            raise ValidationError('You cannot report your own comment.')
        return CommentReport(user._id, **validated_data)
Exemplo n.º 22
0
class ApiOAuth2ApplicationDetailSerializer(ApiOAuth2ApplicationSerializer):
    """
    Overrides ApiOAuth2ApplicationSerializer to make id required.
    """

    id = IDField(
        source='client_id',
        required=True,
        help_text='The client ID for this application (automatically generated)'
    )
class DraftRegistrationContributorsCreateSerializer(NodeContributorsCreateSerializer, DraftRegistrationContributorsSerializer):
    """
    Overrides DraftRegistrationContributorsSerializer to add email, full_name, send_email, and non-required index and users field.

    id and index redefined because of the two serializers we've inherited
    """
    id = IDField(source='_id', required=False, allow_null=True)
    index = ser.IntegerField(required=False, source='_order')

    email_preferences = ['draft_registration', 'false']
Exemplo n.º 24
0
class NodeContributorsCreateSerializer(NodeContributorsSerializer):
    """
    Overrides NodeContributorsSerializer to add email, full_name, send_email, and non-required index and users field.
    """

    id = IDField(source='_id', required=False, allow_null=True)
    full_name = ser.CharField(required=False)
    email = ser.EmailField(required=False, source='user.email')
    index = ser.IntegerField(required=False, source='_order')

    users = RelationshipField(
        related_view='users:user-detail',
        related_view_kwargs={'user_id': '<user._id>'},
        required=False
    )

    email_preferences = ['default', 'preprint', 'false']

    def validate_data(self, node, user_id=None, full_name=None, email=None, index=None):
        if user_id and (full_name or email):
            raise Conflict(detail='Full name and/or email should not be included with a user ID.')
        if not user_id and not full_name:
            raise exceptions.ValidationError(detail='A user ID or full name must be provided to add a contributor.')
        if index > len(node.contributors):
            raise exceptions.ValidationError(detail='{} is not a valid contributor index for node with id {}'.format(index, node._id))

    def create(self, validated_data):
        id = validated_data.get('_id')
        email = validated_data.get('user', {}).get('email', None)
        index = None
        if '_order' in validated_data:
            index = validated_data.pop('_order')
        node = self.context['view'].get_node()
        auth = Auth(self.context['request'].user)
        full_name = validated_data.get('full_name')
        bibliographic = validated_data.get('bibliographic')
        send_email = self.context['request'].GET.get('send_email') or 'default'
        permissions = osf_permissions.expand_permissions(validated_data.get('permission')) or osf_permissions.DEFAULT_CONTRIBUTOR_PERMISSIONS

        self.validate_data(node, user_id=id, full_name=full_name, email=email, index=index)

        if send_email not in self.email_preferences:
            raise exceptions.ValidationError(detail='{} is not a valid email preference.'.format(send_email))

        try:
            contributor_obj = node.add_contributor_registered_or_not(
                auth=auth, user_id=id, email=email, full_name=full_name, send_email=send_email,
                permissions=permissions, bibliographic=bibliographic, index=index, save=True
            )
        except ValidationError as e:
            raise exceptions.ValidationError(detail=e.messages[0])
        except ValueError as e:
            raise exceptions.NotFound(detail=e.args[0])
        return contributor_obj
Exemplo n.º 25
0
class NodeContributorsSerializer(JSONAPISerializer):
    """ Separate from UserSerializer due to necessity to override almost every field as read only
    """
    non_anonymized_fields = ['bibliographic', 'permission']
    filterable_fields = frozenset(
        ['id', 'bibliographic', 'permission', 'index'])

    id = IDField(source='_id', read_only=True)
    type = TypeField()
    index = ser.IntegerField(required=False, read_only=True, source='_order')

    bibliographic = ser.BooleanField(
        help_text=
        'Whether the user will be included in citations for this node or not.',
        default=True)
    permission = ser.ChoiceField(
        choices=osf_permissions.PERMISSIONS,
        required=False,
        allow_null=True,
        default=osf_permissions.reduce_permissions(
            osf_permissions.DEFAULT_CONTRIBUTOR_PERMISSIONS),
        help_text=
        'User permission level. Must be "read", "write", or "admin". Defaults to "write".'
    )
    unregistered_contributor = ser.SerializerMethodField()

    links = LinksField({'self': 'get_absolute_url'})

    users = RelationshipField(related_view='users:user-detail',
                              related_view_kwargs={'user_id': '<user._id>'},
                              always_embed=True)

    node = RelationshipField(related_view='nodes:node-detail',
                             related_view_kwargs={'node_id': '<node._id>'})

    class Meta:
        type_ = 'contributors'

    def get_absolute_url(self, obj):
        return absolute_reverse(
            'nodes:node-contributor-detail',
            kwargs={
                'user_id':
                obj.user._id,
                'node_id':
                self.context['request'].parser_context['kwargs']['node_id'],
                'version':
                self.context['request'].parser_context['kwargs']['version']
            })

    def get_unregistered_contributor(self, obj):
        unclaimed_records = obj.user.unclaimed_records.get(obj.node._id, None)
        if unclaimed_records:
            return unclaimed_records.get('name', None)
Exemplo n.º 26
0
class RegistrationDetailSerializer(RegistrationSerializer):
    """
    Overrides RegistrationSerializer make _id required and other fields writeable
    """

    id = IDField(source='_id', required=True)

    pending_withdrawal = HideIfWithdrawal(ser.BooleanField(
        source='is_pending_retraction', required=False,
        help_text='The registration is awaiting withdrawal approval by project admins.',
    ))
    withdrawal_justification = ser.CharField(required=False)
Exemplo n.º 27
0
class WaterbutlerMetadataSerializer(ser.Serializer):
    source = ser.CharField(write_only=True)
    destination = DestinationSerializer(write_only=True)

    id = IDField(source='_id', read_only=True)
    kind = ser.CharField(read_only=True)
    name = ser.CharField(read_only=True, help_text='Display name used in the general user interface')
    created = ser.CharField(read_only=True)
    modified = ser.CharField(read_only=True)
    path = ser.CharField(read_only=True)
    checkout = ser.SerializerMethodField(read_only=True)
    version = ser.IntegerField(help_text='Latest file version', read_only=True, source='current_version_number')
    downloads = ser.SerializerMethodField()

    sha256 = ser.SerializerMethodField()
    md5 = ser.SerializerMethodField()
    size = ser.SerializerMethodField()

    def get_checkout(self, obj):
        return obj.checkout._id if obj.checkout else None

    def get_downloads(self, obj):
        return obj.get_download_count()

    def get_sha256(self, obj):
        return obj.versions.first().metadata.get('sha256', None) if obj.versions.exists() else None

    def get_md5(self, obj):
        return obj.versions.first().metadata.get('md5', None) if obj.versions.exists() else None

    def get_size(self, obj):
        if obj.versions.exists():
            self.size = obj.versions.first().size
            return self.size
        return None

    def create(self, validated_data):
        source = validated_data.pop('source')
        destination = validated_data.pop('destination')
        name = validated_data.pop('name')

        try:
            return self.context['view'].perform_file_action(source, destination, name)
        except IntegrityError:
            raise exceptions.ValidationError('File already exists with this name.')
        except file_exceptions.FileNodeCheckedOutError:
            raise exceptions.ValidationError('Cannot move file as it is checked out.')
        except file_exceptions.FileNodeIsPrimaryFile:
            raise exceptions.ValidationError('Cannot move file as it is the primary file of preprint.')

    class Meta:
        type_ = 'file_metadata'
Exemplo n.º 28
0
class UserSerializer(JSONAPISerializer):
    filterable_fields = frozenset([
        'full_name',
        'given_name',
        'middle_names',
        'family_name',
        'id'
    ])
    id = IDField(source='_id', read_only=True)
    type = TypeField()
    full_name = ser.CharField(source='fullname', required=True, label='Full name', help_text='Display name used in the general user interface')
    given_name = ser.CharField(required=False, allow_blank=True, help_text='For bibliographic citations')
    middle_names = ser.CharField(required=False, allow_blank=True, help_text='For bibliographic citations')
    family_name = ser.CharField(required=False, allow_blank=True, help_text='For bibliographic citations')
    suffix = ser.CharField(required=False, allow_blank=True, help_text='For bibliographic citations')
    date_registered = ser.DateTimeField(read_only=True)

    # Social Fields are broken out to get around DRF complex object bug and to make API updating more user friendly.
    gitHub = DevOnly(ser.CharField(required=False, label='GitHub', source='social.github', allow_blank=True, help_text='GitHub Handle'))
    scholar = DevOnly(ser.CharField(required=False, source='social.scholar', allow_blank=True, help_text='Google Scholar Account'))
    personal_website = DevOnly(ser.URLField(required=False, source='social.personal', allow_blank=True, help_text='Personal Website'))
    twitter = DevOnly(ser.CharField(required=False, source='social.twitter', allow_blank=True, help_text='Twitter Handle'))
    linkedIn = DevOnly(ser.CharField(required=False, source='social.linkedIn', allow_blank=True, help_text='LinkedIn Account'))
    impactStory = DevOnly(ser.CharField(required=False, source='social.impactStory', allow_blank=True, help_text='ImpactStory Account'))
    orcid = DevOnly(ser.CharField(required=False, label='ORCID', source='social.orcid', allow_blank=True, help_text='ORCID'))
    researcherId = DevOnly(ser.CharField(required=False, label='ResearcherID', source='social.researcherId', allow_blank=True, help_text='ResearcherId Account'))
    profile_image_url = DevOnly(ser.SerializerMethodField(required=False, read_only=True))

    def get_profile_image_url(self, user):
        size = self.context['request'].query_params.get('profile_image_size')
        return user.profile_image_url(size=size)

    links = LinksField({'html': 'absolute_url'})
    nodes = JSONAPIHyperlinkedIdentityField(view_name='users:user-nodes', lookup_field='pk', lookup_url_kwarg='user_id',
                                             link_type='related')

    class Meta:
        type_ = 'users'

    def absolute_url(self, obj):
        return obj.absolute_url

    def update(self, instance, validated_data):
        assert isinstance(instance, User), 'instance must be a User'
        for attr, value in validated_data.items():
            if 'social' == attr:
                for key, val in value.items():
                    instance.social[key] = val
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance
Exemplo n.º 29
0
class NodeAlternativeCitationSerializer(JSONAPISerializer):

    id = IDField(source='_id', read_only=True)
    type = TypeField()
    name = ser.CharField(required=True)
    text = ser.CharField(required=True)

    class Meta:
        type_ = 'citations'

    def create(self, validated_data):
        errors = self.error_checker(validated_data)
        if len(errors) > 0:
            raise exceptions.ValidationError(detail=errors)
        node = self.context['view'].get_node()
        auth = Auth(self.context['request']._user)
        citation = node.add_citation(auth, save=True, **validated_data)
        return citation

    def update(self, instance, validated_data):
        errors = self.error_checker(validated_data)
        if len(errors) > 0:
            raise exceptions.ValidationError(detail=errors)
        node = self.context['view'].get_node()
        auth = Auth(self.context['request']._user)
        instance = node.edit_citation(auth,
                                      instance,
                                      save=True,
                                      **validated_data)
        return instance

    def error_checker(self, data):
        errors = []
        name = data.get('name', None)
        text = data.get('text', None)
        citations = self.context['view'].get_node().alternative_citations
        if not (self.instance and self.instance.name
                == name) and citations.find(Q('name', 'eq', name)).count() > 0:
            errors.append(
                "There is already a citation named '{}'".format(name))
        if not (self.instance and self.instance.text == text):
            matching_citations = citations.find(Q('text', 'eq', text))
            if matching_citations.count() > 0:
                names = "', '".join(
                    [str(citation.name) for citation in matching_citations])
                errors.append("Citation matches '{}'".format(names))
        return errors

    def get_absolute_url(self, obj):
        #  Citations don't have urls
        raise NotImplementedError
Exemplo n.º 30
0
class NodeLinksSerializer(JSONAPISerializer):

    id = IDField(source='_id', read_only=True)
    type = TypeField()
    target_node_id = ser.CharField(
        write_only=True,
        source='node._id',
        help_text='The ID of the node that this Node Link points to')

    # TODO: We don't show the title because the current user may not have access to this node. We may want to conditionally
    # include this field in the future.
    # title = ser.CharField(read_only=True, source='node.title', help_text='The title of the node that this Node Link '
    #                                                                      'points to')

    target_node = JSONAPIHyperlinkedIdentityField(
        view_name='nodes:node-detail',
        lookup_field='pk',
        link_type='related',
        lookup_url_kwarg='node_id')

    class Meta:
        type_ = 'node_links'

    links = LinksField({'self': 'get_absolute_url'})

    def get_absolute_url(self, obj):
        node_id = self.context['request'].parser_context['kwargs']['node_id']
        return absolute_reverse('nodes:node-pointer-detail',
                                kwargs={
                                    'node_id': node_id,
                                    'node_link_id': obj._id
                                })

    def create(self, validated_data):
        request = self.context['request']
        user = request.user
        auth = Auth(user)
        node = self.context['view'].get_node()
        pointer_node = Node.load(validated_data['node']['_id'])
        if not pointer_node:
            raise exceptions.NotFound('Node not found.')
        try:
            pointer = node.add_pointer(pointer_node, auth, save=True)
            return pointer
        except ValueError:
            raise exceptions.ValidationError(
                'Node link to node {} already in list'.format(
                    pointer_node._id))

    def update(self, instance, validated_data):
        pass