Esempio n. 1
0
    def post(self, request):
        form = FeedForm.create_from_request(request)

        if not form.is_valid():
            raise ValidationException(request, form)

        if not has_object_permission('check_catalog_manage', request.user,
                                     form.cleaned_data['catalog_id']):
            raise ProblemDetailException(request,
                                         _("Insufficient permissions"),
                                         status=HTTPStatus.FORBIDDEN)

        if Feed.objects.filter(
                catalog=form.cleaned_data['catalog_id'],
                url_name=form.cleaned_data['url_name']).exists():
            raise ProblemDetailException(
                request,
                _("Feed with same url_name already exists in same catalog"),
                status=HTTPStatus.CONFLICT)

        feed = Feed(creator=request.user)
        form.populate(feed)
        feed.save()

        if 'entries' in form.cleaned_data.keys():
            feed.entries.add(*form.cleaned_data['entries'])

        if 'parents' in form.cleaned_data.keys():
            feed.parents.add(*form.cleaned_data['parents'])

        return SingleResponse(request,
                              feed,
                              serializer=FeedSerializer.Base,
                              status=HTTPStatus.CREATED)
Esempio n. 2
0
    def post(self, request):
        form = CatalogForm.create_from_request(request)

        if not request.user.has_perm('core.add_catalog'):
            raise ProblemDetailException(request,
                                         _("Insufficient permissions"),
                                         status=HTTPStatus.FORBIDDEN)

        if not form.is_valid():
            raise ValidationException(request, form)

        if Catalog.objects.filter(
                url_name=form.cleaned_data['url_name']).exists():
            raise ProblemDetailException(
                request,
                title=_('Catalog url_name already taken'),
                status=HTTPStatus.CONFLICT)

        service = CatalogService()
        catalog = service.populate(catalog=Catalog(creator=request.user),
                                   form=form)

        if not catalog.users.contains(request.user):
            UserCatalog.objects.create(catalog=catalog,
                                       user=request.user,
                                       mode=UserCatalog.Mode.MANAGE)

        return SingleResponse(request,
                              catalog,
                              serializer=CatalogSerializer.Detailed,
                              status=HTTPStatus.CREATED)
Esempio n. 3
0
    def post(self, request):
        form = CreateAuthorForm.create_from_request(request)

        if not form.is_valid():
            raise ValidationException(request, form)

        if not has_object_permission('check_catalog_write', request.user,
                                     form.cleaned_data['catalog_id']):
            raise ProblemDetailException(request,
                                         _("Insufficient permissions"),
                                         status=HTTPStatus.FORBIDDEN)

        if Author.objects.filter(
                catalog=form.cleaned_data['catalog_id'],
                name=form.cleaned_data['name'],
                surname=form.cleaned_data['surname']).exists():
            raise ProblemDetailException(
                request,
                _("Author already exists in the catalog"),
                status=HTTPStatus.CONFLICT)

        author = Author()
        form.populate(author)
        author.save()

        return SingleResponse(request,
                              author,
                              serializer=AuthorSerializer.Detailed,
                              status=HTTPStatus.CREATED)
Esempio n. 4
0
    def post(self, request):
        form = CreateUserForm.create_from_request(request)

        if not form.is_valid():
            raise ValidationException(request, form)

        if not request.user.has_perm('core.add_user'):
            raise ProblemDetailException(request,
                                         _("Insufficient permissions"),
                                         status=HTTPStatus.FORBIDDEN)

        if User.objects.filter(email=form.cleaned_data['email']).exists():
            raise ProblemDetailException(
                request,
                _("User with same email already exists"),
                status=HTTPStatus.CONFLICT)

        user = User()
        form.populate(user)
        user.set_password(form.cleaned_data['password'])
        user.save()

        return SingleResponse(request,
                              user,
                              serializer=UserSerializer.Base,
                              status=HTTPStatus.CREATED)
Esempio n. 5
0
    def _get_entry(request, catalog_id: uuid.UUID, entry_id: uuid.UUID, checker: str = 'check_entry_manage') -> Entry:
        try:
            entry = Entry.objects.get(pk=entry_id, catalog_id=catalog_id)
        except Entry.DoesNotExist:
            raise ProblemDetailException(request, _("Entry not found"), status=HTTPStatus.NOT_FOUND)

        if not has_object_permission(checker, request.user, entry):
            raise ProblemDetailException(request, _("Insufficient permissions"), status=HTTPStatus.FORBIDDEN)

        return entry
