Änderung: Erweiterung der Benutzer- und Rechteverwaltung im Admin-Bereich

Änderungen:
- Neue Funktionen zur Benutzerverwaltung hinzugefügt: Benutzer suchen, Benutzer abrufen und Benutzer aktualisieren.
- Implementierung von Funktionen zur Verwaltung von Benutzerrechten: Rechtearten auflisten, Benutzerrechte auflisten, Recht hinzufügen und Recht entfernen.
- Routen für die neuen Funktionen im Admin-Router definiert.
- Übersetzungen für Benutzer- und Rechteverwaltung in den Sprachdateien aktualisiert.

Diese Anpassungen verbessern die Verwaltung von Benutzern und deren Rechten im Admin-Bereich und erweitern die Funktionalität der Anwendung.
This commit is contained in:
Torsten Schulz (local)
2025-09-12 09:13:22 +02:00
parent 7decc4c4ae
commit 550b4b5fcb
9 changed files with 440 additions and 0 deletions

View File

@@ -23,6 +23,17 @@ class AdminController {
this.getRooms = this.getRooms.bind(this);
this.createRoom = this.createRoom.bind(this);
this.deleteRoom = this.deleteRoom.bind(this);
// User administration
this.searchUsers = this.searchUsers.bind(this);
this.getUser = this.getUser.bind(this);
this.updateUser = this.updateUser.bind(this);
// Rights
this.listRightTypes = this.listRightTypes.bind(this);
this.listUserRights = this.listUserRights.bind(this);
this.addUserRight = this.addUserRight.bind(this);
this.removeUserRight = this.removeUserRight.bind(this);
}
async getOpenInterests(req, res) {
@@ -35,6 +46,93 @@ class AdminController {
}
}
// --- User Administration ---
async searchUsers(req, res) {
try {
const { userid: requester } = req.headers;
const { q } = req.query;
const result = await AdminService.searchUsers(requester, q || '');
res.status(200).json(result);
} catch (error) {
const status = error.message === 'noaccess' ? 403 : 500;
res.status(status).json({ error: error.message });
}
}
async getUser(req, res) {
try {
const { userid: requester } = req.headers;
const { id } = req.params;
const result = await AdminService.getUserByHashedId(requester, id);
res.status(200).json(result);
} catch (error) {
const status = error.message === 'noaccess' ? 403 : (error.message === 'notfound' ? 404 : 500);
res.status(status).json({ error: error.message });
}
}
async updateUser(req, res) {
try {
const { userid: requester } = req.headers;
const { id } = req.params;
const result = await AdminService.updateUser(requester, id, req.body || {});
res.status(200).json(result);
} catch (error) {
const status = error.message === 'noaccess' ? 403 : (error.message === 'notfound' ? 404 : 500);
res.status(status).json({ error: error.message });
}
}
// --- Rights ---
async listRightTypes(req, res) {
try {
const { userid: requester } = req.headers;
const types = await AdminService.listUserRightTypes(requester);
res.status(200).json(types);
} catch (error) {
const status = error.message === 'noaccess' ? 403 : 500;
res.status(status).json({ error: error.message });
}
}
async listUserRights(req, res) {
try {
const { userid: requester } = req.headers;
const { id } = req.params;
const rights = await AdminService.listUserRightsForUser(requester, id);
res.status(200).json(rights);
} catch (error) {
const status = error.message === 'noaccess' ? 403 : (error.message === 'notfound' ? 404 : 500);
res.status(status).json({ error: error.message });
}
}
async addUserRight(req, res) {
try {
const { userid: requester } = req.headers;
const { id } = req.params;
const { rightTypeId } = req.body || {};
const result = await AdminService.addUserRight(requester, id, rightTypeId);
res.status(201).json({ status: 'ok' });
} catch (error) {
const status = error.message === 'noaccess' ? 403 : (error.message === 'notfound' || error.message === 'wrongtype' ? 404 : 500);
res.status(status).json({ error: error.message });
}
}
async removeUserRight(req, res) {
try {
const { userid: requester } = req.headers;
const { id } = req.params;
const { rightTypeId } = req.body || {};
await AdminService.removeUserRight(requester, id, rightTypeId);
res.status(204).send();
} catch (error) {
const status = error.message === 'noaccess' ? 403 : (error.message === 'notfound' ? 404 : 500);
res.status(status).json({ error: error.message });
}
}
async changeInterest(req, res) {
try {
const { userid: userId } = req.headers;

View File

@@ -15,6 +15,17 @@ router.post('/chat/rooms', authenticate, adminController.createRoom);
router.put('/chat/rooms/:id', authenticate, adminController.updateRoom);
router.delete('/chat/rooms/:id', authenticate, adminController.deleteRoom);
// --- Users Admin ---
router.get('/users/search', authenticate, adminController.searchUsers);
router.get('/users/:id', authenticate, adminController.getUser);
router.put('/users/:id', authenticate, adminController.updateUser);
// --- Rights Admin ---
router.get('/rights/types', authenticate, adminController.listRightTypes);
router.get('/rights/:id', authenticate, adminController.listUserRights);
router.post('/rights/:id', authenticate, adminController.addUserRight);
router.delete('/rights/:id', authenticate, adminController.removeUserRight);
router.get('/interests/open', authenticate, adminController.getOpenInterests);
router.post('/interest', authenticate, adminController.changeInterest);
router.post('/interest/translation', authenticate, adminController.changeTranslation);

View File

@@ -409,6 +409,103 @@ class AdminService {
await character.save();
}
// --- User Administration ---
async searchUsers(requestingHashedUserId, query) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'useradministration'))) {
throw new Error('noaccess');
}
if (!query || query.trim().length === 0) return [];
const users = await User.findAll({
where: {
[Op.or]: [
{ username: { [Op.iLike]: `%${query}%` } },
// email is encrypted, can't search directly reliably; skip email search
]
},
attributes: ['id', 'hashedId', 'username', 'active', 'registrationDate']
});
return users.map(u => ({ id: u.hashedId, username: u.username, active: u.active, registrationDate: u.registrationDate }));
}
async getUserByHashedId(requestingHashedUserId, targetHashedId) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'useradministration'))) {
throw new Error('noaccess');
}
const user = await User.findOne({
where: { hashedId: targetHashedId },
attributes: ['id', 'hashedId', 'username', 'active', 'registrationDate']
});
if (!user) throw new Error('notfound');
return { id: user.hashedId, username: user.username, active: user.active, registrationDate: user.registrationDate };
}
async updateUser(requestingHashedUserId, targetHashedId, data) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'useradministration'))) {
throw new Error('noaccess');
}
const user = await User.findOne({ where: { hashedId: targetHashedId } });
if (!user) throw new Error('notfound');
const updates = {};
if (typeof data.username === 'string' && data.username.trim().length > 0) {
updates.username = data.username.trim();
}
if (typeof data.active === 'boolean') {
updates.active = data.active;
}
if (Object.keys(updates).length === 0) return { id: user.hashedId, username: user.username, active: user.active };
await user.update(updates);
return { id: user.hashedId, username: user.username, active: user.active };
}
// --- User Rights Administration ---
async listUserRightTypes(requestingHashedUserId) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'rights'))) {
throw new Error('noaccess');
}
const types = await UserRightType.findAll({ attributes: ['id', 'title'] });
// map to tr keys if needed; keep title as key used elsewhere
return types.map(t => ({ id: t.id, title: t.title }));
}
async listUserRightsForUser(requestingHashedUserId, targetHashedId) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'rights'))) {
throw new Error('noaccess');
}
const user = await User.findOne({ where: { hashedId: targetHashedId }, attributes: ['id', 'hashedId', 'username'] });
if (!user) throw new Error('notfound');
const rights = await UserRight.findAll({
where: { userId: user.id },
include: [{ model: UserRightType, as: 'rightType' }]
});
return rights.map(r => ({ rightTypeId: r.rightTypeId, title: r.rightType?.title }));
}
async addUserRight(requestingHashedUserId, targetHashedId, rightTypeId) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'rights'))) {
throw new Error('noaccess');
}
const user = await User.findOne({ where: { hashedId: targetHashedId } });
if (!user) throw new Error('notfound');
const type = await UserRightType.findByPk(rightTypeId);
if (!type) throw new Error('wrongtype');
const existing = await UserRight.findOne({ where: { userId: user.id, rightTypeId } });
if (existing) return existing; // idempotent
const created = await UserRight.create({ userId: user.id, rightTypeId });
return created;
}
async removeUserRight(requestingHashedUserId, targetHashedId, rightTypeId) {
if (!(await this.hasUserAccess(requestingHashedUserId, 'rights'))) {
throw new Error('noaccess');
}
const user = await User.findOne({ where: { hashedId: targetHashedId } });
if (!user) throw new Error('notfound');
await UserRight.destroy({ where: { userId: user.id, rightTypeId } });
return true;
}
// --- Chat Room Admin ---
async getRoomTypes(userId) {
if (!(await this.hasUserAccess(userId, 'chatrooms'))) {