Compare commits

..

No commits in common. "80189580901876e62f20125859a7d64f4af9ed83" and "4a3f97ef2aceb1f237e3328e8d2a630d8a0380a9" have entirely different histories.

3 changed files with 53 additions and 85 deletions

View file

@ -9,10 +9,9 @@ A Matrix-Bot which is able to:
1. create default rooms 1. create default rooms
2. load users and their groups from matrix and ldap 2. load users and their groups from matrix and ldap
3. create rooms for all projects which end in `lab` 3. create rooms for all projects which have `sophomorixMailList=TRUE`
4. create rooms for all projects which have `sophomorixMailList=TRUE` 4. load all rooms (created by the bot) and their power levels from matrix
5. load all rooms (created by the bot) and their power levels from matrix 5. sync user memberships and power levels
6. sync user memberships and power levels
# How to use # How to use

View file

@ -35,76 +35,72 @@ class MlmMatrixBot:
async def run(self): async def run(self):
self._current_log_step = 0
logging.info("==> Sync started <==") logging.info("==> Sync started <==")
await self._create_default_rooms() await self._create_default_rooms()
current_users, all_projects = await self._load_users_and_groups() current_users, all_projects = await self._load_users_and_groups()
await self._create_project_rooms(all_projects) await self._create_project_rooms(all_projects)
await self._create_lab_rooms()
self._log_step("Loading current rooms") logging.info("= (4/5) Loading current rooms =")
matrix_rooms = await self._get_managed_rooms(with_power_levels=True) matrix_rooms = await self._get_managed_rooms(with_power_levels=True)
matrix_room_ids = list(map(lambda room: room['id'], matrix_rooms)) matrix_room_ids = list(map(lambda room: room['id'], matrix_rooms))
room_name_id_map = {} room_name_id_map = {}
room_id_map = {} room_id_map = {
self._config['MATRIX_SPACE_ID']: {
'name': "default space",
}
}
for room in matrix_rooms: for room in matrix_rooms:
room_name_id_map[room['name']] = room['id'] room_name_id_map[room['name']] = room['id']
room_id_map[room['id']] = room room_id_map[room['id']] = room
self._log_step("Syncing user memberships") logging.info("= (5/5) Syncing user memberships =")
for user in current_users: for user in current_users:
if not "mlm" in user['groups']: if not "mlm" in user['groups']:
continue continue
# default groups and space # default groups and space
rooms_to_join = self._get_default_rooms( rooms_to_join = self._get_default_rooms() + [self._config["MATRIX_SPACE_ID"]]
) + [self._config["MATRIX_SPACE_ID"]]
# projects # projects
rooms_to_join += list(map(lambda group: group.replace('p_', ''), rooms_to_join += list(map(lambda group: group.replace('p_', ''), filter(lambda project: project.startswith("p_"), user['groups'])))
filter(lambda project: project.startswith("p_"), user['groups'])))
# resolve names to ids # resolve names to ids
rooms_to_join = list(map( rooms_to_join = list(map(
lambda room: room if room.startswith( lambda room: room if room.startswith("!") else room_name_id_map[room],
"!") else room_name_id_map[room],
rooms_to_join)) rooms_to_join))
logging.info(f" \t* Syncing user {user['username']}") logging.info(f" \t* Syncing user {user['username']}")
for room_id in rooms_to_join: for room_id in rooms_to_join:
# set power level # set power level
if room_id in user['rooms']: if room_id in user['rooms']:
if ( user['matrix_username'] in room_id_map[room_id]['power_levels'] and room_id_map[room_id]['power_levels'][user['matrix_username']] != user['power_level']) or (user['matrix_username'] not in room_id_map[room_id]['power_levels'] and user['power_level'] != 0):
logging.info(f"\t\t* Setting power level in {room_id_map[room_id]['name']} to {user['power_level']}")
await self._matrixHelper.set_user_power_level_in_room(user['matrix_username'], room_id, user['power_level'])
continue continue
logging.info(f"\t\t* joining {room_id_map[room_id]['name']}") logging.info(f"\t* joining {room_id_map[room_id]['name']}")
await self._matrixHelper.add_user_to_room(user['matrix_username'], room_id) await self._matrixHelper.add_user_to_room(user['matrix_username'], room_id)
if user['power_level'] != 0:
logging.info(f"\t\t* Setting power level in {room_id_map[room_id]['name']} to {user['power_level']}")
await self._matrixHelper.set_user_power_level_in_room(user['matrix_username'], room_id, user['power_level'])
# remove from rooms that are not in the groups # remove from rooms that are not in the groups
rooms_to_be_left = list(filter( rooms_to_be_left = list(filter(lambda room: room in matrix_room_ids and not room in rooms_to_join, user['rooms']))
lambda room: room in matrix_room_ids and not room in rooms_to_join, user['rooms']))
for room_id in rooms_to_be_left: for room_id in rooms_to_be_left:
logging.info(f"\t\t* leaving {room_id_map[room_id]['name']}") logging.info(f"\t* leaving {room_id_map[room_id]['name']}")
await self._matrixHelper.remove_user_from_room( await self._matrixHelper.remove_user_from_room(
user['matrix_username'], room_id) user['matrix_username'], room_id)
# update power levels
for room in matrix_rooms:
if not room['id'] in rooms_to_join and not room['id'] in user['rooms']:
continue
if (user['matrix_username'] in room['power_levels'] and room['power_levels'][user['matrix_username']] != user['power_level']) or (user['matrix_username'] not in room['power_levels'] and user['power_level'] != 0):
logging.info(
f"\t\t* Setting power level in {room['name']} to {user['power_level']}")
await self._matrixHelper.set_user_power_level_in_room(user['matrix_username'], room['id'], user['power_level'])
logging.info("==> Sync finished <==") logging.info("==> Sync finished <==")
async def _create_default_rooms(self): async def _create_default_rooms(self):
# create default rooms # create default rooms
self._log_step("Creating default rooms") logging.info("= (1/5) Creating default rooms =")
await self._create_rooms(self._get_default_rooms()) await self._create_rooms(self._get_default_rooms())
async def _load_users_and_groups(self): async def _load_users_and_groups(self):
@ -125,7 +121,7 @@ class MlmMatrixBot:
'p_3d-printing' 'p_3d-printing'
]) ])
""" """
self._log_step("Loading users and their groups") logging.info("= (2/5) Loading users and their groups =")
matrix_users = await self._matrixHelper.get_users() matrix_users = await self._matrixHelper.get_users()
current_users = [] current_users = []
all_projects = [] all_projects = []
@ -138,17 +134,15 @@ class MlmMatrixBot:
continue continue
rc, raw_groups = self._ldapHelper.search(f"(&(member:1.2.840.113556.1.4.1941:={ldap_user[0]['distinguishedName']})(|(sophomorixType=adminclass)(sophomorixType=project)))", [ rc, raw_groups = self._ldapHelper.search(f"(&(member:1.2.840.113556.1.4.1941:={ldap_user[0]['distinguishedName']})(|(sophomorixType=adminclass)(sophomorixType=project)))", [
'sAMAccountName', 'sophomorixType', 'sophomorixMailList']) 'sAMAccountName', 'sophomorixType', 'sophomorixMailList'])
if not rc: if not rc:
continue continue
all_groups = list( all_groups = list(map(lambda group: group['sAMAccountName'], raw_groups))
map(lambda group: group['sAMAccountName'], raw_groups))
power_level = 100 if self._config['ADMIN_GROUP'] in all_groups else 50 if self._config['MODERATOR_GROUP'] in all_groups else 0 power_level = 100 if self._config['ADMIN_GROUP'] in all_groups else 50 if self._config['MODERATOR_GROUP'] in all_groups else 0
groups = list(filter(lambda group: group['sophomorixMailList'] == groups = list(filter(lambda group: group['sophomorixMailList'] == 'TRUE' or group['sophomorixType'] == "adminclass", raw_groups))
'TRUE' or group['sophomorixType'] == "adminclass", raw_groups))
all_projects.extend(list(map(lambda group: group['sAMAccountName'], filter( all_projects.extend(list(map(lambda group: group['sAMAccountName'], filter(
lambda group: 'sophomorixType' in group and group['sophomorixType'] == 'project', groups)))) lambda group: 'sophomorixType' in group and group['sophomorixType'] == 'project', groups))))
@ -169,28 +163,16 @@ class MlmMatrixBot:
return current_users, all_projects return current_users, all_projects
async def _create_project_rooms(self, projects): async def _create_project_rooms(self, projects):
self._log_step("Creating project rooms") logging.info("= (3/5) Creating project rooms =")
projects = list( projects = list(map(lambda project: project.replace('p_', ''), projects))
map(lambda project: project.replace('p_', ''), projects))
await self._create_rooms(projects) await self._create_rooms(projects)
async def _create_lab_rooms(self): async def _create_rooms(self, rooms):
self._log_step("Creating lab rooms")
rc, labs = self._ldapHelper.search(
f"(&(sophomorixType=project)(sAMAccountName=*lab))", ['sAMAccountName'])
if not rc:
return
labs = list(map(lambda lab: lab['sAMAccountName'].replace("p_", "").replace("lab", "-lab"), labs))
await self._create_rooms(labs, joinable_for_space_members=True)
async def _create_rooms(self, rooms, joinable_for_space_members=False, suggested=False):
matrix_room_names = list(map(lambda room: room['name'], await self._get_managed_rooms())) matrix_room_names = list(map(lambda room: room['name'], await self._get_managed_rooms()))
for room in rooms: for room in rooms:
if room not in matrix_room_names: if room not in matrix_room_names:
logging.info(f"* Creating room {room}") logging.info(f"* Creating room {room}")
await self._matrixHelper.create_room(room, room, joinable_for_space_members=joinable_for_space_members, suggested=True) await self._matrixHelper.create_room(room, room)
async def _get_managed_rooms(self, with_power_levels=False): async def _get_managed_rooms(self, with_power_levels=False):
matrix_rooms = await self._matrixHelper.get_rooms() matrix_rooms = await self._matrixHelper.get_rooms()
@ -202,8 +184,7 @@ class MlmMatrixBot:
if with_power_levels: if with_power_levels:
for i in range(len(matrix_rooms)): for i in range(len(matrix_rooms)):
logging.info( logging.info(f"\t* Getting power levels of room {matrix_rooms[i]['name']}")
f"\t* Getting power levels of room {matrix_rooms[i]['name']}")
matrix_rooms[i]['power_levels'] = await self._matrixHelper.get_room_power_levels(matrix_rooms[i]['id']) matrix_rooms[i]['power_levels'] = await self._matrixHelper.get_room_power_levels(matrix_rooms[i]['id'])
return matrix_rooms return matrix_rooms
@ -211,10 +192,6 @@ class MlmMatrixBot:
def _get_default_rooms(self): def _get_default_rooms(self):
return self._config['DEFAULT_ROOMS'].split(',') return self._config['DEFAULT_ROOMS'].split(',')
def _log_step(self, message):
logging.info(f"= ({self._current_log_step}/6) {message} =")
self._current_log_step += 1
def _readConfig(self): def _readConfig(self):
requiredConfigKeys = [ requiredConfigKeys = [
'MATRIX_BOT_LDAP_URI', 'MATRIX_BOT_LDAP_URI',
@ -239,8 +216,7 @@ class MlmMatrixBot:
for configKey in requiredConfigKeys: for configKey in requiredConfigKeys:
if configKey not in os.environ: if configKey not in os.environ:
logging.error( logging.error(f"Required environment value {configKey} is not set")
f"Required environment value {configKey} is not set")
sys.exit() sys.exit()
config[configKey.replace('MATRIX_BOT_', '') config[configKey.replace('MATRIX_BOT_', '')
] = os.environ[configKey] ] = os.environ[configKey]

View file

@ -57,16 +57,27 @@ class MatrixHelper:
async def logout(self): async def logout(self):
await self._client.close() await self._client.close()
async def create_room(self, name, alias, joinable_for_space_members=False, suggested=False): async def create_room(self, name, alias, joinable_for_space_members=False):
# we not only need to create the room but also have to add it to the space # we not only need to create the room but also have to add it to the space
via_domain = self._space_id.split(":")[1]
initial_state = [ initial_state = [
{ {
"type": "m.space.parent", "type": "m.space.parent",
"state_key": self._space_id, "state_key": self._space_id,
"content": { "content": {
"canonical": True, "canonical": True,
"via": [via_domain], "via": [self._space_id.split(":")[1]],
}
},
{
"type": "m.room.join_rules",
"content": {
"join_rule": "restricted",
"allow": [
{
"type": "m.room_membership",
"room_id": self._space_id
}
]
} }
}, },
{ {
@ -84,33 +95,15 @@ class MatrixHelper:
] ]
if joinable_for_space_members: if joinable_for_space_members:
initial_state.insert(0, { initial_state.append({
"type": "m.room.guest_access", "type": "m.room.guest_access",
"state_key": "", "state_key": "",
"content": {"guest_access": "can_join"} "content": {"guest_access": "can_join"}
}) })
initial_state.insert(2, {
"type": "m.room.join_rules",
"content": {
"join_rule": "restricted",
"allow": [
{
"type": "m.room_membership",
"room_id": self._space_id
}
]
}
})
room = await self._client.room_create(visibility=nio.RoomVisibility.private, name=name, alias=alias, federate=False, initial_state=initial_state) room = await self._client.room_create(visibility=nio.RoomVisibility.private, name=name, alias=alias, federate=False, initial_state=initial_state)
assert room.room_id is not None
# add as child room assert room.room_id is not None
state_update = await self._client.room_put_state(self._space_id, "m.space.child", {
"suggested": suggested,
"via": [via_domain],
}, state_key=room.room_id)
assert state_update.event_id is not None
return room return room