Пример #1
0
 def _get_form_alerts(self, errors: Dict[str, List[str]]) -> List[Alert]:
     """Create alerts from all errors in a WTForms form."""
     alerts = []
     for field, field_errors in errors.items():
         for error in field_errors:
             alerts.append(Alert("danger", error, field))
     return alerts
Пример #2
0
    def post(self, request_id):
        # check for request existence
        request = permissions.get_request_by_id(self.session, request_id)
        if not request:
            return self.notfound()

        # check that this user should be actioning this request
        user_requests, total = permissions.get_requests(
            self.session,
            status="pending",
            limit=None,
            offset=0,
            owner=self.current_user)
        user_request_ids = [ur.id for ur in user_requests.requests]
        if request.id not in user_request_ids:
            return self.forbidden()

        form = PermissionRequestUpdateForm(self.request.arguments)
        form.status.choices = self._get_choices(request.status)
        if not form.validate():
            change_comment_list = [
                (sc, user_requests.comment_by_status_change_id[sc.id])
                for sc in user_requests.status_change_by_request_id[request.id]
            ]

            return self.render(
                "permission-request-update.html",
                form=form,
                request=request,
                change_comment_list=change_comment_list,
                statuses=REQUEST_STATUS_CHOICES,
                alerts=self.get_form_alerts(form.errors),
            )

        try:
            permissions.update_request(self.session, request,
                                       self.current_user, form.status.data,
                                       form.reason.data)
        except UserNotAuditor as e:
            alerts = [Alert("danger", str(e))]

            change_comment_list = [
                (sc, user_requests.comment_by_status_change_id[sc.id])
                for sc in user_requests.status_change_by_request_id[request.id]
            ]

            return self.render(
                "permission-request-update.html",
                form=form,
                request=request,
                change_comment_list=change_comment_list,
                statuses=REQUEST_STATUS_CHOICES,
                alerts=alerts,
            )

        return self.redirect("/permissions/requests?status=pending")
Пример #3
0
 def disable_permission_failed_existing_grants(
     self,
     name: str,
     group_grants: List[GroupPermissionGrant],
     service_account_grants: List[ServiceAccountPermissionGrant],
 ) -> None:
     """The permission view page will show the grants, so we don't include them in the error."""
     alert = Alert(
         "danger",
         (
             "Permission cannot be disabled while it is still granted to some groups or"
             " service accounts"
         ),
     )
     self.redirect("/permissions/{}".format(name), alerts=[alert])
Пример #4
0
    def post(self, *args: Any, **kwargs: Any) -> None:
        name = self.get_path_argument("name")

        user = User.get(self.session, name=name)
        if not user:
            return self.notfound()

        if not self.check_access(self.session, self.current_user, user):
            return self.forbidden()

        try:
            if user.role_user:
                disable_role_user(self.session, user=user)
            else:
                disable_user(self.session, user)
        except PluginRejectedDisablingUser as e:
            alert = Alert("danger", str(e))
            return self.redirect("/users/{}".format(user.name), alerts=[alert])

        self.session.commit()

        AuditLog.log(
            self.session,
            self.current_user.id,
            "disable_user",
            "Disabled user.",
            on_user_id=user.id,
        )

        if user.role_user:
            group = Group.get(self.session, name=user.username)
            if group and group.audit:
                # complete the audit
                group.audit.complete = True
                self.session.commit()

                cancel_async_emails(self.session, f"audit-{group.id}")

                AuditLog.log(
                    self.session,
                    self.current_user.id,
                    "complete_audit",
                    "Disabling group completes group audit.",
                    on_group_id=group.id,
                )

        return self.redirect("/users/{}?refresh=yes".format(user.name))