Esempio n. 6
0
    def post(self, request):
        form = RefreshTokenForm.create_from_request(request)

        if not form.is_valid():
            raise ValidationException(request, form)

        try:
            claims = JWTFactory.decode(form.cleaned_data['refresh'])
        except JoseError as e:
            raise ProblemDetailException(request,
                                         _('Invalid token.'),
                                         status=HTTPStatus.UNAUTHORIZED,
                                         previous=e)

        redis = Redis(host=settings.REDIS_HOST,
                      port=settings.REDIS_PORT,
                      db=settings.REDIS_DATABASE)

        if not redis.exists(f"refresh_token:{claims['jti']}"):
            raise UnauthorizedException(request)

        access_token = JWTFactory(claims['sub']).access()

        return SingleResponse(request, {'access_token': access_token},
                              status=HTTPStatus.OK)
Esempio n. 7
0
    def get(self, request, entry_id: uuid.UUID):
        try:
            entry = Entry.objects.get(pk=entry_id, image__isnull=False)
        except Entry.DoesNotExist:
            raise ProblemDetailException(request, _("Entry image not found"), status=HTTPStatus.NOT_FOUND)

        return FileResponse(entry.image, as_attachment=True, filename=entry.title)
Esempio n. 8
0
    def put(self, request, feed_id: UUID):
        feed = self._get_feed(request, feed_id)

        form = FeedForm.create_from_request(request)
        form['parents'].queryset = form['parents'].queryset.exclude(pk=feed.pk)

        if not form.is_valid():
            raise ValidationException(request, form)

        if Feed.objects.filter(catalog=form.cleaned_data['catalog_id'],
                               url_name=form.cleaned_data['url_name']).exclude(
                                   pk=feed.id).exists():
            raise ProblemDetailException(
                request,
                _("Feed with same url_name already exists in same catalog"),
                status=HTTPStatus.CONFLICT)

        form.populate(feed)
        feed.save()

        if feed.kind == Feed.FeedKind.ACQUISITION and 'entries' in form.cleaned_data.keys(
        ):
            feed.entries.clear()
            feed.entries.add(*form.cleaned_data['entries'])

        if feed.kind == Feed.FeedKind.NAVIGATION and 'parents' in form.cleaned_data.keys(
        ):
            feed.parents.clear()
            feed.parents.add(*form.cleaned_data['parents'])

        return SingleResponse(request, feed, serializer=FeedSerializer.Base)
Esempio n. 9
0
    def _get_feed(request, feed_id: UUID) -> Feed:
        try:
            feed = Feed.objects.select_related('catalog').get(pk=feed_id)
        except Feed.DoesNotExist as e:
            raise ProblemDetailException(request,
                                         _("Feed not found"),
                                         status=HTTPStatus.NOT_FOUND,
                                         previous=e)

        if not has_object_permission('check_catalog_manage', request.user,
                                     feed.catalog):
            raise ProblemDetailException(request,
                                         _("Insufficient permissions"),
                                         status=HTTPStatus.FORBIDDEN)

        return feed
Esempio n. 10
0
    def dispatch(self, request, *args, **kwargs):
        try:
            self.catalog = Catalog.objects.get(url_name=kwargs['catalog_name'])
        except Catalog.DoesNotExist:
            raise ProblemDetailException(request,
                                         _("Catalog not found"),
                                         status=HTTPStatus.NOT_FOUND)
        except KeyError as e:
            raise ProblemDetailException(request,
                                         _("Internal server error"),
                                         status=HTTPStatus.NOT_FOUND,
                                         previous=e)

        if request.method not in self.UNSECURED_METHODS and not self.catalog.is_public:
            self._authenticate(request)

        return View.dispatch(self, request, *args, **kwargs)
Esempio n. 11
0
    def delete(self, request, api_key_id: UUID):
        try:
            api_key = ApiKey.objects.get(pk=api_key_id)
        except ApiKey.DoesNotExist as e:
            raise ProblemDetailException(request,
                                         _("ApiKey not found"),
                                         status=HTTPStatus.NOT_FOUND,
                                         previous=e)

        if not request.user.is_superuser and api_key.user_id != request.user.id:
            raise ProblemDetailException(request,
                                         _("ApiKey not found"),
                                         status=HTTPStatus.NOT_FOUND)

        api_key.hard_delete()

        return SingleResponse(request, status=HTTPStatus.NO_CONTENT)
