Compare commits

...

2 commits

Author SHA1 Message Date
8018958090
Feat: create rooms for labs
Some checks are pending
ci/woodpecker/push/woodpecker Pipeline is pending
2022-09-29 22:13:09 +02:00
f9bd2a46a1
Chore: refactor power_level sync 2022-09-29 21:31:07 +02:00
3 changed files with 85 additions and 53 deletions

View file

@ -9,9 +9,10 @@ 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 have `sophomorixMailList=TRUE` 3. create rooms for all projects which end in `lab`
4. load all rooms (created by the bot) and their power levels from matrix 4. create rooms for all projects which have `sophomorixMailList=TRUE`
5. sync user memberships and power levels 5. load all rooms (created by the bot) and their power levels from matrix
6. sync user memberships and power levels
# How to use # How to use

View file

@ -35,72 +35,76 @@ 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()
logging.info("= (4/5) Loading current rooms =") self._log_step("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
logging.info("= (5/5) Syncing user memberships =") self._log_step("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() + [self._config["MATRIX_SPACE_ID"]] rooms_to_join = self._get_default_rooms(
) + [self._config["MATRIX_SPACE_ID"]]
# projects # projects
rooms_to_join += list(map(lambda group: group.replace('p_', ''), filter(lambda project: project.startswith("p_"), user['groups']))) rooms_to_join += list(map(lambda group: group.replace('p_', ''),
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("!") else room_name_id_map[room], lambda room: room if room.startswith(
"!") 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* joining {room_id_map[room_id]['name']}") logging.info(f"\t\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(lambda room: room in matrix_room_ids and not room in rooms_to_join, user['rooms'])) rooms_to_be_left = list(filter(
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* leaving {room_id_map[room_id]['name']}") logging.info(f"\t\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
logging.info("= (1/5) Creating default rooms =") self._log_step("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):
@ -121,7 +125,7 @@ class MlmMatrixBot:
'p_3d-printing' 'p_3d-printing'
]) ])
""" """
logging.info("= (2/5) Loading users and their groups =") self._log_step("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 = []
@ -134,15 +138,17 @@ 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(map(lambda group: group['sAMAccountName'], raw_groups)) all_groups = list(
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'] == 'TRUE' or group['sophomorixType'] == "adminclass", raw_groups)) groups = list(filter(lambda group: group['sophomorixMailList'] ==
'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))))
@ -163,16 +169,28 @@ 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):
logging.info("= (3/5) Creating project rooms =") self._log_step("Creating project rooms")
projects = list(map(lambda project: project.replace('p_', ''), projects)) projects = list(
map(lambda project: project.replace('p_', ''), projects))
await self._create_rooms(projects) await self._create_rooms(projects)
async def _create_rooms(self, rooms): async def _create_lab_rooms(self):
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) await self._matrixHelper.create_room(room, room, joinable_for_space_members=joinable_for_space_members, suggested=True)
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()
@ -184,7 +202,8 @@ 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(f"\t* Getting power levels of room {matrix_rooms[i]['name']}") logging.info(
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
@ -192,6 +211,10 @@ 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',
@ -216,7 +239,8 @@ class MlmMatrixBot:
for configKey in requiredConfigKeys: for configKey in requiredConfigKeys:
if configKey not in os.environ: if configKey not in os.environ:
logging.error(f"Required environment value {configKey} is not set") logging.error(
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,27 +57,16 @@ 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): async def create_room(self, name, alias, joinable_for_space_members=False, suggested=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": [self._space_id.split(":")[1]], "via": [via_domain],
}
},
{
"type": "m.room.join_rules",
"content": {
"join_rule": "restricted",
"allow": [
{
"type": "m.room_membership",
"room_id": self._space_id
}
]
} }
}, },
{ {
@ -95,16 +84,34 @@ class MatrixHelper:
] ]
if joinable_for_space_members: if joinable_for_space_members:
initial_state.append({ initial_state.insert(0, {
"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 assert room.room_id is not None
# add as child room
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
async def get_rooms(self): async def get_rooms(self):