Пример #5
0
    def post(self, *args: Any, **kwargs: Any) -> None:
        name = self.get_path_argument("name")

        group = Group.get(self.session, name=name)
        if not group:
            return self.notfound()

        if not user_can_manage_group(self.session, group, self.current_user):
            return self.forbidden()

        form = GroupRemoveForm(self.request.arguments)
        if not form.validate():
            return self.send_error(status_code=400)

        member_type, member_name = form.data["member_type"], form.data[
            "member"]

        members = group.my_members()
        if not members.get((member_type.capitalize(), member_name), None):
            return self.send_error(
                status_code=400,
                reason="Invalid remove request: {} is not a member of {}.".
                format(member_name, group.name),
            )

        removed_member = get_user_or_group(self.session,
                                           member_name,
                                           user_or_group=member_type)

        if self.current_user == removed_member:
            return self.send_error(
                status_code=400,
                reason="Can't remove yourself. Leave group instead.")

        role_user = is_role_user(self.session, group=group)

        if role_user and get_role_user(
                self.session, group=group).user.name == removed_member.name:
            return self.send_error(
                status_code=400,
                reason=
                "Can't remove a service account user from the service account group.",
            )

        try:
            group.revoke_member(self.current_user, removed_member,
                                "Removed by owner/np-owner/manager")

            AuditLog.log(
                self.session,
                self.current_user.id,
                "remove_from_group",
                "{} was removed from the group.".format(removed_member.name),
                on_group_id=group.id,
                on_user_id=removed_member.id,
            )
        except PluginRejectedGroupMembershipUpdate as e:
            alert = Alert("danger", str(e))

            if role_user:
                return self.redirect("/service/{}".format(group.name),
                                     alerts=[alert])
            else:
                return self.redirect("/groups/{}".format(group.name),
                                     alerts=[alert])

        return self.redirect("/groups/{}?refresh=yes".format(group.name))
Пример #6
0
    def post(self, *args: Any, **kwargs: Any) -> None:
        name = self.get_path_argument("name")

        group = Group.get(self.session, name=name)
        if not group or not group.enabled:
            return self.notfound()

        members = group.my_members()
        member_groups = {g for t, g in members if t == "Group"}
        user_is_member = self._is_user_a_member(group, members)

        form = GroupJoinForm(self.request.arguments)
        form.member.choices = self._get_choices(group, member_groups,
                                                user_is_member)
        if not form.validate():
            return self.render("group-join.html",
                               form=form,
                               group=group,
                               alerts=self.get_form_alerts(form.errors))

        member = self._get_member(form.data["member"])
        if not member:
            return self.render(
                "group-join.html",
                form=form,
                group=group,
                alerts=[
                    Alert(
                        "danger", "Unknown user or group: {}".format(
                            form.data["member"]))
                ],
            )

        try:
            assert_can_join(group, member, role=form.data["role"])
        except UserNotAuditor as e:
            return self.render(
                "group-join.html",
                form=form,
                group=group,
                alerts=[Alert("danger", str(e), "Audit Policy Enforcement")],
            )

        if group.canjoin == "nobody":
            fail_message = "This group cannot be joined at this time."
            return self.render("group-join.html",
                               form=form,
                               group=group,
                               alerts=[Alert("danger", fail_message)])

        if group.require_clickthru_tojoin:
            if not form.data["clickthru_agreement"]:
                return self.render(
                    "group-join.html",
                    form=form,
                    group=group,
                    alerts=[
                        Alert(
                            "danger",
                            "please accept review of the group's description",
                            "Clickthru Enforcement",
                        )
                    ],
                )

        # We only use the default expiration time if no expiration time was given
        # This does mean that if a user wishes to join a group with no expiration
        # (even with an owner's permission) that has an auto expiration, they must
        # first be accepted to the group and then have the owner edit the user to
        # have no expiration.

        expiration = None
        if form.data["expiration"]:
            expiration = datetime.strptime(form.data["expiration"], "%m/%d/%Y")
        elif group.auto_expire:
            expiration = datetime.utcnow() + group.auto_expire

        # If the requested role is member, set the status based on the group's canjoin setting,
        # which automatically actions the request if the group can be joined by anyone and
        # otherwise sets it pending.
        #
        # However, we don't want to let people autojoin as owner or np-owner even to otherwise open
        # groups, so if the role is not member, force the status to pending.
        if form.data["role"] == "member":
            status = GROUP_JOIN_CHOICES[group.canjoin]
        else:
            status = "pending"

        try:
            request = group.add_member(
                requester=self.current_user,
                user_or_group=member,
                reason=form.data["reason"],
                status=status,
                expiration=expiration,
                role=form.data["role"],
            )
        except InvalidRoleForMember as e:
            return self.render(
                "group-join.html",
                form=form,
                group=group,
                alerts=[Alert("danger", str(e), "Invalid Role")],
            )
        self.session.commit()

        if status == "pending":
            AuditLog.log(
                self.session,
                self.current_user.id,
                "join_group",
                "{} requested to join with role: {}".format(
                    member.name, form.data["role"]),
                on_group_id=group.id,
            )

            mail_to = [
                user.name for user in group.my_users()
                if GROUP_EDGE_ROLES[user.role] in ("manager", "owner",
                                                   "np-owner")
            ]

            email_context = {
                "requester": member.name,
                "requested_by": self.current_user.name,
                "request_id": request.id,
                "group_name": group.name,
                "reason": form.data["reason"],
                "expiration": expiration,
                "role": form.data["role"],
                "references_header": request.reference_id,
            }

            subj = self.render_template("email/pending_request_subj.tmpl",
                                        group=group.name,
                                        user=self.current_user.name)
            send_email(self.session, mail_to, subj, "pending_request",
                       settings(), email_context)

        elif status == "actioned":
            AuditLog.log(
                self.session,
                self.current_user.id,
                "join_group",
                "{} auto-approved to join with role: {}".format(
                    member.name, form.data["role"]),
                on_group_id=group.id,
            )
        else:
            raise Exception(f"Unknown join status {status}")

        return self.redirect("/groups/{}?refresh=yes".format(group.name))