Esempio n. 12
0
    def _get_author(request,
                    author_id: UUID,
                    checker: str = 'check_catalog_manage') -> Author:
        try:
            author = Author.objects.select_related('catalog').get(pk=author_id)
        except Author.DoesNotExist as e:
            raise ProblemDetailException(request,
                                         _("Author not found"),
                                         status=HTTPStatus.NOT_FOUND,
                                         previous=e)

        if not has_object_permission(checker, request.user, author.catalog):
            raise ProblemDetailException(request,
                                         _("Insufficient permissions"),
                                         status=HTTPStatus.FORBIDDEN)

        return author
Esempio n. 13
0
    def _get_catalog(request,
                     catalog_id: UUID,
                     checker: str = 'check_catalog_manage') -> Catalog:
        try:
            catalog = Catalog.objects.get(pk=catalog_id)
        except Catalog.DoesNotExist as e:
            raise ProblemDetailException(request,
                                         _("Catalog not found"),
                                         status=HTTPStatus.NOT_FOUND,
                                         previous=e)

        if not has_object_permission(checker, request.user, catalog):
            raise ProblemDetailException(request,
                                         _("Insufficient permissions"),
                                         status=HTTPStatus.FORBIDDEN)

        return catalog
Esempio n. 14
0
    def _get_acquisition(request,
                         acquisition_id: UUID,
                         checker: str = 'check_catalog_manage') -> Acquisition:
        try:
            acquisition = Acquisition.objects.select_related(
                'entry__catalog').get(pk=acquisition_id)
        except Acquisition.DoesNotExist:
            raise ProblemDetailException(request,
                                         _("Acquisition not found"),
                                         status=HTTPStatus.NOT_FOUND)

        if not has_object_permission(checker, request.user,
                                     acquisition.entry.catalog):
            raise ProblemDetailException(request,
                                         _("Insufficient permissions"),
                                         status=HTTPStatus.FORBIDDEN)

        return acquisition
Esempio n. 15
0
    def _get_user(request,
                  user_id: UUID,
                  perm_test: Optional[Callable] = None) -> User:
        try:
            user = User.objects.get(pk=user_id)
        except User.DoesNotExist as e:
            raise ProblemDetailException(request,
                                         _("User not found"),
                                         status=HTTPStatus.NOT_FOUND,
                                         previous=e)

        if not perm_test:
            perm_test = lambda: True

        if not (user.id == request.user.id or perm_test()):
            raise ProblemDetailException(request,
                                         _("Insufficient permissions"),
                                         status=HTTPStatus.FORBIDDEN)

        return user
Esempio n. 16
0
    def get(self, request, catalog_name: str, feed_name: str = None):
        try:
            catalog = Catalog.objects.get(url_name=catalog_name)
        except Catalog.DoesNotExist as e:
            raise ProblemDetailException(request,
                                         _("Feed not found"),
                                         status=HTTPStatus.NOT_FOUND,
                                         previous=e)

        tags = {
            'short_name':
            catalog.title,
            'description':
            'OPDS catalog',
            'filter':
            EntryFilter,
            'url':
            f"{settings.BASE_URL}"
            f"{reverse('root', kwargs={'catalog_name': catalog.url_name})}"
            f"?{EntryFilter.template()}"
        }

        if feed_name:
            try:
                feed = Feed.objects.get(catalog=catalog, url_name=feed_name)
            except Feed.DoesNotExist as e:
                raise ProblemDetailException(request,
                                             _("Feed not found"),
                                             status=HTTPStatus.NOT_FOUND,
                                             previous=e)

            tags['short_name'] = f"{feed.title} - {catalog.title}"
            tags['description'] = f"{feed.title} feed"
            tags['url'] = f"{settings.BASE_URL}" \
                          f"{reverse('feed', kwargs={'catalog_name': catalog.url_name, 'feed_name': feed.url_name})}" \
                          f"?{EntryFilter.template()}"

        return render(request,
                      'opds/search.xml',
                      tags,
                      content_type='application/opensearchdescription+xml')
Esempio n. 17
0
    def post(self, request, catalog_id: uuid.UUID):
        try:
            catalog = Catalog.objects.get(pk=catalog_id)
        except Catalog.DoesNotExist as e:
            raise ProblemDetailException(request, _("Catalog not found"), status=HTTPStatus.NOT_FOUND, previous=e)

        if not has_object_permission('check_catalog_write', request.user, catalog):
            raise ProblemDetailException(request, _("Insufficient permissions"), status=HTTPStatus.FORBIDDEN)

        form = EntryForm.create_from_request(request)
        form.fields['category_ids'].queryset = form.fields['category_ids'].queryset.filter(catalog=catalog)
        form.fields['author_id'].queryset = form.fields['author_id'].queryset.filter(catalog=catalog)

        if not form.is_valid():
            raise ValidationException(request, form)

        entry = Entry(creator=request.user, catalog=catalog)
        service = EntryService(catalog, request.user)
        service.populate(entry, form)

        return SingleResponse(request, entry, serializer=EntrySerializer.Detailed, status=HTTPStatus.CREATED)
