Esempio n. 1
0
async def verify_contact_method(request, ctx: AppConfig,
                                session: AuthnSession):
    try:
        user_profile_id = UUID(request.match_info['user_profile_id'])
        contact_method_id = UUID(request.match_info['contact_method_id'])
    except ValueError:
        raise HTTPNotFound()

    challenge_response = AuthnChallengeResponseRequest.unmarshal_request(
        await request.json())

    if user_profile_id != session.user_profile_id:
        raise HTTPForbidden()

    if challenge_response.challenge_id != session.pending_challenge.challenge_id:
        raise HTTPBadRequest()

    session.pending_challenge.attempts += 1
    session.changed()
    if session.pending_challenge.attempts > security.MaxVerificationChallengeAttempts:
        session.invalidate()
        raise HTTPForbidden(body="Too many invalid attempts")

    if challenge_response.passcode != session.pending_challenge.secret:
        raise HTTPUnauthorized(body="Incorrect passcode")

    async with op.session(ctx) as ss:
        await op.user_profile.\
            mark_contact_method_verified(contact_method_id, user_profile_id=user_profile_id).\
            execute(ss)

    session.clear_pending_challenge()
    return HTTPOk()
Esempio n. 2
0
    async def list_distributions(self, request):
        """
        The handler for an HTML listing all distributions

        Args:
            request (:class:`aiohttp.web.request`): The request from the client.

        Returns:
            :class:`aiohttp.web.HTTPOk`: The response back to the client.
        """
        self._reset_db_connection()

        def get_base_paths_blocking():
            if self.distribution_model is None:
                base_paths = list(
                    Distribution.objects.values_list("base_path", flat=True))
            else:
                base_paths = list(
                    self.distribution_model.objects.values_list("base_path",
                                                                flat=True))
            return base_paths

        base_paths = await sync_to_async(get_base_paths_blocking)()
        directory_list = ["{}/".format(path) for path in base_paths]
        return HTTPOk(headers={"Content-Type": "text/html"},
                      body=self.render_html(directory_list))
Esempio n. 3
0
    def _add_acl(self, user_identity_id: str, shared_directory_basename: str):
        """
        Attempt to add acl for the given user id and directory
        """
        try:
            resp = self.globus_transfer_client.add_endpoint_acl_rule(
                self.endpoint_id,
                dict(DATA_TYPE="access", principal=user_identity_id,
                     principal_type='identity', path=shared_directory_basename, permissions='rw'),
            )

            response = {'success': True, 'principal': user_identity_id,
                        'path': shared_directory_basename,
                        'permissions': 'rw'}

            logging.info(response)
            logging.info('Shared %s with %s\n' % (shared_directory_basename, user_identity_id))

            logging.info(response)
            return response

        except globus_sdk.TransferAPIError as error:
            response = {'success': False, 'error_type': 'TransferAPIError', 'error': error.message,
                        'error_code': error.code,
                        'shared_directory_basename': shared_directory_basename}
            logging.error(response)
            if error.code == "Exists":
                raise HTTPOk(text=json.dumps(response), content_type='application/json')

        raise HTTPInternalServerError(text=json.dumps(response), content_type='application/json')
 async def get_application(self):
     test_app = Application()
     test_app.middlewares.append(logger_middleware)
     test_app['DISTRIBUTOR_CLIENT'] = TestClient(HTTPOk())
     test_app['DISTRIBUTOR_DB'] = prepare_connection()
     cursor: sqlite3.Cursor = test_app['DISTRIBUTOR_DB']
     with open('asynctransaction/data/model/transaction.sql') as prepare_db:
         prepare_sql = prepare_db.read()
         cursor.executescript(str(prepare_sql))
     with open('asynctransaction/data/model/test_data.sql') as prepare_db:
         prepare_sql = prepare_db.read()
         cursor.executescript(str(prepare_sql))
     apply_routes(test_app)
     return test_app
Esempio n. 5
0
    async def list_distributions(self, request):
        """
        The handler for an HTML listing all distributions

        Args:
            request (:class:`aiohttp.web.request`): The request from the client.

        Returns:
            :class:`aiohttp.web.HTTPOk`: The response back to the client.
        """
        if self.distribution_model is None:
            distributions = BaseDistribution.objects.only("base_path").all()
        else:
            distributions = self.distribution_model.objects.only("base_path").all()
        directory_list = ['{}/'.format(d.base_path) for d in distributions]
        return HTTPOk(headers={"Content-Type": "text/html"}, body=self.render_html(directory_list))
Esempio n. 6
0
async def verify_authn_challenge(request, ctx: AppConfig,
                                 session: AuthnSession):
    if not session.pending_challenge:
        raise HTTPBadRequest()

    challenge_response =\
        AuthnChallengeResponseRequest.unmarshal_request(await request.json())

    if challenge_response.challenge_id != session.pending_challenge.challenge_id:
        raise ValueError("invalid challenge")

    session.pending_challenge.attempts += 1
    session.changed()
    if session.pending_challenge.attempts > security.MaxVerificationChallengeAttempts:
        session.invalidate()
        raise HTTPForbidden(text="Too many invalid attempts")

    otp_types = [AuthnChallengeType.Email, AuthnChallengeType.SMS]
    if session.pending_challenge.challenge_type in otp_types:
        if challenge_response.passcode != session.pending_challenge.secret:
            raise HTTPUnauthorized(text="Incorrect passcode")
    elif session.pending_challenge.challenge_type is AuthnChallengeType.Password:
        async with op.session(ctx) as ss:
            result = await op.authn.\
                authenticate_user(
                    user_profile_id=session.user_profile_id,
                    password=challenge_response.passcode).\
                execute(ss)
            if not result:
                raise HTTPUnauthorized(text="Incorrect passcode")
    else:
        raise HTTPBadRequest()

    session.clear_pending_challenge()
    async with op.session(ctx) as ss:
        u = await op.user_profile.\
            get_user_profile(user_profile_id=session.user_profile_id).\
            execute(ss)
    if session.required_challenges:
        session.next_challenge_for_user(u)
        return HTTPAccepted(), session.pending_challenge.get_view('public')

    session.authenticated = True
    session.set_role(u.role)
    session.remove_capabilities(Capability.Authenticate)
    return HTTPOk()
Esempio n. 7
0
    def _add_acl(self, user_identity_id: str, shared_directory_basename: str):
        """
        Attempt to add acl for the given user id and directory
        """
        try:
            resp = self.globus_transfer_client.add_endpoint_acl_rule(
                self.endpoint_id,
                dict(
                    DATA_TYPE="access",
                    principal=user_identity_id,
                    principal_type="identity",
                    path=shared_directory_basename,
                    permissions="rw",
                ),
            )

            response = {
                "success": True,
                "principal": user_identity_id,
                "path": shared_directory_basename,
                "permissions": "rw",
            }

            logging.info(response)
            logging.info("Shared %s with %s\n" %
                         (shared_directory_basename, user_identity_id))

            logging.info(response)
            return response

        except globus_sdk.TransferAPIError as error:
            response = {
                "success": False,
                "error_type": "TransferAPIError",
                "error": error.message,
                "error_code": error.code,
                "shared_directory_basename": shared_directory_basename,
            }
            logging.error(response)
            if error.code == "Exists":
                raise HTTPOk(text=json.dumps(response),
                             content_type="application/json")

        raise HTTPInternalServerError(text=json.dumps(response),
                                      content_type="application/json")
Esempio n. 8
0
async def init_authn_session(request, ctx: AppConfig, session: AuthnSession):
    sr = InitiateAuthnSessionRequest.unmarshal_request(await request.json())

    async with op.session(ctx) as ss:
        if sr.principal_type is AuthnPrincipalType.Email:
            u = await op.user_profile.get_user_profile(
                email_address=sr.principal_name).execute(ss)
            session.require_challenge(AuthnChallengeType.Email)
        elif sr.principal_type is AuthnPrincipalType.Phone:
            u = await op.user_profile.get_user_profile(
                phone_number=sr.principal_name).execute(ss)
            session.require_challenge(AuthnChallengeType.SMS)
        else:
            raise ValueError(sr.principal_type)

    if u is None:
        raise HTTPUnauthorized()

    session.user_profile_id = u.user_profile_id
    if u.role in {UserRole.Superuser, UserRole.Manager, UserRole.Creator}:
        session.require_challenge(AuthnChallengeType.Password)

    session.add_capabilities(Capability.Authenticate)
    return HTTPOk()