Пример #7
0
def _deserialize_alert(alert_dict: Dict[str, str]) -> Alert:
    return Alert(
        severity=alert_dict["severity"],
        message=alert_dict["message"],
        heading=alert_dict["heading"],
    )
Пример #8
0
 def get_form_alerts(self, errors: Dict[str, List[str]]) -> List[Alert]:
     alerts = []
     for field, field_errors in errors.items():
         for error in field_errors:
             alerts.append(Alert("danger", error, field))
     return alerts
Пример #9
0
    def post(self, *args: Any, **kwargs: Any) -> None:
        request_id = self.get_path_argument("request_id")
        name = self.get_path_argument("name")

        group = Group.get(self.session, name=name)
        if not group:
            return self.notfound()

        members = group.my_members()
        my_role = user_role(self.current_user, members)
        if my_role not in ("manager", "owner", "np-owner"):
            return self.forbidden()

        request = self.session.query(Request).filter_by(id=request_id).scalar()
        if not request:
            return self.notfound()

        on_behalf = get_on_behalf_by_request(self.session, request)

        form = GroupRequestModifyForm(self.request.arguments)
        form.status.choices = self._get_choices(request.status)

        updates = request.my_status_updates()

        if not form.status.choices:
            alerts = [Alert("info", "Request has already been processed")]
            return self.render(
                "group-request-update.html",
                group=group,
                request=request,
                on_behalf=on_behalf,
                members=members,
                form=form,
                alerts=alerts,
                statuses=REQUEST_STATUS_CHOICES,
                updates=updates,
            )

        if not form.validate():
            return self.render(
                "group-request-update.html",
                group=group,
                request=request,
                on_behalf=on_behalf,
                members=members,
                form=form,
                alerts=self.get_form_alerts(form.errors),
                statuses=REQUEST_STATUS_CHOICES,
                updates=updates,
            )

        # We have to test this here, too, to ensure that someone can't sneak in with a pending
        # request that used to be allowed.
        if form.data["status"] != "cancelled":
            try:
                assert_can_join(request.requesting,
                                on_behalf,
                                role=request.edge.role)
            except UserNotAuditor as e:
                return self.render(
                    "group-request-update.html",
                    group=group,
                    request=request,
                    on_behalf=on_behalf,
                    members=members,
                    form=form,
                    statuses=REQUEST_STATUS_CHOICES,
                    updates=updates,
                    alerts=[
                        Alert("danger", str(e), "Audit Policy Enforcement")
                    ],
                )

        request.update_status(self.current_user, form.data["status"],
                              form.data["reason"])
        self.session.commit()

        AuditLog.log(
            self.session,
            self.current_user.id,
            "update_request",
            "Updated request to status: {}".format(form.data["status"]),
            on_group_id=group.id,
            on_user_id=request.requester.id,
        )

        edge = self.session.query(GroupEdge).filter_by(
            id=request.edge_id).one()

        approver_mail_to = [
            user.name for user in group.my_approver_users()
            if user.name != self.current_user.name
            and user.name != request.requester.username
        ]

        subj = "Re: " + self.render_template("email/pending_request_subj.tmpl",
                                             group=group.name,
                                             user=request.requester.username)

        send_email(
            self.session,
            approver_mail_to,
            subj,
            "approver_request_updated",
            settings(),
            {
                "group_name": group.name,
                "requester": request.requester.username,
                "changed_by": self.current_user.name,
                "status": form.data["status"],
                "role": edge.role,
                "reason": form.data["reason"],
                "references_header": request.reference_id,
            },
        )

        if form.data["status"] == "actioned":
            send_email(
                self.session,
                [request.requester.name],
                "Added to group: {}".format(group.groupname),
                "request_actioned",
                settings(),
                {
                    "group_name": group.name,
                    "actioned_by": self.current_user.name,
                    "reason": form.data["reason"],
                    "expiration": edge.expiration,
                    "role": edge.role,
                },
            )
        elif form.data["status"] == "cancelled":
            send_email(
                self.session,
                [request.requester.name],
                "Request to join cancelled: {}".format(group.groupname),
                "request_cancelled",
                settings(),
                {
                    "group_name": group.name,
                    "cancelled_by": self.current_user.name,
                    "reason": form.data["reason"],
                    "expiration": edge.expiration,
                    "role": edge.role,
                },
            )

        # No explicit refresh because handler queries SQL.
        if form.data["redirect_aggregate"]:
            return self.redirect("/user/requests")
        else:
            return self.redirect("/groups/{}/requests".format(group.name))