Esempio n. 18
0
    def authenticate(self, request, bearer=None, **kwargs):
        try:
            claims = JWTFactory.decode(bearer)
        except JoseError as e:
            raise ProblemDetailException(request,
                                         _('Invalid token.'),
                                         status=HTTPStatus.UNAUTHORIZED,
                                         previous=e)

        if claims['type'] == 'api_key':
            try:
                api_key = ApiKey.objects.get(pk=claims['jti'], is_active=True)
            except (ApiKey.DoesNotExist, ValidationError):
                raise ProblemDetailException(request,
                                             _('Invalid api key.'),
                                             status=HTTPStatus.UNAUTHORIZED)

            api_key.last_seen_at = timezone.now()
            api_key.save()
            setattr(request, 'api_key', api_key)
            user = api_key.user
        elif claims['type'] == 'access':
            try:
                user = User.objects.get(pk=claims['sub'])
            except User.DoesNotExist:
                raise ProblemDetailException(request,
                                             _('Inactive user.'),
                                             status=HTTPStatus.FORBIDDEN)
        else:
            raise ProblemDetailException(request,
                                         _('Invalid token'),
                                         status=HTTPStatus.UNAUTHORIZED)

        if not self.user_can_authenticate(user):
            raise ProblemDetailException(request,
                                         _('Inactive user.'),
                                         status=HTTPStatus.FORBIDDEN)

        return user
Esempio n. 19
0
    def __init__(
        self, request, qs, ordering: Ordering = None, **kwargs
    ):
        kwargs.setdefault('content_type', 'application/json')

        # Ordering
        ordering = ordering if ordering else Ordering.create_from_request(request)
        qs = qs.order_by(*ordering.columns)

        # Pagination
        paginate = request.GET.get('paginate', 'true') == 'true'
        if paginate:
            limit = int(request.GET.get('limit', settings.PAGINATION['DEFAULT_LIMIT']))
            page = int(request.GET.get('page', 1))

            paginator = Paginator(qs, limit)

            try:
                paginator.validate_number(page)
            except EmptyPage as e:
                raise ProblemDetailException(
                    request,
                    title=_('Page not found'),
                    status=HTTPStatus.NOT_FOUND,
                    previous=e,
                    detail_type='out_of_range',
                    detail=_('That page contains no results')
                )

            items = paginator.get_page(page)
            num_pages = paginator.num_pages
            total = paginator.count
        else:
            limit = None
            page = 1
            items = qs
            num_pages = 1
            total = qs.count()

        data = {
            'items': items,
            'metadata': {
                'page': page,
                'limit': limit,
                'pages': num_pages,
                'total': total
            }
        }

        super().__init__(request, data, **kwargs)
Esempio n. 20
0
    def authenticate(self, request, basic=None, **kwargs):
        auth_params = base64.b64decode(basic).decode().split(':')

        # TODO: use auth sources

        user = super().authenticate(request,
                                    username=auth_params[0],
                                    password=auth_params[1])
        if not user:
            raise ProblemDetailException(
                request,
                _('Invalid credentials'),
                status=HTTPStatus.UNAUTHORIZED,
                extra_headers=(
                    ('WWW-Authenticate',
                     f'Bearer realm="{slugify(settings.INSTANCE_NAME)}"'), ))
        return user
Esempio n. 21
0
    def get(self, request, catalog_name: str, entry_id: UUID):
        try:
            entry = Entry.objects.get(pk=entry_id,
                                      catalog__url_name=catalog_name)
        except Entry.DoesNotExist as e:
            raise ProblemDetailException(request,
                                         _("Entry not found"),
                                         status=HTTPStatus.NOT_FOUND,
                                         previous=e)

        return render(
            request,
            "opds/_partials/entry.xml", {
                'entry': entry,
                'is_complete': True
            },
            content_type='application/atom+xml;type=entry;profile=opds-catalog'
        )
Esempio n. 22
0
    def get(self, request, catalog_name: str, feed_name: str):
        try:
            feed = Feed.objects.get(catalog=self.catalog, url_name=feed_name)
        except Feed.DoesNotExist:
            raise ProblemDetailException(request, _("Feed not found"), status=HTTPStatus.NOT_FOUND)

        entry_filter = EntryFilter(request.GET, queryset=Entry.objects.filter(feeds=feed), request=request)

        try:
            updated_at = entry_filter.qs.latest('updated_at').updated_at
        except Entry.DoesNotExist:
            updated_at = self.catalog.updated_at

        return render(request, 'opds/feeds/feed.xml', {
            'feed': feed,
            'updated_at': updated_at,
            'catalog': self.catalog,
            'entry_filter': entry_filter
        }, content_type=f'application/atom+xml;profile=opds-catalog;kind={feed.kind}')
