use ConfigParser
This commit is contained in:
parent
4a312378b7
commit
baeb83b1e2
|
@ -1,18 +1,37 @@
|
||||||
class UserNotFoundError(Exception):
|
class UserNotFoundError(Exception):
|
||||||
pass
|
def __init__(self):
|
||||||
|
super().__init__('user not found')
|
||||||
|
|
||||||
|
|
||||||
class GroupNotFoundError(Exception):
|
class GroupNotFoundError(Exception):
|
||||||
pass
|
def __init__(self):
|
||||||
|
super().__init__('group not found')
|
||||||
|
|
||||||
|
|
||||||
class BadRequest(Exception):
|
class BadRequest(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UserAlreadyExistsError(Exception):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__('user already exists')
|
||||||
|
|
||||||
|
|
||||||
|
class GroupAlreadyExistsError(Exception):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__('group already exists')
|
||||||
|
|
||||||
|
|
||||||
class UserAlreadySubscribedError(Exception):
|
class UserAlreadySubscribedError(Exception):
|
||||||
pass
|
def __init__(self):
|
||||||
|
super().__init__('user is already subscribed')
|
||||||
|
|
||||||
|
|
||||||
class UserNotSubscribedError(Exception):
|
class UserNotSubscribedError(Exception):
|
||||||
pass
|
def __init__(self):
|
||||||
|
super().__init__('user is not subscribed')
|
||||||
|
|
||||||
|
|
||||||
|
class NoSuchListError(Exception):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__('mailing list does not exist')
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
from configparser import ConfigParser
|
||||||
|
import os
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from zope.interface import implementer
|
from zope.interface import implementer
|
||||||
|
|
||||||
from ceo_common.interfaces import IConfig
|
from ceo_common.interfaces import IConfig
|
||||||
|
@ -5,39 +9,14 @@ from ceo_common.interfaces import IConfig
|
||||||
|
|
||||||
@implementer(IConfig)
|
@implementer(IConfig)
|
||||||
class Config:
|
class Config:
|
||||||
# TODO: read from a config file
|
def __init__(self, config_file: Union[str, None] = None):
|
||||||
_domain = 'csclub.internal'
|
if config_file is None:
|
||||||
_ldap_base = ','.join(['dc=' + dc for dc in _domain.split('.')])
|
config_file = os.environ.get('CEOD_CONFIG', '/etc/csc/ceod.ini')
|
||||||
_config = {
|
self.config = ConfigParser()
|
||||||
'base_domain': _domain,
|
self.config.read(config_file)
|
||||||
'ldap_admin_principal': 'ceod/admin',
|
|
||||||
'ldap_server_url': 'ldap://ldap-master.' + _domain,
|
|
||||||
'ldap_users_base': 'ou=People,' + _ldap_base,
|
|
||||||
'ldap_groups_base': 'ou=Group,' + _ldap_base,
|
|
||||||
'ldap_sudo_base': 'ou=SUDOers,' + _ldap_base,
|
|
||||||
'ldap_sasl_realm': _domain.upper(),
|
|
||||||
'uwldap_server_url': 'ldap://uwldap.uwaterloo.ca',
|
|
||||||
'uwldap_base': 'dc=uwaterloo,dc=ca',
|
|
||||||
'member_min_id': 20001,
|
|
||||||
'member_max_id': 29999,
|
|
||||||
'club_min_id': 30001,
|
|
||||||
'club_max_id': 39999,
|
|
||||||
'member_home': '/users',
|
|
||||||
'club_home': '/users',
|
|
||||||
'member_home_skel': '/users/skel',
|
|
||||||
'club_home_skel': '/users/skel',
|
|
||||||
'smtp_url': 'smtp://mail.' + _domain,
|
|
||||||
'smtp_starttls': False,
|
|
||||||
'mailman3_api_base_url': 'http://localhost:8001/3.1',
|
|
||||||
'mailman3_api_username': 'restadmin',
|
|
||||||
'mailman3_api_password': 'mailman3',
|
|
||||||
'new_member_list': 'csc-general',
|
|
||||||
'ceod_admin_host': 'phosphoric-acid',
|
|
||||||
'fs_root_host': 'phosphoric-acid',
|
|
||||||
'mailman_host': 'mail',
|
|
||||||
'ceod_use_https': False,
|
|
||||||
'ceod_port': 9987,
|
|
||||||
}
|
|
||||||
|
|
||||||
def get(self, key: str) -> str:
|
def get(self, key: str) -> str:
|
||||||
return self._config[key]
|
section, subkey = key.split('_', 1)
|
||||||
|
if section in self.config:
|
||||||
|
return self.config[section][subkey]
|
||||||
|
return self.config['DEFAULT'][key]
|
||||||
|
|
|
@ -20,6 +20,7 @@ class HTTPClient:
|
||||||
else:
|
else:
|
||||||
self.scheme = 'http'
|
self.scheme = 'http'
|
||||||
self.ceod_port = cfg.get('ceod_port')
|
self.ceod_port = cfg.get('ceod_port')
|
||||||
|
self.base_domain = cfg.get('base_domain')
|
||||||
|
|
||||||
# Determine which principal to use for SPNEGO
|
# Determine which principal to use for SPNEGO
|
||||||
# TODO: this code is duplicated in app_factory.py. Figure out
|
# TODO: this code is duplicated in app_factory.py. Figure out
|
||||||
|
@ -51,6 +52,9 @@ class HTTPClient:
|
||||||
target_name='ceod',
|
target_name='ceod',
|
||||||
creds=self.get_creds(),
|
creds=self.get_creds(),
|
||||||
)
|
)
|
||||||
|
# always use the FQDN, for HTTPS purposes
|
||||||
|
if '.' not in host:
|
||||||
|
host = host + '.' + self.base_domain
|
||||||
return requests.request(
|
return requests.request(
|
||||||
method,
|
method,
|
||||||
f'{self.scheme}://{host}:{self.ceod_port}{api_path}',
|
f'{self.scheme}://{host}:{self.ceod_port}{api_path}',
|
||||||
|
|
|
@ -8,7 +8,7 @@ from ..interfaces import IMailmanService, IConfig, IHTTPClient
|
||||||
class RemoteMailmanService:
|
class RemoteMailmanService:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
cfg = component.getUtility(IConfig)
|
cfg = component.getUtility(IConfig)
|
||||||
self.mailman_host = cfg.get('mailman_host')
|
self.mailman_host = cfg.get('ceod_mailman_host')
|
||||||
self.http_client = component.getUtility(IHTTPClient)
|
self.http_client = component.getUtility(IHTTPClient)
|
||||||
|
|
||||||
def subscribe(self, address: str, mailing_list: str):
|
def subscribe(self, address: str, mailing_list: str):
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
[DEFAULT]
|
||||||
|
base_domain = csclub.internal
|
||||||
|
|
||||||
|
[ceod]
|
||||||
|
# this is the host with the ceod/admin Kerberos key
|
||||||
|
admin_host = phosphoric-acid
|
||||||
|
# this is the host with NFS no_root_squash
|
||||||
|
fs_root_host = phosphoric-acid
|
||||||
|
mailman_host = mail
|
||||||
|
use_https = false
|
||||||
|
port = 9987
|
||||||
|
|
||||||
|
[ldap]
|
||||||
|
admin_principal = ceod/admin
|
||||||
|
server_url = ldap://ldap-master.csclub.internal
|
||||||
|
sasl_realm = CSCLUB.INTERNAL
|
||||||
|
users_base = ou=People,dc=csclub,dc=internal
|
||||||
|
groups_base = ou=Group,dc=csclub,dc=internal
|
||||||
|
sudo_base = ou=SUDOers,dc=csclub,dc=internal
|
||||||
|
|
||||||
|
[uwldap]
|
||||||
|
server_url = ldap://uwldap.uwaterloo.ca
|
||||||
|
base = dc=uwaterloo,dc=ca
|
||||||
|
|
||||||
|
[members]
|
||||||
|
min_id = 20001
|
||||||
|
max_id = 29999
|
||||||
|
home = /users
|
||||||
|
skel = /users/skel
|
||||||
|
|
||||||
|
[clubs]
|
||||||
|
min_id = 30001
|
||||||
|
max_id = 39999
|
||||||
|
home = /users
|
||||||
|
skel = /users/skel
|
||||||
|
|
||||||
|
[mail]
|
||||||
|
smtp_url = smtp://mail.csclub.internal
|
||||||
|
smtp_starttls = false
|
||||||
|
|
||||||
|
[mailman3]
|
||||||
|
api_base_url = http://localhost:8001/3.1
|
||||||
|
api_username = restadmin
|
||||||
|
api_password = mailman3
|
||||||
|
new_member_list = csc-general
|
|
@ -1,9 +1,12 @@
|
||||||
|
import importlib.resources
|
||||||
|
import os
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask_kerberos import init_kerberos
|
from flask_kerberos import init_kerberos
|
||||||
from zope import component
|
from zope import component
|
||||||
|
|
||||||
|
from .error_handlers import register_error_handlers
|
||||||
from ceo_common.interfaces import IConfig, IKerberosService, ILDAPService, IFileService, \
|
from ceo_common.interfaces import IConfig, IKerberosService, ILDAPService, IFileService, \
|
||||||
IMailmanService, IMailService, IUWLDAPService, IHTTPClient
|
IMailmanService, IMailService, IUWLDAPService, IHTTPClient
|
||||||
from ceo_common.model import Config, HTTPClient, RemoteMailmanService
|
from ceo_common.model import Config, HTTPClient, RemoteMailmanService
|
||||||
|
@ -15,11 +18,12 @@ def create_app(flask_config={}):
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.from_mapping(flask_config)
|
app.config.from_mapping(flask_config)
|
||||||
|
|
||||||
if app.config.get('TESTING') or app.config.get('ENV') == 'development':
|
if app.config.get('ENV') == 'development' and 'CEOD_CONFIG' not in os.environ:
|
||||||
# TODO: create test config class
|
with importlib.resources.path('ceo_common.test', 'ceod_dev.ini') as p:
|
||||||
cfg = Config()
|
config_file = p.__fspath__()
|
||||||
else:
|
else:
|
||||||
cfg = Config()
|
config_file = None
|
||||||
|
cfg = Config(config_file)
|
||||||
component.provideUtility(cfg, IConfig)
|
component.provideUtility(cfg, IConfig)
|
||||||
|
|
||||||
init_kerberos(app, service='ceod')
|
init_kerberos(app, service='ceod')
|
||||||
|
@ -43,13 +47,15 @@ def create_app(flask_config={}):
|
||||||
http_client = HTTPClient()
|
http_client = HTTPClient()
|
||||||
component.provideUtility(http_client, IHTTPClient)
|
component.provideUtility(http_client, IHTTPClient)
|
||||||
|
|
||||||
# Only instantiate FileService is this host has NFS no_root_squash
|
# Only instantiate FileService if this host has NFS no_root_squash
|
||||||
if hostname == cfg.get('fs_root_host'):
|
# If admin_host and fs_root_host become separate, we will need
|
||||||
|
# to create a RemoteFileService
|
||||||
|
if hostname == cfg.get('ceod_fs_root_host'):
|
||||||
file_srv = FileService()
|
file_srv = FileService()
|
||||||
component.provideUtility(file_srv, IFileService)
|
component.provideUtility(file_srv, IFileService)
|
||||||
|
|
||||||
# Only offer mailman API if this host is running Mailman
|
# Only offer mailman API if this host is running Mailman
|
||||||
if hostname == cfg.get('mailman_host'):
|
if hostname == cfg.get('ceod_mailman_host'):
|
||||||
mailman_srv = MailmanService()
|
mailman_srv = MailmanService()
|
||||||
component.provideUtility(mailman_srv, IMailmanService)
|
component.provideUtility(mailman_srv, IMailmanService)
|
||||||
|
|
||||||
|
@ -68,6 +74,8 @@ def create_app(flask_config={}):
|
||||||
from ceod.api import uwldap
|
from ceod.api import uwldap
|
||||||
app.register_blueprint(uwldap.bp, url_prefix='/api/uwldap')
|
app.register_blueprint(uwldap.bp, url_prefix='/api/uwldap')
|
||||||
|
|
||||||
|
register_error_handlers(app)
|
||||||
|
|
||||||
@app.route('/ping')
|
@app.route('/ping')
|
||||||
def ping():
|
def ping():
|
||||||
"""Health check"""
|
"""Health check"""
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from flask.app import Flask
|
||||||
|
from werkzeug.exceptions import HTTPException
|
||||||
|
|
||||||
|
from ceo_common.logger_factory import logger_factory
|
||||||
|
|
||||||
|
__all__ = ['register_error_handlers']
|
||||||
|
logger = logger_factory(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def register_error_handlers(app: Flask):
|
||||||
|
"""Register error handlers for the application."""
|
||||||
|
app.register_error_handler(Exception, generic_error_handler)
|
||||||
|
|
||||||
|
|
||||||
|
def generic_error_handler(err: Exception):
|
||||||
|
"""Return JSON for internal server errors."""
|
||||||
|
# pass through HTTP errors
|
||||||
|
if isinstance(err, HTTPException):
|
||||||
|
return err
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return {'error': type(err).__name__ + ': ' + str(err)}, 500
|
|
@ -2,7 +2,8 @@ from flask import Blueprint
|
||||||
from zope import component
|
from zope import component
|
||||||
|
|
||||||
from .utils import authz_restrict_to_staff
|
from .utils import authz_restrict_to_staff
|
||||||
from ceo_common.errors import UserAlreadySubscribedError, UserNotSubscribedError
|
from ceo_common.errors import UserAlreadySubscribedError, UserNotSubscribedError, \
|
||||||
|
NoSuchListError
|
||||||
from ceo_common.interfaces import IMailmanService
|
from ceo_common.interfaces import IMailmanService
|
||||||
|
|
||||||
bp = Blueprint('mailman', __name__)
|
bp = Blueprint('mailman', __name__)
|
||||||
|
@ -16,6 +17,8 @@ def subscribe(mailing_list, username):
|
||||||
mailman_srv.subscribe(username, mailing_list)
|
mailman_srv.subscribe(username, mailing_list)
|
||||||
except UserAlreadySubscribedError as err:
|
except UserAlreadySubscribedError as err:
|
||||||
return {'error': str(err)}, 409
|
return {'error': str(err)}, 409
|
||||||
|
except NoSuchListError as err:
|
||||||
|
return {'error': str(err)}, 404
|
||||||
return {'result': 'OK'}
|
return {'result': 'OK'}
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,6 +28,6 @@ def unsubscribe(mailing_list, username):
|
||||||
mailman_srv = component.getUtility(IMailmanService)
|
mailman_srv = component.getUtility(IMailmanService)
|
||||||
try:
|
try:
|
||||||
mailman_srv.unsubscribe(username, mailing_list)
|
mailman_srv.unsubscribe(username, mailing_list)
|
||||||
except UserNotSubscribedError as err:
|
except (UserNotSubscribedError, NoSuchListError) as err:
|
||||||
return {'error': str(err)}, 404
|
return {'error': str(err)}, 404
|
||||||
return {'result': 'OK'}
|
return {'result': 'OK'}
|
||||||
|
|
|
@ -39,7 +39,8 @@ def get_user(auth_user: str, username: str):
|
||||||
get_forwarding_addresses = True
|
get_forwarding_addresses = True
|
||||||
ldap_srv = component.getUtility(ILDAPService)
|
ldap_srv = component.getUtility(ILDAPService)
|
||||||
try:
|
try:
|
||||||
return ldap_srv.get_user(username).to_dict(get_forwarding_addresses)
|
user = ldap_srv.get_user(username)
|
||||||
|
return user.to_dict(get_forwarding_addresses)
|
||||||
except UserNotFoundError:
|
except UserNotFoundError:
|
||||||
return {
|
return {
|
||||||
'error': 'user not found'
|
'error': 'user not found'
|
||||||
|
|
|
@ -13,8 +13,8 @@ from ceo_common.interfaces import IFileService, IConfig, IUser
|
||||||
class FileService:
|
class FileService:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
cfg = component.getUtility(IConfig)
|
cfg = component.getUtility(IConfig)
|
||||||
self.member_home_skel = cfg.get('member_home_skel')
|
self.member_home_skel = cfg.get('members_skel')
|
||||||
self.club_home_skel = cfg.get('club_home_skel')
|
self.club_home_skel = cfg.get('clubs_skel')
|
||||||
|
|
||||||
def create_home_dir(self, user: IUser):
|
def create_home_dir(self, user: IUser):
|
||||||
if user.is_club():
|
if user.is_club():
|
||||||
|
|
|
@ -8,7 +8,8 @@ import ldap.modlist
|
||||||
from zope import component
|
from zope import component
|
||||||
from zope.interface import implementer
|
from zope.interface import implementer
|
||||||
|
|
||||||
from ceo_common.errors import UserNotFoundError, GroupNotFoundError
|
from ceo_common.errors import UserNotFoundError, GroupNotFoundError, \
|
||||||
|
UserAlreadyExistsError, GroupAlreadyExistsError
|
||||||
from ceo_common.interfaces import ILDAPService, IKerberosService, IConfig, \
|
from ceo_common.interfaces import ILDAPService, IKerberosService, IConfig, \
|
||||||
IUser, IGroup, IUWLDAPService
|
IUser, IGroup, IUWLDAPService
|
||||||
from .User import User
|
from .User import User
|
||||||
|
@ -25,10 +26,10 @@ class LDAPService:
|
||||||
self.ldap_server_url = cfg.get('ldap_server_url')
|
self.ldap_server_url = cfg.get('ldap_server_url')
|
||||||
self.ldap_users_base = cfg.get('ldap_users_base')
|
self.ldap_users_base = cfg.get('ldap_users_base')
|
||||||
self.ldap_groups_base = cfg.get('ldap_groups_base')
|
self.ldap_groups_base = cfg.get('ldap_groups_base')
|
||||||
self.member_min_id = cfg.get('member_min_id')
|
self.member_min_id = cfg.get('members_min_id')
|
||||||
self.member_max_id = cfg.get('member_max_id')
|
self.member_max_id = cfg.get('members_max_id')
|
||||||
self.club_min_id = cfg.get('club_min_id')
|
self.club_min_id = cfg.get('clubs_min_id')
|
||||||
self.club_max_id = cfg.get('club_max_id')
|
self.club_max_id = cfg.get('clubs_max_id')
|
||||||
|
|
||||||
def _get_ldap_conn(self, gssapi_bind: bool = True) -> ldap.ldapobject.LDAPObject:
|
def _get_ldap_conn(self, gssapi_bind: bool = True) -> ldap.ldapobject.LDAPObject:
|
||||||
# TODO: cache the connection
|
# TODO: cache the connection
|
||||||
|
@ -126,8 +127,10 @@ class LDAPService:
|
||||||
new_user.gid_number = uid_number
|
new_user.gid_number = uid_number
|
||||||
|
|
||||||
modlist = ldap.modlist.addModlist(new_user.serialize_for_ldap())
|
modlist = ldap.modlist.addModlist(new_user.serialize_for_ldap())
|
||||||
conn.add_s(new_user.dn, modlist)
|
try:
|
||||||
|
conn.add_s(new_user.dn, modlist)
|
||||||
|
except ldap.ALREADY_EXISTS:
|
||||||
|
raise UserAlreadyExistsError()
|
||||||
return new_user
|
return new_user
|
||||||
|
|
||||||
def modify_user(self, old_user: IUser, new_user: IUser):
|
def modify_user(self, old_user: IUser, new_user: IUser):
|
||||||
|
@ -147,7 +150,10 @@ class LDAPService:
|
||||||
# make sure that the caller initialized the GID number
|
# make sure that the caller initialized the GID number
|
||||||
assert group.gid_number
|
assert group.gid_number
|
||||||
modlist = ldap.modlist.addModlist(group.serialize_for_ldap())
|
modlist = ldap.modlist.addModlist(group.serialize_for_ldap())
|
||||||
conn.add_s(group.dn, modlist)
|
try:
|
||||||
|
conn.add_s(group.dn, modlist)
|
||||||
|
except ldap.ALREADY_EXISTS:
|
||||||
|
raise GroupAlreadyExistsError()
|
||||||
return group
|
return group
|
||||||
|
|
||||||
def modify_group(self, old_group: IGroup, new_group: IGroup):
|
def modify_group(self, old_group: IGroup, new_group: IGroup):
|
||||||
|
|
|
@ -17,14 +17,14 @@ smtp_url_re = re.compile(r'^(?P<scheme>smtps?)://(?P<host>[\w.-]+)(:(?P<port>\d+
|
||||||
class MailService:
|
class MailService:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
cfg = component.getUtility(IConfig)
|
cfg = component.getUtility(IConfig)
|
||||||
smtp_url = cfg.get('smtp_url')
|
smtp_url = cfg.get('mail_smtp_url')
|
||||||
match = smtp_url_re.match(smtp_url)
|
match = smtp_url_re.match(smtp_url)
|
||||||
if match is None:
|
if match is None:
|
||||||
raise Exception('Invalid SMTP URL: %s' % smtp_url)
|
raise Exception('Invalid SMTP URL: %s' % smtp_url)
|
||||||
self.smtps = match.group('scheme') == 'smtps'
|
self.smtps = match.group('scheme') == 'smtps'
|
||||||
self.host = match.group('host')
|
self.host = match.group('host')
|
||||||
self.port = int(match.group('port') or 25)
|
self.port = int(match.group('port') or 25)
|
||||||
self.starttls = cfg.get('smtp_starttls')
|
self.starttls = cfg.get('mail_smtp_starttls')
|
||||||
assert not (self.smtps and self.starttls)
|
assert not (self.smtps and self.starttls)
|
||||||
self.base_domain = cfg.get('base_domain')
|
self.base_domain = cfg.get('base_domain')
|
||||||
self.jinja_env = jinja2.Environment(
|
self.jinja_env = jinja2.Environment(
|
||||||
|
|
|
@ -3,7 +3,8 @@ from requests.auth import HTTPBasicAuth
|
||||||
from zope import component
|
from zope import component
|
||||||
from zope.interface import implementer
|
from zope.interface import implementer
|
||||||
|
|
||||||
from ceo_common.errors import UserAlreadySubscribedError, UserNotSubscribedError
|
from ceo_common.errors import UserAlreadySubscribedError, UserNotSubscribedError, \
|
||||||
|
NoSuchListError
|
||||||
from ceo_common.interfaces import IMailmanService, IConfig
|
from ceo_common.interfaces import IMailmanService, IConfig
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,8 +14,9 @@ class MailmanService:
|
||||||
cfg = component.getUtility(IConfig)
|
cfg = component.getUtility(IConfig)
|
||||||
self.base_domain = cfg.get('base_domain')
|
self.base_domain = cfg.get('base_domain')
|
||||||
self.api_base_url = cfg.get('mailman3_api_base_url')
|
self.api_base_url = cfg.get('mailman3_api_base_url')
|
||||||
self.api_username = cfg.get('mailman3_api_username')
|
api_username = cfg.get('mailman3_api_username')
|
||||||
self.api_password = cfg.get('mailman3_api_password')
|
api_password = cfg.get('mailman3_api_password')
|
||||||
|
self.basic_auth = HTTPBasicAuth(api_username, api_password)
|
||||||
|
|
||||||
def subscribe(self, address: str, mailing_list: str):
|
def subscribe(self, address: str, mailing_list: str):
|
||||||
if '@' in mailing_list:
|
if '@' in mailing_list:
|
||||||
|
@ -31,11 +33,15 @@ class MailmanService:
|
||||||
'pre_confirmed': 'True',
|
'pre_confirmed': 'True',
|
||||||
'pre_approved': 'True',
|
'pre_approved': 'True',
|
||||||
},
|
},
|
||||||
auth=HTTPBasicAuth(self.api_username, self.api_password),
|
auth=self.basic_auth,
|
||||||
)
|
)
|
||||||
if resp.status_code == 409:
|
if not resp.ok:
|
||||||
raise UserAlreadySubscribedError(resp.json()['description'])
|
desc = resp.json().get('description')
|
||||||
resp.raise_for_status()
|
if resp.status_code == 409:
|
||||||
|
raise UserAlreadySubscribedError()
|
||||||
|
elif resp.status_code == 400 and desc == 'No such list':
|
||||||
|
raise NoSuchListError()
|
||||||
|
raise Exception(desc)
|
||||||
|
|
||||||
def unsubscribe(self, address: str, mailing_list: str):
|
def unsubscribe(self, address: str, mailing_list: str):
|
||||||
if '@' not in mailing_list:
|
if '@' not in mailing_list:
|
||||||
|
@ -49,8 +55,12 @@ class MailmanService:
|
||||||
'pre_approved': 'True',
|
'pre_approved': 'True',
|
||||||
'pre_confirmed': 'True',
|
'pre_confirmed': 'True',
|
||||||
},
|
},
|
||||||
auth=HTTPBasicAuth(self.api_username, self.api_password),
|
auth=self.basic_auth,
|
||||||
)
|
)
|
||||||
if resp.status_code == 404:
|
if not resp.ok:
|
||||||
raise UserNotSubscribedError('user is not subscribed')
|
desc = resp.json().get('description')
|
||||||
resp.raise_for_status()
|
if resp.status_code == 404:
|
||||||
|
# Unfortunately, a 404 here could mean either the list doesn't
|
||||||
|
# exist, or the member isn't subscribed
|
||||||
|
raise UserNotSubscribedError()
|
||||||
|
raise Exception(desc)
|
||||||
|
|
|
@ -41,9 +41,9 @@ class User:
|
||||||
self.gid_number = gid_number
|
self.gid_number = gid_number
|
||||||
if home_directory is None:
|
if home_directory is None:
|
||||||
if is_club:
|
if is_club:
|
||||||
home_parent = cfg.get('member_home')
|
home_parent = cfg.get('members_home')
|
||||||
else:
|
else:
|
||||||
home_parent = cfg.get('club_home')
|
home_parent = cfg.get('clubs_home')
|
||||||
self.home_directory = os.path.join(home_parent, uid)
|
self.home_directory = os.path.join(home_parent, uid)
|
||||||
else:
|
else:
|
||||||
self.home_directory = home_directory
|
self.home_directory = home_directory
|
||||||
|
|
Loading…
Reference in New Issue