Ejemplo n.º 1
0
def project_api(request, org_slug, project_id):
    from collections import OrderedDict

    # Get user from API key.
    api_key = request.META.get("HTTP_AUTHORIZATION", "").strip()
    if len(
            api_key
    ) < 32:  # prevent null values from matching against users without api keys
        return JsonResponse(OrderedDict([
            ("status", "error"),
            ("error",
             "An API key was not present in the Authorization header.")
        ]),
                            json_dumps_params={"indent": 2},
                            status=403)

    from django.db.models import Q
    from .models import User, Project

    try:
        user = User.objects.get(
            Q(api_key_rw=api_key) | Q(api_key_ro=api_key)
            | Q(api_key_wo=api_key))
    except User.DoesNotExist:
        return JsonResponse(OrderedDict([
            ("status", "error"),
            ("error",
             "A valid API key was not present in the Authorization header.")
        ]),
                            json_dumps_params={"indent": 2},
                            status=403)

    # Get project and check authorization.
    organization = get_object_or_404(Organization, subdomain=org_slug)
    project = get_object_or_404(Project,
                                id=project_id,
                                organization=organization)
    if not project.has_read_priv(user):
        return JsonResponse(OrderedDict([
            ("status", "error"),
            ("error",
             "The user associated with the API key does not have read perission on the project."
             )
        ]),
                            json_dumps_params={"indent": 2},
                            status=403)

    if request.method == "GET":
        # Export data.
        value = project.export_json(include_file_content=False,
                                    include_metadata=False)

        # Return the value as JSON.
        return JsonResponse(value, json_dumps_params={"indent": 2})

    elif request.method == "POST":
        # Update the value.

        # Check that the API key has write access.
        if not project.root_task.has_write_priv(user):
            return JsonResponse(OrderedDict([
                ("status", "error"),
                ("error",
                 "The user associated with the API key does not have write perission on the project."
                 )
            ]),
                                json_dumps_params={"indent": 2},
                                status=403)
        if api_key not in (user.api_key_rw, user.api_key_wo):
            return JsonResponse(OrderedDict([
                ("status", "error"),
                ("error",
                 "The API key does not have write perission on the project.")
            ]),
                                json_dumps_params={"indent": 2},
                                status=403)

        # Parse the new project body.
        if request.META.get("CONTENT_TYPE") == "application/json":
            # A JSON object is given in the import/export format. Parse it.
            import json
            try:
                value = json.loads(request.body.decode("utf-8"))
            except json.decoder.JSONDecodeError as e:
                return JsonResponse(OrderedDict([
                    ("status", "error"),
                    ("error", "Invalid JSON in request body. " + str(e))
                ]),
                                    json_dumps_params={"indent": 2},
                                    status=400)

            # Update.
            log = []
            ok = project.import_json(value, user, "api",
                                     lambda msg: log.append(msg))

        else:
            # Form fields are given in POST and FILES. Process each item...

            from guidedmodules.models import TaskAnswer
            from guidedmodules.answer_validation import question_input_parser, validator
            import django.core.files.uploadedfile

            # For each item...
            log = []
            ok = True
            for key, v in list(request.POST.lists()) + list(
                    request.FILES.items()):
                try:
                    # The item is a dotted path of project + question IDs to the
                    # question to update. Follow the path to find the Task and ModuleQuestion
                    # to update. Start with the project root task, then for each item in the path...
                    task = project.root_task
                    question = None
                    if not key.startswith("project."):
                        raise ValueError("Invalid question ID: " + key)
                    for i, pathitem in enumerate(key.split(".")[1:]):
                        # If this is not the first item, then in the last iteration we
                        # were left with a Task and a ModuleQuestion in that task. Since
                        # we've continued with another dot and path part, the last ModuleQuestion
                        # must have been a module-type question. Move to its *answer*,
                        # which is another Task.
                        if question is not None:
                            if question.spec["type"] != "module":
                                raise ValueError("Invalid question ID: " + key)
                            try:
                                task = task.get_or_create_subtask(
                                    user, question)
                            except ValueError:
                                # Raised if the question is not answered and the question uses
                                # a protocol for selecting compliance apps rather than specifying
                                # a concrete Module to use for answers. In this case, we can't
                                # start a Task implicitly.
                                raise ValueError(
                                    "Invalid question ID '{}': {} has not been answered yet by a compliance app so its data fields cannot be set."
                                    .format(key,
                                            ".".join(key.split(".")[:i + 1])))

                        # Get the question this corresponds to within the task
                        question = task.module.questions.filter(
                            key=pathitem).first()
                        if not question:
                            raise ValueError("Invalid question ID: " + key)

                    # Parse and validate the answer.
                    v = question_input_parser.parse(question, v)
                    v = validator.validate(question, v)

                    # Save.
                    v_file = None
                    if question.spec["type"] == "file":
                        v_file = v
                        v = None
                    ta, _ = TaskAnswer.objects.get_or_create(task=task,
                                                             question=question)
                    saved = ta.save_answer(v, [], v_file, user, "api")
                    if saved:
                        log.append(key + " updated")
                    else:
                        log.append(key + " unchanged")

                except ValueError as e:
                    log.append(key + ": " + str(e))
                    ok = False

        return JsonResponse(OrderedDict([
            ("status", "ok" if ok else "error"),
            ("details", log),
        ]),
                            json_dumps_params={"indent": 2})