Esempio n. 23
0
    def post(self, request):
        form = ApiKeyForm.create_from_request(request)

        if not form.is_valid():
            raise ValidationException(request, form)

        if 'user_id' in form.cleaned_data.keys(
        ) and not request.user.is_superuser:
            raise ProblemDetailException(request,
                                         _("Insufficient permissions"),
                                         status=HTTPStatus.FORBIDDEN)

        api_key = ApiKey(user=request.user)
        form.populate(api_key)
        api_key.save()

        return SingleResponse(request,
                              api_key,
                              serializer=ApiKeySerializer.Base,
                              status=HTTPStatus.CREATED)
Esempio n. 24
0
    def get(self, request, acquisition_id: uuid.UUID):
        try:
            acquisition = Acquisition.objects.get(pk=acquisition_id)
        except Acquisition.DoesNotExist:
            raise ProblemDetailException(request, _("Acquisition not found"), status=HTTPStatus.NOT_FOUND)

        if acquisition.relation != Acquisition.AcquisitionType.ACQUISITION.OPEN_ACCESS:
            self._authenticate(request)

        redis = Redis(
            host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=settings.REDIS_DATABASE
        )

        if request.user.is_anonymous:
            user_id = uuid.uuid4()
        else:
            user_id = request.user.pk

        redis.pfadd(f"popularity:{acquisition.entry_id}", str(user_id))

        return FileResponse(acquisition.content, as_attachment=True, filename=acquisition.entry.title)
Esempio n. 25
0
    def put(self, request, catalog_id: UUID):
        form = CatalogForm.create_from_request(request)

        if not form.is_valid():
            raise ValidationException(request, form)

        catalog = self._get_catalog(request, catalog_id)

        if Catalog.objects.exclude(pk=catalog.pk).filter(
                url_name=form.cleaned_data['url_name']).exists():
            raise ProblemDetailException(
                request,
                title=_('Catalog url_name already taken'),
                status=HTTPStatus.CONFLICT)

        service = CatalogService()
        catalog = service.populate(catalog=catalog, form=form)

        return SingleResponse(request,
                              catalog,
                              serializer=CatalogSerializer.Detailed)
Esempio n. 26
0
    def post(self, request, catalog_id: uuid.UUID, entry_id: uuid.UUID):
        entry = self._get_entry(request, catalog_id, entry_id)

        try:
            metadata = json.loads(request.POST.get('metadata', '{}'))
        except json.JSONDecodeError as e:
            raise ProblemDetailException(
                request,
                title=_('Unable to parse metadata for request file'),
                status=HTTPStatus.BAD_REQUEST,
                previous=e
            )

        form = AcquisitionMetaForm(metadata, request)

        if not form.is_valid():
            raise ValidationException(request, form)

        acquisition = Acquisition(
            entry=entry,
            relation=form.cleaned_data.get('relation', Acquisition.AcquisitionType.ACQUISITION),
            mime=request.FILES['content'].content_type
        )

        if 'content' in request.FILES.keys():
            acquisition.content.save(
                f"{uuid.uuid4()}{mimetypes.guess_extension(acquisition.mime)}",
                request.FILES['content']
            )

        for price in form.cleaned_data.get('prices', []):
            Price.objects.create(
                acquisition=acquisition,
                currency=price['currency_code'],
                value=price['value']
            )

        return SingleResponse(
            request, acquisition, serializer=AcquisitionSerializer.Detailed, status=HTTPStatus.CREATED
        )
Esempio n. 27
0
    def put(self, request, author_id: UUID):
        form = CreateAuthorForm.create_from_request(request)
        author = self._get_author(request, author_id)

        if not form.is_valid():
            raise ValidationException(request, form)

        if Author.objects.exclude(pk=author_id).filter(
                catalog=form.cleaned_data['catalog_id'],
                name=form.cleaned_data['name'],
                surname=form.cleaned_data['surname'],
        ).exists():
            raise ProblemDetailException(
                request,
                _("Author already exists in the catalog"),
                status=HTTPStatus.CONFLICT)

        form.populate(author)
        author.save()

        return SingleResponse(request,
                              author,
                              serializer=AuthorSerializer.Detailed)