Commit def763d6 authored by Martin Mareš's avatar Martin Mareš
Browse files

If a user has multiple e-mails in CAS, pick one of them

We prefer local UK addresses.
parent 72e5ab00
...@@ -6,7 +6,6 @@ CREATE TABLE owl_users ( ...@@ -6,7 +6,6 @@ CREATE TABLE owl_users (
auth_token varchar(64) UNIQUE DEFAULT NULL, -- this is called "access key" in UI auth_token varchar(64) UNIQUE DEFAULT NULL, -- this is called "access key" in UI
full_name varchar(255) NOT NULL, full_name varchar(255) NOT NULL,
email varchar(255) DEFAULT NULL, email varchar(255) DEFAULT NULL,
-- we allow CAS's format "{email1,email2,...}"
created timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, created timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_admin boolean NOT NULL DEFAULT false, is_admin boolean NOT NULL DEFAULT false,
notify boolean NOT NULL DEFAULT false, notify boolean NOT NULL DEFAULT false,
......
...@@ -1033,7 +1033,7 @@ def user_settings(): ...@@ -1033,7 +1033,7 @@ def user_settings():
form.notify_self.data = user.notify_self form.notify_self.data = user.notify_self
form.inline_att.data = user.inline_att form.inline_att.data = user.inline_att
return render_template('settings.html', form=form, user=user, emails=parse_emails(user.email)) return render_template('settings.html', form=form, user=user)
### Administration ### ### Administration ###
...@@ -1347,17 +1347,37 @@ def cas_login(): ...@@ -1347,17 +1347,37 @@ def cas_login():
return redirect(next) return redirect(next)
def parse_cas_emails(raw_email):
if raw_email[0] == '{' and raw_email[-1] == '}':
# Strange CAS encoding of multiple e-mail addresses
return raw_email[1:-1].split(',')
else:
return [raw_email]
def primary_cas_email(raw_email):
emails = parse_cas_emails(raw_email)
if not emails:
return None
uk = [e for e in emails if e.endswith('.cuni.cz')]
if uk:
return uk[0]
else:
return emails[0]
def session_from_cas(user, attrs): def session_from_cas(user, attrs):
db_query("SELECT * FROM owl_users WHERE ukco=%s", (user,)) db_query("SELECT * FROM owl_users WHERE ukco=%s", (user,))
row = db.fetchone() row = db.fetchone()
if row: if row:
app.logger.info('Logged in user: uid=%s, ukco=%s, cn=%s, mail=%s, admin=%s', row.uid, row.ukco, row.full_name, row.email, row.is_admin) app.logger.info('Logged in user: uid=%s, ukco=%s, cn=%s, mail=%s, admin=%s', row.uid, row.ukco, row.full_name, row.email, row.is_admin)
else: else:
email = primary_cas_email(attrs['mail'])
db_query(""" db_query("""
INSERT INTO owl_users(ukco, full_name, email) INSERT INTO owl_users(ukco, full_name, email)
VALUES (%s, %s, %s) VALUES (%s, %s, %s)
RETURNING uid, ukco, full_name, email, is_admin, inline_att RETURNING uid, ukco, full_name, email, is_admin, inline_att
""", (user, attrs['cn'], attrs['mail'])) """, (user, attrs['cn'], email))
row = db.fetchone() row = db.fetchone()
app.logger.info('Created new user: uid=%s, ukco=%s, cn=%s, mail=%s', row.uid, row.ukco, row.full_name, row.email) app.logger.info('Created new user: uid=%s, ukco=%s, cn=%s, mail=%s', row.uid, row.ukco, row.full_name, row.email)
db_connection.commit() db_connection.commit()
...@@ -1562,6 +1582,25 @@ def cli_add_teacher(course_ident, teacher): ...@@ -1562,6 +1582,25 @@ def cli_add_teacher(course_ident, teacher):
db_connection.commit() db_connection.commit()
@app.cli.command("convert-emails")
def cli_convert_emails():
"""Convert up e-mail addresses in the database from old SIS format."""
db_query("""
SELECT *
FROM owl_users
WHERE email LIKE '%%{%%'
""")
users = db.fetchall()
for u in users:
primary = primary_cas_email(u.email)
print(u.uid, u.full_name, u.email, '->', primary)
db_query("UPDATE owl_users SET email=%s WHERE uid=%s", (primary, u.uid))
db_connection.commit()
### Sending notifications ### ### Sending notifications ###
...@@ -1626,26 +1665,20 @@ def send_notify_post(post): ...@@ -1626,26 +1665,20 @@ def send_notify_post(post):
app.logger.info(f"E-mail: not sending self-notify to uid={dest.uid}") app.logger.info(f"E-mail: not sending self-notify to uid={dest.uid}")
def parse_emails(raw_email):
if raw_email[0] == '{' and raw_email[-1] == '}':
# Strange CAS encoding of multiple e-mail addresses
return raw_email[1:-1].split(',')
else:
return [raw_email]
def send_notify_to_dest(post, topic, course, author, target, dest): def send_notify_to_dest(post, topic, course, author, target, dest):
emails = parse_emails(dest.email)
if 'MAIL_FROM' not in app.config: if 'MAIL_FROM' not in app.config:
app.logger.info('Not sending: MAIL_FROM not defined') app.logger.info('Not sending: MAIL_FROM not defined')
return return
app.logger.info(f"E-mail: uid={dest.uid} emails={emails}") if not dest.email:
app.logger.info(f"E-mail: uid={dest.uid} has no address")
return
app.logger.info(f"E-mail: uid={dest.uid} email={dest.email}")
msg = email.message.EmailMessage() msg = email.message.EmailMessage()
msg['From'] = email.headerregistry.Address(display_name=author.full_name + " via Owl", addr_spec=app.config['MAIL_FROM']) msg['From'] = email.headerregistry.Address(display_name=author.full_name + " via Owl", addr_spec=app.config['MAIL_FROM'])
msg['To'] = [ email.headerregistry.Address(display_name=dest.full_name, addr_spec=e) for e in emails ] msg['To'] = email.headerregistry.Address(display_name=dest.full_name, addr_spec=dest.email)
msg['Subject'] = f'Owl: {topic.title}' msg['Subject'] = f'Owl: {topic.title}'
msg['Date'] = (post.modified or post.created).astimezone() msg['Date'] = (post.modified or post.created).astimezone()
if 'MAIL_REFERENCES' in app.config: if 'MAIL_REFERENCES' in app.config:
...@@ -1690,7 +1723,8 @@ def send_notify_to_dest(post, topic, course, author, target, dest): ...@@ -1690,7 +1723,8 @@ def send_notify_to_dest(post, topic, course, author, target, dest):
"/usr/sbin/sendmail", "/usr/sbin/sendmail",
"-oi", "-oi",
"-f", app.config['MAIL_FROM'], "-f", app.config['MAIL_FROM'],
] + emails, stdin=subprocess.PIPE) dest.email,
], stdin=subprocess.PIPE)
sm.communicate(msg.as_bytes()) sm.communicate(msg.as_bytes())
if sm.returncode != 0: if sm.returncode != 0:
app.logger.error("Sendmail failed with return code %d", sm.returncode) app.logger.error("Sendmail failed with return code %d", sm.returncode)
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
{{ form.csrf_token }} {{ form.csrf_token }}
<table class=settings> <table class=settings>
<tr><td>Name:<td>{{ user.full_name }} <tr><td>Name:<td>{{ user.full_name }}
<tr><td>E-mail:<td>{{ "<br>".join(emails) }} <tr><td>E-mail:<td>{{ user.email }}
<tr><td>Notify by e-mail:<td>{{ form.notify() }} <tr><td>Notify by e-mail:<td>{{ form.notify() }}
<tr><td>Notify on own posts:<td>{{ form.notify_self() }} <tr><td>Notify on own posts:<td>{{ form.notify_self() }}
<tr><td>Show PDF attachments inline:<td>{{ form.inline_att() }} <tr><td>Show PDF attachments inline:<td>{{ form.inline_att() }}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment