Accept email tickets/comments for anon users
This commit is contained in:
parent
846417baeb
commit
204597dfb4
|
@ -10,8 +10,8 @@ from email.utils import parseaddr
|
|||
from grp import getgrnam
|
||||
from todosrht.access import get_tracker, get_ticket
|
||||
from todosrht.types import TicketAccess, TicketResolution, Tracker, Ticket, User
|
||||
from todosrht.types import Label, TicketLabel, Event, EventType
|
||||
from todosrht.tickets import add_comment, get_participant_for_user, submit_ticket
|
||||
from todosrht.types import Label, TicketLabel, Event, EventType, ParticipantType
|
||||
from todosrht.tickets import add_comment, get_participant_for_email, submit_ticket
|
||||
from todosrht.webhooks import UserWebhook, TrackerWebhook, TicketWebhook
|
||||
from srht.validation import Validation
|
||||
import asyncio
|
||||
|
@ -68,10 +68,11 @@ class MailHandler:
|
|||
else:
|
||||
# TODO: user groups
|
||||
return None, None
|
||||
tracker, access = get_tracker(owner, tracker_name, user=sender)
|
||||
# TODO: ACLs for email participants
|
||||
tracker, access = get_tracker(owner, tracker_name, user=sender.user)
|
||||
if not ticket_id:
|
||||
return tracker, access
|
||||
ticket, access = get_ticket(tracker, ticket_id, user=sender)
|
||||
ticket, access = get_ticket(tracker, ticket_id, user=sender.user)
|
||||
return ticket, access
|
||||
|
||||
async def handle_RCPT(self, server, session,
|
||||
|
@ -105,8 +106,7 @@ class MailHandler:
|
|||
print("Rejecting email due to validation errors")
|
||||
return "550 " + ", ".join([e["reason"] for e in valid.errors])
|
||||
|
||||
participant = get_participant_for_user(sender)
|
||||
ticket = submit_ticket(tracker, participant, title, desc)
|
||||
ticket = submit_ticket(tracker, sender, title, desc)
|
||||
UserWebhook.deliver(UserWebhook.Events.ticket_create,
|
||||
ticket.to_dict(),
|
||||
UserWebhook.Subscription.user_id == sender.id)
|
||||
|
@ -122,8 +122,10 @@ class MailHandler:
|
|||
|
||||
resolution = None
|
||||
resolve = reopen = False
|
||||
cmds = ["!resolve", "!reopen", "!assign", "!label", "!unlabel"]
|
||||
participant = get_participant_for_user(sender)
|
||||
cmds = ["!resolve", "!reopen"]
|
||||
if sender.participant_type == ParticipantType.user:
|
||||
# TODO: This should be possible via ACLs later
|
||||
cmds += ["!assign", "!label", "!unlabel"]
|
||||
if any(last_line.startswith(cmd) for cmd in cmds):
|
||||
cmd = shlex.split(last_line)
|
||||
body = body[:-len(last_line)-1].rstrip()
|
||||
|
@ -141,7 +143,7 @@ class MailHandler:
|
|||
return ("550 The label you requested does not exist on " +
|
||||
"this tracker.")
|
||||
if not TicketAccess.triage in access:
|
||||
print(f"Rejected, {participant.name} has insufficient " +
|
||||
print(f"Rejected, {sender.name} has insufficient " +
|
||||
f"permissions (have {access}, want triage)")
|
||||
return "550 You do not have permission to triage on this tracker."
|
||||
for label in labels:
|
||||
|
@ -149,7 +151,7 @@ class MailHandler:
|
|||
.filter(TicketLabel.label_id == label.id)
|
||||
.filter(TicketLabel.ticket_id == ticket.id)).first()
|
||||
event = Event()
|
||||
event.participant_id = participant.id
|
||||
event.participant_id = sender.id
|
||||
event.ticket_id = ticket.id
|
||||
event.label_id = label.id
|
||||
if not ticket_label and cmd[0] == "!label":
|
||||
|
@ -174,7 +176,7 @@ class MailHandler:
|
|||
# TODO: Remaining commands
|
||||
|
||||
if not required_access in access:
|
||||
print(f"Rejected, {participant.name} has insufficient " +
|
||||
print(f"Rejected, {sender.name} has insufficient " +
|
||||
f"permissions (have {access}, want {required_access})")
|
||||
return "550 You do not have permission to post on this tracker."
|
||||
|
||||
|
@ -183,7 +185,7 @@ class MailHandler:
|
|||
return "550 Comment must be between 3 and 16384 characters."
|
||||
|
||||
if body:
|
||||
event = add_comment(participant, ticket, text=body,
|
||||
event = add_comment(sender, ticket, text=body,
|
||||
resolution=resolution, resolve=resolve, reopen=reopen)
|
||||
TicketWebhook.deliver(TicketWebhook.Events.event_create,
|
||||
event.to_dict(),
|
||||
|
@ -206,14 +208,8 @@ class MailHandler:
|
|||
|
||||
mail = email.message_from_bytes(envelope.content,
|
||||
policy=email.policy.SMTP)
|
||||
_from = parseaddr(mail["From"])
|
||||
sender = User.query.filter(User.email == _from[1]).one_or_none()
|
||||
|
||||
if not sender:
|
||||
print(f"Rejecting email from unknown sender {_from[1]}")
|
||||
# TODO: allow posting from users without an account
|
||||
return ("550 There is no account associated with this address. " +
|
||||
"Have you logged into todo.sr.ht on the web before?")
|
||||
name, sender_addr = parseaddr(mail["From"])
|
||||
sender = get_participant_for_email(sender_addr, name)
|
||||
|
||||
dest, access = self.lookup_destination(address, sender)
|
||||
if dest is None:
|
||||
|
|
|
@ -158,7 +158,7 @@
|
|||
<div class="updated">{{ ticket.updated | date }}</div>
|
||||
<div class="submitter">
|
||||
<a href="{{ ticket.submitter|participant_url }}">
|
||||
{{ ticket.submitter }}
|
||||
{{ ticket.submitter.name }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="comments">
|
||||
|
|
|
@ -64,6 +64,21 @@ def get_participant_for_user(user):
|
|||
db.session.flush()
|
||||
return participant
|
||||
|
||||
def get_participant_for_email(email, email_name=None):
|
||||
user = User.query.filter(User.email == email).one_or_none()
|
||||
if user:
|
||||
return get_participant_for_user(user)
|
||||
participant = Participant.query.filter(
|
||||
Participant.email == email).one_or_none()
|
||||
if not participant:
|
||||
participant = Participant()
|
||||
participant.email = email
|
||||
participant.email_name = email_name
|
||||
participant.participant_type = ParticipantType.email
|
||||
db.session.add(participant)
|
||||
db.session.flush()
|
||||
return participant
|
||||
|
||||
def find_mentioned_users(text):
|
||||
# TODO: Find mentioned email addresses as well
|
||||
usernames = re.findall(USER_MENTION_PATTERN, text)
|
||||
|
|
|
@ -52,7 +52,7 @@ class Tracker(Base):
|
|||
|
||||
default_anonymous_perms = sa.Column(FlagType(TicketAccess),
|
||||
nullable=False,
|
||||
default=TicketAccess.browse)
|
||||
default=TicketAccess.browse + TicketAccess.submit + TicketAccess.comment)
|
||||
"""Permissions granted to anonymous (non-logged in) users"""
|
||||
|
||||
@staticmethod
|
||||
|
|
Loading…
Reference in New Issue