Пример #10
0
    def post(self, *args: Any, **kwargs: Any) -> None:
        name = self.get_path_argument("name")

        group = Group.get(self.session, name=name)
        if not group:
            return self.notfound()

        if not user_can_manage_group(self.session, group, self.current_user):
            return self.forbidden()

        members = group.my_members()
        my_role = user_role(self.current_user, members)
        form = self.get_form(role=my_role)
        if not form.validate():
            return self.render("group-add.html",
                               form=form,
                               group=group,
                               alerts=self.get_form_alerts(form.errors))

        member = get_user_or_group(self.session, form.data["member"])
        if member.type == "User" and is_role_user(self.session, member):
            # For service accounts, we want to always add the group to other groups, not the user
            member = get_role_user(self.session, user=member).group
        if not member:
            form.member.errors.append("User or group not found.")
        elif (member.type, member.name) in group.my_members():
            form.member.errors.append(
                "User or group is already a member of this group.")
        elif group.name == member.name:
            form.member.errors.append(
                "By definition, this group is a member of itself already.")

        # Ensure this doesn't violate auditing constraints
        try:
            assert_can_join(group, member, role=form.data["role"])
        except UserNotAuditor as e:
            form.member.errors.append(str(e))

        if form.member.errors:
            return self.render("group-add.html",
                               form=form,
                               group=group,
                               alerts=self.get_form_alerts(form.errors))

        expiration = None
        if form.data["expiration"]:
            expiration = datetime.strptime(form.data["expiration"], "%m/%d/%Y")

        try:
            group.add_member(
                requester=self.current_user,
                user_or_group=member,
                reason=form.data["reason"],
                status="actioned",
                expiration=expiration,
                role=form.data["role"],
            )
        except InvalidRoleForMember as e:
            return self.render("group-add.html",
                               form=form,
                               group=group,
                               alerts=[Alert("danger", str(e))])

        self.session.commit()

        on_user_id = member.id if member.type == "User" else None
        AuditLog.log(
            self.session,
            self.current_user.id,
            "join_group",
            "{} added to group with role: {}".format(member.name,
                                                     form.data["role"]),
            on_group_id=group.id,
            on_user_id=on_user_id,
        )

        if member.type == "User":
            send_email(
                self.session,
                [member.name],
                "Added to group: {}".format(group.name),
                "request_actioned",
                settings(),
                {
                    "group_name": group.name,
                    "actioned_by": self.current_user.name,
                    "reason": form.data["reason"],
                    "expiration": expiration,
                    "role": form.data["role"],
                },
            )

        return self.redirect("/groups/{}?refresh=yes".format(group.name))