Ejemplo n.º 2
0
def project_api(request, org_slug, project_id):
    from collections import OrderedDict

    # Get user from API key.
    api_key = request.META.get("HTTP_AUTHORIZATION", "").strip()
    if len(
            api_key
    ) < 32:  # prevent null values from matching against users without api keys
        return JsonResponse(OrderedDict([
            ("status", "error"),
            ("error",
             "An API key was not present in the Authorization header.")
        ]),
                            json_dumps_params={"indent": 2},
                            status=403)

    from django.db.models import Q
    from .models import User, Project

    try:
        user = User.objects.get(
            Q(api_key_rw=api_key) | Q(api_key_ro=api_key)
            | Q(api_key_wo=api_key))
    except User.DoesNotExist:
        return JsonResponse(OrderedDict([
            ("status", "error"),
            ("error",
             "A valid API key was not present in the Authorization header.")
        ]),
                            json_dumps_params={"indent": 2},
                            status=403)

    # Get project and check authorization.
    organization = get_object_or_404(Organization, subdomain=org_slug)
    project = get_object_or_404(Project,
                                id=project_id,
                                organization=organization)
    if not project.has_read_priv(user):
        return JsonResponse(OrderedDict([
            ("status", "error"),
            ("error",
             "The user associated with the API key does not have read perission on the project."
             )
        ]),
                            json_dumps_params={"indent": 2},
                            status=403)

    if request.method == "GET":
        # Export data.
        value = project.export_json(include_file_content=False,
                                    include_metadata=False)

        # Return the value as JSON.
        return JsonResponse(value, json_dumps_params={"indent": 2})

    elif request.method == "POST":
        # Update the value.

        # Check that the API key has write access.
        if not project.root_task.has_write_priv(user):
            return JsonResponse(OrderedDict([
                ("status", "error"),
                ("error",
                 "The user associated with the API key does not have write perission on the project."
                 )
            ]),
                                json_dumps_params={"indent": 2},
                                status=403)
        if api_key not in (user.api_key_rw, user.api_key_wo):
            return JsonResponse(OrderedDict([
                ("status", "error"),
                ("error",
                 "The API key does not have write perission on the project.")
            ]),
                                json_dumps_params={"indent": 2},
                                status=403)

        # Parse the new project body.
        if request.META.get("CONTENT_TYPE") == "application/json":
            # A JSON object is given in the import/export format. Parse it.
            import json
            try:
                value = json.loads(request.body.decode("utf-8"))
            except json.decoder.JSONDecodeError as e:
                return JsonResponse(OrderedDict([
                    ("status", "error"),
                    ("error", "Invalid JSON in request body. " + str(e))
                ]),
                                    json_dumps_params={"indent": 2},
                                    status=400)

            # Update.
            log = []
            ok = project.import_json(value, user, "api",
                                     lambda msg: log.append(msg))

        else:
            # Form fields are given in POST and FILES. Process each item...

            from guidedmodules.models import TaskAnswer
            from guidedmodules.answer_validation import question_input_parser, validator
            import django.core.files.uploadedfile

            log = []
            ok = True
            for key, v in list(request.POST.lists()) + list(
                    request.FILES.items()):
                try:
                    # Find the Task and ModuleQuestion that corresponds to
                    # this key-value pair.
                    task = project.root_task
                    question = None
                    if not key.startswith("project."):
                        raise ValueError("Invalid question ID: " + key)
                    for pathitem in key.split(".")[1:]:
                        # Advance to the task pointed to by the last question.
                        if question is not None:
                            if question.spec["type"] != "module":
                                raise ValueError("Invalid question ID: " + key)
                            ta = task.answers.filter(question=question).first()
                            if ta is None:
                                raise ValueError(
                                    "Invalid question ID (question on path is not answered yet): "
                                    + key)
                            a = ta.get_current_answer()
                            if not a or a.cleared:
                                raise ValueError(
                                    "Invalid question ID (question on path is not answered yet): "
                                    + key)
                            task = a.answered_by_task.first()

                        # Get the question this corresponds to within the task
                        question = task.module.questions.filter(
                            key=pathitem).first()
                        if not question:
                            raise ValueError("Invalid question ID: " + key)

                    # Parse and validate the answer.
                    v = question_input_parser.parse(question, v)
                    v = validator.validate(question, v)

                    # Save.
                    v_file = None
                    if question.spec["type"] == "file":
                        v_file = v
                        v = None
                    ta, _ = TaskAnswer.objects.get_or_create(task=task,
                                                             question=question)
                    saved = ta.save_answer(v, [], v_file, user, "api")
                    if saved:
                        log.append(key + " updated")
                    else:
                        log.append(key + " unchanged")

                except ValueError as e:
                    log.append(key + ": " + str(e))
                    ok = False

        return JsonResponse(OrderedDict([
            ("status", "ok" if ok else "error"),
            ("details", log),
        ]),
                            json_dumps_params={"indent": 2})