Compare commits

...

4 Commits

Author SHA1 Message Date
radex bcc23d5055
admin: add "protected" callouts 2023-11-23 17:54:50 +01:00
radex 365d404438
config: add protected groups + clean up 2023-11-23 17:41:28 +01:00
radex 2816cb245d
Improve README 2023-11-23 17:35:52 +01:00
radex ed7a028196
Add papertrail (email alerts) module 2023-11-23 17:26:12 +01:00
6 changed files with 109 additions and 15 deletions

View File

@ -1,6 +1,23 @@
# ldap-web aka profile.hackerspace.pl
# LDAPWeb aka profile.hackerspace.pl
## quick start locally
## Configuration
No configuration is required to see and change your own profile and password.
To test admin pages, notifications, and avatars, you need to configure these environment variables:
- `LDAPWEB_ADMIN_DN` - LDAP user DN, e.g. `uid=radex,ou=people,dc=hackerspace,dc=pl`
- `LDAPWEB_ADMIN_PASSWORD` - password to the above LDAP user
- `LDAPWEB_SMTP_USER` - username with a `@hackerspace.pl` mailbox, e.g. `radex`
- `LDAPWEB_SMTP_PASSWORD` - password to the above mailbox
- `LDAPWEB_ADMIN_GROUPS` - comma-separated LDAP groups allowed to see admin pages (note that every user has a group with the same name), e.g. `radex,zarzad`
- `LDAPWEB_PAPERTRAIL_RECIPIENTS` - comma-separated email addresses that will get email alerts for changes done via admin UI
See `webapp/config.py` for more details.
(Note that you don't need to be an LDAP admin to be able to hack on this - you just won't see all attributes, and won't be able to change other people's profiles)
## Quick Start
```
poetry install
@ -11,7 +28,7 @@ Open app at localhost:5001
To test changing password locally, you must add HACKERSPACE.PL realm to your /etc/krb5.conf. See krb5.conf for an example
## quick start (dockerized)
## Quick Start (Dockerized)
```
docker build -t ldapweb .
@ -20,6 +37,6 @@ docker run -p 8000:8000 ldapweb
Open app at localhost:8000
## deployment
## Deployment
Look for `ldapweb.libsonnet` in hscloud repo
Build Docker imagine manually and bump https://code.hackerspace.pl/hswaw/hscloud/src/branch/master/hswaw/ldapweb/prod.jsonnet

View File

@ -101,7 +101,9 @@ def admin_user_view(uid):
profile = _get_profile(conn, uid)
groups = _get_groups_of(conn, uid)
return flask.render_template('admin/user.html', uid=uid, profile=_format_profile(profile), groups=groups)
is_protected = any(group in config.ldap_protected_groups for group in groups)
return flask.render_template('admin/user.html', uid=uid, profile=_format_profile(profile), groups=groups, is_protected=is_protected)
@bp.route('/admin/groups/')
@admin_required
@ -137,4 +139,6 @@ def admin_group_view(name):
group_attrs = _get_group(conn, name)
members = _get_user_list(conn, f'memberOf={ldaputils.group_dn(name)}')
return flask.render_template('admin/group.html', name=name, attributes=_format_profile(group_attrs), members=members)
is_protected = name in config.ldap_protected_groups
return flask.render_template('admin/group.html', name=name, attributes=_format_profile(group_attrs), members=members, is_protected=is_protected)

View File

@ -7,28 +7,41 @@ hackerspace_name = 'Warsaw Hackerspace'
secret_key = secrets.token_hex(32)
# Kerberos configuration
kadmin_principal_map = "{}@HACKERSPACE.PL"
# LDAP configuration
ldap_url = 'ldap://ldap.hackerspace.pl'
ldap_base = 'dc=hackerspace,dc=pl'
ldap_people = 'ou=people,dc=hackerspace,dc=pl'
ldap_user_dn_format = 'uid={},ou=people,dc=hackerspace,dc=pl'
ldap_group_dn_format = 'cn={},ou=group,dc=hackerspace,dc=pl'
# user groups allowed to see /admin
# LDAP user groups allowed to see /admin
ldap_admin_groups = os.getenv('LDAPWEB_ADMIN_GROUPS', 'ldap-admin,staff,zarzad').split(',')
# user groups indicating that a user is active
# LDAP user groups indicating that a user is active
ldap_active_groups = os.getenv('LDAPWEB_ACTIVE_GROUPS', 'fatty,starving,potato').split(',')
# service user with admin privileges (for admin listings, creating new users)
# LDAP service user with admin privileges (for admin listings, creating new users)
ldap_admin_dn = os.getenv('LDAPWEB_ADMIN_DN', 'cn=ldapweb,ou=services,dc=hackerspace,dc=pl')
ldap_admin_password = os.getenv('LDAPWEB_ADMIN_PASSWORD', 'unused')
# avatar server
# Protected LDAP user groups
# These groups (and their members) cannot be modified by admin UI
ldap_protected_groups = (
'staff,zarzad,rewizja,ldap-admin'.split(',') +
os.getenv('LDAPWEB_PROTECTED_GROUPS', '').split(',')
)
# Email notification (paper trail) configuration
smtp_server = 'mail.hackerspace.pl'
smtp_format = '{}@hackerspace.pl'
smtp_user = os.getenv('LDAPWEB_SMTP_USER', 'ldapweb')
smtp_password = os.getenv('LDAPWEB_SMTP_PASSWORD', 'unused')
papertrail_recipients = os.getenv('LDAPWEB_PAPERTRAIL_RECIPIENTS', 'zarzad@hackerspace.pl')
# Avatar server
avatar_cache_timeout = int(os.getenv('LDAPWEB_AVATAR_CACHE_TIMEOUT', '1800'))
# LDAP attribute configuration

50
webapp/email.py Normal file
View File

@ -0,0 +1,50 @@
import smtplib
from email.message import EmailMessage
import flask
import datetime
from webapp import config, context
cached_connection = None
def test_connection_open(conn):
try:
status = conn.noop()[0]
except:
status = -1
return True if status == 250 else False
def create_connection():
conn = smtplib.SMTP_SSL(config.smtp_server)
conn.login(config.smtp_user, config.smtp_password)
return conn
def get_connection():
global cached_connection
if test_connection_open(cached_connection):
return cached_connection
cached_connection = create_connection()
return cached_connection
def send_email(conn, subject, body, recipient_emails):
msg = EmailMessage()
msg.set_content(body)
msg['Subject'] = subject
sender_email = config.smtp_format.format(config.smtp_user)
msg['From'] = f'LDAPWeb <{sender_email}>'
msg['To'] = recipient_emails
conn.send_message(msg)
def send_papertrail(title, description):
username = flask.session.get('username')
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
subject = f'[LDAPWeb] {title}'
body = f"Changed by {username} at {current_time}:\n\n{description or title}"
recipients = config.papertrail_recipients
conn = get_connection()
send_email(conn, subject, body, recipients)

View File

@ -13,7 +13,12 @@
{% endfor %}
</p>
<p>Full LDAP record:</p>
<p>
{% if is_protected %}
<span class="label label-danger" title="You cant modify this group because it is protected">Protected group</span>
{% endif %}
Full LDAP record:
</p>
<table class="table profile-table">
<tr>

View File

@ -17,7 +17,12 @@
{% endfor %}
</p>
<p>Full LDAP record:</p>
<p>
{% if is_protected %}
<span class="label label-danger" title="You cant modify this user because it belongs to a protected group">Protected user</span>
{% endif %}
Full LDAP record:
</p>
<table class="table profile-table">
<tr>