Esempio n. 9
0
    async def _match_and_stream(self, path, request):
        """
        Match the path and stream results either from the filesystem or by downloading new data.

        After deciding the client can access the distribution at ``path``, this function calls
        :meth:`BaseDistribution.content_handler`. If that function returns a not-None result,
        it is returned to the client.

        Then the publication linked to the Distribution is used to determine what content should
        be served. If ``path`` is a directory entry (i.e. not a file), the directory contents
        are served to the client. This method calls
        :meth:`BaseDistribution.content_handler_list_directory` to acquire any additional entries
        the Distribution's content_handler might serve in that directory. If there is an actifact
        to be served, it is served to the client.

        If there's no publication, the above paragraph is applied to the lastest repository linked
        to the matched Distribution.

        Finally, when nothing is served to client yet, we check if there is a remote for the
        Distribution. If so, the artifact is pulled from the remote and streamed to the client.

        Args:
            path (str): The path component of the URL.
            request(:class:`~aiohttp.web.Request`): The request to prepare a response for.

        Raises:
            PathNotResolved: The path could not be matched to a published file.
            PermissionError: When not permitted.

        Returns:
            :class:`aiohttp.web.StreamResponse` or :class:`aiohttp.web.FileResponse`: The response
                streamed back to the client.
        """
        distro = self._match_distribution(path)
        self._permit(request, distro)

        rel_path = path.lstrip("/")
        rel_path = rel_path[len(distro.base_path) :]
        rel_path = rel_path.lstrip("/")

        content_handler_result = distro.content_handler(rel_path)
        if content_handler_result is not None:
            return content_handler_result

        headers = self.response_headers(rel_path)

        publication = getattr(distro, "publication", None)

        if publication:
            if rel_path == "" or rel_path[-1] == "/":
                try:
                    index_path = "{}index.html".format(rel_path)
                    publication.published_artifact.get(relative_path=index_path)
                    rel_path = index_path
                    headers = self.response_headers(rel_path)
                except ObjectDoesNotExist:
                    dir_list = await self.list_directory(None, publication, rel_path)
                    dir_list.update(distro.content_handler_list_directory(rel_path))
                    return HTTPOk(
                        headers={"Content-Type": "text/html"}, body=self.render_html(dir_list)
                    )

            # published artifact
            try:
                pa = publication.published_artifact.get(relative_path=rel_path)
                ca = pa.content_artifact
            except ObjectDoesNotExist:
                pass
            else:
                if ca.artifact:
                    return self._serve_content_artifact(ca, headers)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca
                    )

            # pass-through
            if publication.pass_through:
                try:
                    ca = ContentArtifact.objects.get(
                        content__in=publication.repository_version.content, relative_path=rel_path
                    )
                except MultipleObjectsReturned:
                    log.error(
                        _("Multiple (pass-through) matches for {b}/{p}"),
                        {"b": distro.base_path, "p": rel_path},
                    )
                    raise
                except ObjectDoesNotExist:
                    pass
                else:
                    if ca.artifact:
                        return self._serve_content_artifact(ca, headers)
                    else:
                        return await self._stream_content_artifact(
                            request, StreamResponse(headers=headers), ca
                        )

        repo_version = getattr(distro, "repository_version", None)
        repository = getattr(distro, "repository", None)

        if repository or repo_version:
            if repository:
                repo_version = distro.repository.latest_version()

            if rel_path == "" or rel_path[-1] == "/":
                try:
                    index_path = "{}index.html".format(rel_path)
                    ContentArtifact.objects.get(
                        content__in=repo_version.content, relative_path=index_path
                    )
                    rel_path = index_path
                except ObjectDoesNotExist:
                    dir_list = await self.list_directory(repo_version, None, rel_path)
                    dir_list.update(distro.content_handler_list_directory(rel_path))
                    return HTTPOk(
                        headers={"Content-Type": "text/html"}, body=self.render_html(dir_list)
                    )

            try:
                ca = ContentArtifact.objects.get(
                    content__in=repo_version.content, relative_path=rel_path
                )
            except MultipleObjectsReturned:
                log.error(
                    _("Multiple (pass-through) matches for {b}/{p}"),
                    {"b": distro.base_path, "p": rel_path},
                )
                raise
            except ObjectDoesNotExist:
                pass
            else:
                if ca.artifact:
                    return self._serve_content_artifact(ca, headers)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca
                    )

        if distro.remote:
            remote = distro.remote.cast()
            try:
                url = remote.get_remote_artifact_url(rel_path)
                ra = RemoteArtifact.objects.get(remote=remote, url=url)
                ca = ra.content_artifact
                if ca.artifact:
                    return self._serve_content_artifact(ca, headers)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca
                    )
            except ObjectDoesNotExist:
                ca = ContentArtifact(relative_path=rel_path)
                ra = RemoteArtifact(remote=remote, url=url, content_artifact=ca)
                return await self._stream_remote_artifact(
                    request, StreamResponse(headers=headers), ra
                )

        raise PathNotResolved(path)