Пример #11
0
def get_group_view_template_vars(session, actor, group, graph):
    # type: (Session, User, Group, GroupGraph) -> Dict[str, Any]
    ret = {}
    ret["grantable"] = user_grantable_permissions(session, actor)

    try:
        group_md = graph.get_group_details(group.name)
    except NoSuchGroup:
        # Very new group with no metadata yet, or it has been disabled and
        # excluded from in-memory cache.
        group_md = {}

    ret["members"] = group.my_members()
    ret["groups"] = group.my_groups()
    ret["service_accounts"] = get_service_accounts(session, group)
    ret["permissions"] = group_md.get("permissions", [])
    for permission in ret["permissions"]:
        permission["granted_on"] = datetime.fromtimestamp(permission["granted_on"])

    ret["permission_requests_pending"] = []
    for req in get_pending_request_by_group(session, group):
        granters = []
        for owner, argument in get_owner_arg_list(session, req.permission, req.argument):
            granters.append(owner.name)
        ret["permission_requests_pending"].append((req, granters))

    ret["audited"] = group_md.get("audited", False)
    ret["log_entries"] = group.my_log_entries()
    ret["num_pending"] = count_requests_by_group(session, group, status="pending")
    ret["current_user_role"] = {
        "is_owner": user_role_index(actor, ret["members"]) in OWNER_ROLE_INDICES,
        "is_approver": user_role_index(actor, ret["members"]) in APPROVER_ROLE_INDICES,
        "is_manager": user_role(actor, ret["members"]) == "manager",
        "is_member": user_role(actor, ret["members"]) is not None,
        "role": user_role(actor, ret["members"]),
    }
    ret["statuses"] = AUDIT_STATUS_CHOICES

    # The user can leave if they're a normal member, or if they're not the only owner.
    number_of_owners = len(
        [m for (t, m), o in ret["members"].items() if t == "User" and o.role in OWNER_ROLE_INDICES]
    )
    ret["can_leave"] = ret["current_user_role"]["is_member"] and (
        not ret["current_user_role"]["is_owner"] or number_of_owners > 1
    )

    # Add mapping_id to permissions structure
    ret["my_permissions"] = group.my_permissions()
    for perm_up in ret["permissions"]:
        for perm_direct in ret["my_permissions"]:
            if (
                perm_up["permission"] == perm_direct.name
                and perm_up["argument"] == perm_direct.argument
            ):
                perm_up["mapping_id"] = perm_direct.mapping_id
                break

    ret["alerts"] = []
    ret["self_pending"] = count_requests_by_group(session, group, status="pending", user=actor)
    if ret["self_pending"]:
        ret["alerts"].append(Alert("info", "You have a pending request to join this group.", None))

    return ret
