pass client token through API via caller ID

This commit is contained in:
Hazelnoot 2025-06-22 15:34:52 -04:00
parent 2e61fafe57
commit df750a6b65
5 changed files with 85 additions and 13 deletions

View file

@ -342,11 +342,11 @@ export class ChatService {
}
@bindThis
public async hasPermissionToViewRoomTimeline(meId: MiUser['id'], room: MiChatRoom) {
if (await this.isRoomMember(room, meId)) {
public async hasPermissionToViewRoomTimeline(me: MiUser, room: MiChatRoom) {
if (await this.isRoomMember(room, me.id)) {
return true;
} else {
const iAmModerator = await this.roleService.isModerator({ id: meId });
const iAmModerator = await this.roleService.isModerator(me);
if (iAmModerator) {
return true;
}
@ -563,12 +563,12 @@ export class ChatService {
}
@bindThis
public async hasPermissionToDeleteRoom(meId: MiUser['id'], room: MiChatRoom) {
if (room.ownerId === meId) {
public async hasPermissionToDeleteRoom(me: MiUser, room: MiChatRoom) {
if (room.ownerId === me.id) {
return true;
}
const iAmModerator = await this.roleService.isModerator({ id: meId });
const iAmModerator = await this.roleService.isModerator(me);
if (iAmModerator) {
return true;
}

View file

@ -31,6 +31,7 @@ import type { Packed } from '@/misc/json-schema.js';
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
import { NotificationService } from '@/core/NotificationService.js';
import type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common';
import { getCallerId } from '@/misc/attach-caller-id.js';
export type RolePolicies = {
gtlAvailable: boolean;
@ -414,7 +415,21 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id));
const user = typeof(userOrId) === 'object' ? userOrId : roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userOrId) : null;
const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, assignedRoles, r.condFormula, followStats));
return [...assignedRoles, ...matchedCondRoles];
let allRoles = [...assignedRoles, ...matchedCondRoles];
// Check for dropped token permissions
const rank = user ? getCallerId(user)?.accessToken?.rank : null;
if (rank != null) {
// Copy roles, since they come from a cache
allRoles = allRoles.map(role => ({
...role,
isModerator: role.isModerator && (rank === 'admin' || rank === 'mod'),
isAdministrator: role.isAdministrator && rank === 'admin',
}));
}
return allRoles;
}
/**
@ -514,12 +529,22 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
@bindThis
public async isModerator(user: { id: MiUser['id'] } | null): Promise<boolean> {
if (user == null) return false;
// Check for dropped token permissions
const callerId = getCallerId(user);
if (callerId?.accessToken?.rank != null && callerId.accessToken.rank !== 'admin' && callerId.accessToken.rank !== 'mod') return false;
return (this.meta.rootUserId === user.id) || (await this.getUserRoles(user.id)).some(r => r.isModerator || r.isAdministrator);
}
@bindThis
public async isAdministrator(user: { id: MiUser['id'] } | null): Promise<boolean> {
if (user == null) return false;
// Check for dropped token permissions
const callerId = getCallerId(user);
if (callerId?.accessToken?.rank != null && callerId.accessToken.rank !== 'admin') return false;
return (this.meta.rootUserId === user.id) || (await this.getUserRoles(user.id)).some(r => r.isAdministrator);
}

View file

@ -481,8 +481,8 @@ export class UserEntityService implements OnModuleInit {
const isDetailed = opts.schema !== 'UserLite';
const meId = me ? me.id : null;
const isMe = meId === user.id;
const iAmModerator = opts.iAmModerator ?? (me ? await this.roleService.isModerator(me as MiUser) : false);
const iAmAdmin = opts.iAmAdmin ?? (me ? await this.roleService.isAdministrator(user) : false);
const iAmModerator = opts.iAmModerator ?? (me ? await this.roleService.isModerator(me) : false);
const iAmAdmin = opts.iAmAdmin ?? (me ? await this.roleService.isAdministrator(me) : false);
const profile = isDetailed
? (opts.userProfile ?? user.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id }))