Esempio n. 10
0
    async def _match_and_stream(self, path, request):
        """
        Match the path and stream results either from the filesystem or by downloading new data.

        After deciding the client can access the distribution at ``path``, this function calls
        :meth:`Distribution.content_handler`. If that function returns a not-None result, it is
        returned to the client.

        Then the publication linked to the Distribution is used to determine what content should
        be served. If ``path`` is a directory entry (i.e. not a file), the directory contents
        are served to the client. This method calls
        :meth:`Distribution.content_handler_list_directory` to acquire any additional entries the
        Distribution's content_handler might serve in that directory. If there is an Artifact to be
        served, it is served to the client.

        If there's no publication, the above paragraph is applied to the latest repository linked
        to the matched Distribution.

        Finally, when nothing is served to client yet, we check if there is a remote for the
        Distribution. If so, the Artifact is pulled from the remote and streamed to the client.

        Args:
            path (str): The path component of the URL.
            request(:class:`~aiohttp.web.Request`): The request to prepare a response for.

        Raises:
            PathNotResolved: The path could not be matched to a published file.
            PermissionError: When not permitted.

        Returns:
            :class:`aiohttp.web.StreamResponse` or :class:`aiohttp.web.FileResponse`: The response
                streamed back to the client.
        """

        distro = await sync_to_async(self._match_distribution)(path)

        await sync_to_async(self._permit)(request, distro)

        rel_path = path.lstrip("/")
        rel_path = rel_path[len(distro.base_path):]
        rel_path = rel_path.lstrip("/")

        content_handler_result = await sync_to_async(distro.content_handler
                                                     )(rel_path)
        if content_handler_result is not None:
            return content_handler_result

        headers = self.response_headers(rel_path)

        repository = distro.repository
        publication = distro.publication
        repo_version = distro.repository_version

        if repository:

            def get_latest_publication_or_version_blocking():
                nonlocal repo_version
                nonlocal publication

                # Search for publication serving the closest latest version
                if not publication:
                    try:
                        versions = repository.versions.all()
                        publications = Publication.objects.filter(
                            repository_version__in=versions, complete=True)
                        publication = publications.select_related(
                            "repository_version").latest(
                                "repository_version", "pulp_created")
                        repo_version = publication.repository_version
                    except ObjectDoesNotExist:
                        pass

                if not repo_version:
                    repo_version = repository.latest_version()

            await sync_to_async(get_latest_publication_or_version_blocking)()

        if publication:
            if rel_path == "" or rel_path[-1] == "/":
                try:
                    index_path = "{}index.html".format(rel_path)

                    await sync_to_async(publication.published_artifact.get
                                        )(relative_path=index_path)

                    rel_path = index_path
                    headers = self.response_headers(rel_path)
                except ObjectDoesNotExist:
                    dir_list = await self.list_directory(
                        None, publication, rel_path)
                    dir_list.update(await sync_to_async(
                        distro.content_handler_list_directory)(rel_path))
                    return HTTPOk(headers={"Content-Type": "text/html"},
                                  body=self.render_html(dir_list))

            # published artifact
            try:

                def get_contentartifact_blocking():
                    return (publication.published_artifact.select_related(
                        "content_artifact",
                        "content_artifact__artifact",
                    ).get(relative_path=rel_path).content_artifact)

                ca = await sync_to_async(get_contentartifact_blocking)()
            except ObjectDoesNotExist:
                pass
            else:
                if ca.artifact:
                    return await self._serve_content_artifact(
                        ca, headers, request)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca)

            # pass-through
            if publication.pass_through:
                try:

                    def get_contentartifact_blocking():
                        ca = ContentArtifact.objects.select_related(
                            "artifact").get(
                                content__in=publication.repository_version.
                                content,
                                relative_path=rel_path,
                            )
                        return ca

                    ca = await sync_to_async(get_contentartifact_blocking)()
                except MultipleObjectsReturned:
                    log.error(
                        _("Multiple (pass-through) matches for {b}/{p}"),
                        {
                            "b": distro.base_path,
                            "p": rel_path
                        },
                    )
                    raise
                except ObjectDoesNotExist:
                    pass
                else:
                    if ca.artifact:
                        return await self._serve_content_artifact(
                            ca, headers, request)
                    else:
                        return await self._stream_content_artifact(
                            request, StreamResponse(headers=headers), ca)

        if repo_version and not publication and not distro.SERVE_FROM_PUBLICATION:
            if rel_path == "" or rel_path[-1] == "/":
                index_path = "{}index.html".format(rel_path)

                def contentartifact_exists_blocking():
                    return ContentArtifact.objects.filter(
                        content__in=repo_version.content,
                        relative_path=index_path).exists()

                contentartifact_exists = await sync_to_async(
                    contentartifact_exists_blocking)()
                if contentartifact_exists:
                    rel_path = index_path
                else:
                    dir_list = await self.list_directory(
                        repo_version, None, rel_path)
                    dir_list.update(await sync_to_async(
                        distro.content_handler_list_directory)(rel_path))
                    return HTTPOk(headers={"Content-Type": "text/html"},
                                  body=self.render_html(dir_list))

            try:

                def get_contentartifact_blocking():
                    ca = ContentArtifact.objects.select_related(
                        "artifact").get(content__in=repo_version.content,
                                        relative_path=rel_path)
                    return ca

                ca = await sync_to_async(get_contentartifact_blocking)()
            except MultipleObjectsReturned:
                log.error(
                    _("Multiple (pass-through) matches for {b}/{p}"),
                    {
                        "b": distro.base_path,
                        "p": rel_path
                    },
                )
                raise
            except ObjectDoesNotExist:
                pass
            else:
                if ca.artifact:
                    return await self._serve_content_artifact(
                        ca, headers, request)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca)

        if distro.remote:

            def cast_remote_blocking():
                return distro.remote.cast()

            remote = await sync_to_async(cast_remote_blocking)()

            try:
                url = remote.get_remote_artifact_url(rel_path, request=request)

                def get_remote_artifact_blocking():
                    ra = RemoteArtifact.objects.select_related(
                        "content_artifact",
                        "content_artifact__artifact",
                        "remote",
                    ).get(remote=remote, url=url)
                    return ra

                ra = await sync_to_async(get_remote_artifact_blocking)()
                ca = ra.content_artifact
                if ca.artifact:
                    return await self._serve_content_artifact(
                        ca, headers, request)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca)
            except ObjectDoesNotExist:
                ca = ContentArtifact(relative_path=rel_path)
                ra = RemoteArtifact(remote=remote,
                                    url=url,
                                    content_artifact=ca)
                return await self._stream_remote_artifact(
                    request, StreamResponse(headers=headers), ra)

        raise PathNotResolved(path)