Пример #12
0
    def post(self, *args: Any, **kwargs: Any) -> None:
        name = self.get_path_argument("name")
        member_name = self.get_path_argument("member_name")
        member_type = self.get_path_argument("member_type")

        group = Group.get(self.session, name=name)
        if not group:
            return self.notfound()

        members = group.my_members()
        my_role = user_role(self.current_user, members)
        if my_role not in ("manager", "owner", "np-owner"):
            return self.forbidden()

        member = members.get((member_type.capitalize(), member_name), None)
        if not member:
            return self.notfound()

        if member.type == "Group":
            user_or_group = Group.get(self.session, member.id)
        else:
            user_or_group = User.get(self.session, member.id)
        if not user_or_group:
            return self.notfound()

        edge = GroupEdge.get(
            self.session,
            group_id=group.id,
            member_type=OBJ_TYPES[member.type],
            member_pk=member.id,
        )
        if not edge:
            return self.notfound()

        form = self._get_form(member_name, my_role, member_type)
        if not form.validate():
            return self.render(
                "group-edit-member.html",
                group=group,
                member=member,
                edge=edge,
                form=form,
                alerts=self.get_form_alerts(form.errors),
            )

        try:
            assert_can_join(group, user_or_group, role=form.data["role"])
        except UserNotAuditor as e:
            return self.render(
                "group-edit-member.html",
                form=form,
                group=group,
                member=member,
                edge=edge,
                alerts=[Alert("danger", str(e), "Audit Policy Enforcement")],
            )

        expiration = None
        if form.data["expiration"]:
            expiration = datetime.strptime(form.data["expiration"], "%m/%d/%Y")

        try:
            group.edit_member(
                self.current_user,
                user_or_group,
                form.data["reason"],
                role=form.data["role"],
                expiration=expiration,
            )
        except (InvalidRoleForMember,
                PluginRejectedGroupMembershipUpdate) as e:
            return self.render(
                "group-edit-member.html",
                form=form,
                group=group,
                member=member,
                edge=edge,
                alerts=[Alert("danger", str(e))],
            )

        return self.redirect("/groups/{}?refresh=yes".format(group.name))
Пример #13
0
 def grant_permission_to_service_account_failed_permission_denied(
         self, permission: str, argument: str, service: str,
         message: str) -> None:
     alert = Alert("error", message)
     self._render_template(self._form, service, self._owner, [alert])