Esempio n. 11
0
    async def _match_and_stream(self, path, request):
        """
        Match the path and stream results either from the filesystem or by downloading new data.

        Args:
            path (str): The path component of the URL.
            request(:class:`~aiohttp.web.Request`): The request to prepare a response for.

        Raises:
            PathNotResolved: The path could not be matched to a published file.
            PermissionError: When not permitted.

        Returns:
            :class:`aiohttp.web.StreamResponse` or :class:`aiohttp.web.FileResponse`: The response
                streamed back to the client.
        """
        distro = self._match_distribution(path)
        self._permit(request, distro)

        rel_path = path.lstrip('/')
        rel_path = rel_path[len(distro.base_path):]
        rel_path = rel_path.lstrip('/')

        headers = self.response_headers(rel_path)

        publication = getattr(distro, 'publication', None)

        if publication:
            if rel_path == '' or rel_path[-1] == '/':
                try:
                    index_path = '{}index.html'.format(rel_path)
                    publication.published_artifact.get(
                        relative_path=index_path)
                    rel_path = index_path
                except ObjectDoesNotExist:
                    dir_list = await self.list_directory(
                        None, publication, rel_path)
                    return HTTPOk(headers={"Content-Type": "text/html"},
                                  body=dir_list)

            # published artifact
            try:
                pa = publication.published_artifact.get(relative_path=rel_path)
                ca = pa.content_artifact
            except ObjectDoesNotExist:
                pass
            else:
                if ca.artifact:
                    return self._handle_file_response(ca.artifact.file,
                                                      headers)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca)

            # pass-through
            if publication.pass_through:
                try:
                    ca = ContentArtifact.objects.get(
                        content__in=publication.repository_version.content,
                        relative_path=rel_path)
                except MultipleObjectsReturned:
                    log.error(_('Multiple (pass-through) matches for {b}/{p}'),
                              {
                                  'b': distro.base_path,
                                  'p': rel_path,
                              })
                    raise
                except ObjectDoesNotExist:
                    pass
                else:
                    if ca.artifact:
                        return self._handle_file_response(
                            ca.artifact.file, headers)
                    else:
                        return await self._stream_content_artifact(
                            request, StreamResponse(headers=headers), ca)

        repo_version = getattr(distro, 'repository_version', None)
        repository = getattr(distro, 'repository', None)

        if repository or repo_version:
            if repository:
                repo_version = distro.repository.latest_version()

            if rel_path == '' or rel_path[-1] == '/':
                try:
                    index_path = '{}index.html'.format(rel_path)
                    ContentArtifact.objects.get(
                        content__in=repo_version.content,
                        relative_path=index_path)
                    rel_path = index_path
                except ObjectDoesNotExist:
                    dir_list = await self.list_directory(
                        repo_version, None, rel_path)
                    return HTTPOk(headers={"Content-Type": "text/html"},
                                  body=dir_list)

            try:
                ca = ContentArtifact.objects.get(
                    content__in=repo_version.content, relative_path=rel_path)
            except MultipleObjectsReturned:
                log.error(_('Multiple (pass-through) matches for {b}/{p}'), {
                    'b': distro.base_path,
                    'p': rel_path,
                })
                raise
            except ObjectDoesNotExist:
                pass
            else:
                return self._handle_file_response(ca.artifact.file, headers)

        if distro.remote:
            remote = distro.remote.cast()
            try:
                url = remote.get_remote_artifact_url(rel_path)
                ra = RemoteArtifact.objects.get(remote=remote, url=url)
                ca = ra.content_artifact
                if ca.artifact:
                    return self._handle_file_response(ca.artifact.file,
                                                      headers)
                else:
                    return await self._stream_content_artifact(
                        request, StreamResponse(headers=headers), ca)
            except ObjectDoesNotExist:
                ca = ContentArtifact(relative_path=rel_path)
                ra = RemoteArtifact(remote=remote,
                                    url=url,
                                    content_artifact=ca)
                return await self._stream_remote_artifact(
                    request, StreamResponse(headers=headers), ra)

        raise PathNotResolved(path)
Esempio n. 12
0
 async def post(self):
     post_data = await self.request.post()
     print(post_data)
     return HTTPOk()
Esempio n. 13
0
 async def error(request):
     raise HTTPOk(text='hello')