Пример #14
0
    def post(self, audit_id):
        if not user_has_permission(self.session, self.current_user,
                                   PERMISSION_AUDITOR):
            return self.forbidden()

        audit = self.session.query(Audit).filter(Audit.id == audit_id).one()

        # only owners can complete
        owner_ids = {member.id for member in audit.group.my_owners().values()}
        if self.current_user.id not in owner_ids:
            return self.forbidden()

        if audit.complete:
            return self.redirect("/groups/{}".format(audit.group.name))

        edges = {}
        for argument in self.request.arguments:
            if argument.startswith("audit_"):
                edges[int(argument.split("_")
                          [1])] = self.request.arguments[argument][0].decode()

        for audit_member_info in get_group_audit_members_infos(
                self.session, audit.group):
            if audit_member_info.audit_member_obj.id in edges:
                # You can only approve yourself (otherwise you can remove yourself
                # from the group and leave it ownerless)
                if audit_member_info.member_obj.id == self.current_user.id:
                    audit_member_info.audit_member_obj.status = "approved"
                elif edges[audit_member_info.audit_member_obj.
                           id] in AUDIT_STATUS_CHOICES:
                    audit_member_info.audit_member_obj.status = edges[
                        audit_member_info.audit_member_obj.id]

        self.session.commit()

        # If there are still pending statuses, then redirect to the group page.
        if group_has_pending_audit_members(self.session, audit.group):
            return self.redirect("/groups/{}".format(audit.group.name))

        # Complete audits have to be "enacted" now. This means anybody marked as remove has to
        # be removed from the group now.
        try:
            for audit_member_info in get_group_audit_members_infos(
                    self.session, audit.group):
                member_obj = audit_member_info.member_obj
                if audit_member_info.audit_member_obj.status == "remove":
                    audit.group.revoke_member(self.current_user, member_obj,
                                              "Revoked as part of audit.")
                    AuditLog.log(
                        self.session,
                        self.current_user.id,
                        "remove_member",
                        "Removed membership in audit: {}".format(
                            member_obj.name),
                        on_group_id=audit.group.id,
                        on_user_id=member_obj.id,
                        category=AuditLogCategory.audit,
                    )
        except PluginRejectedGroupMembershipUpdate as e:
            alert = Alert("danger", str(e))
            return self.redirect("/groups/{}".format(audit.group.name),
                                 alerts=[alert])

        audit.complete = True
        self.session.commit()

        # Now cancel pending emails
        cancel_async_emails(self.session, "audit-{}".format(audit.group.id))

        AuditLog.log(
            self.session,
            self.current_user.id,
            "complete_audit",
            "Completed group audit.",
            on_group_id=audit.group.id,
            category=AuditLogCategory.audit,
        )

        # check if all audits are complete
        if get_audits(self.session, only_open=True).count() == 0:
            AuditLog.log(
                self.session,
                self.current_user.id,
                "complete_global_audit",
                "last open audit have been completed",
                category=AuditLogCategory.audit,
            )

        return self.redirect("/groups/{}".format(audit.group.name))
Пример #15
0
    def post(self, *args, **kwargs):
        # type: (*Any, **Any) -> None
        form, args_by_perm = self._build_form(self.request.arguments)

        if not form.validate():
            return self.render(
                "permission-request.html",
                args_by_perm_json=json.dumps(args_by_perm),
                form=form,
                alerts=self.get_form_alerts(form.errors),
            )

        group = Group.get(self.session, name=form.group_name.data)
        if group is None:
            raise HTTPError(status_code=400,
                            reason="that group does not exist")

        permission = get_permission(self.session, form.permission.data)
        if permission is None:
            raise HTTPError(status_code=400,
                            reason="that permission does not exist")

        if permission.name not in args_by_perm:
            raise HTTPError(status_code=400,
                            reason="that permission was not in the form")

        argument = form.argument.data.strip()

        # save off request
        try:
            request = permissions.create_request(self.session,
                                                 self.current_user, group,
                                                 permission, argument,
                                                 form.reason.data)
        except permissions.RequestAlreadyGranted:
            alerts = [
                Alert("danger",
                      "This group already has this permission and argument.")
            ]
        except permissions.RequestAlreadyExists:
            alerts = [
                Alert(
                    "danger",
                    "Request for permission and argument already exists, please wait patiently.",
                )
            ]
        except permissions.NoOwnersAvailable:
            self.log_message(
                "prefilled perm+arg have no owner",
                group_name=group.name,
                permission=permission.name,
                argument=argument,
            )
            alerts = [
                Alert(
                    "danger",
                    "No owners available for requested permission and argument."
                    " If this error persists please contact an adminstrator.",
                )
            ]
        except UserNotAuditor as e:
            alerts = [Alert("danger", str(e))]
        else:
            alerts = []

        if alerts:
            return self.render(
                "permission-request.html",
                args_by_perm_json=json.dumps(args_by_perm),
                form=form,
                alerts=alerts,
            )
        else:
            return self.redirect("/permissions/requests/{}".format(request.id))