forked from FoundKeyGang/FoundKey
server: improve error messages
Refactor Error's to ApiError's. Changelog: Changed
This commit is contained in:
parent
09bc3cf95a
commit
c2372315f7
26 changed files with 151 additions and 62 deletions
|
@ -1,5 +1,6 @@
|
||||||
import { IsNull } from 'typeorm';
|
import { IsNull } from 'typeorm';
|
||||||
import { Users } from '@/models/index.js';
|
import { Users } from '@/models/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
import { signup } from '../../../common/signup.js';
|
import { signup } from '../../../common/signup.js';
|
||||||
|
|
||||||
|
@ -17,6 +18,8 @@ export const meta = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
errors: ['ACCESS_DENIED'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -31,10 +34,17 @@ export const paramDef = {
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, _me) => {
|
export default define(meta, paramDef, async (ps, _me) => {
|
||||||
const me = _me ? await Users.findOneByOrFail({ id: _me.id }) : null;
|
const me = _me ? await Users.findOneByOrFail({ id: _me.id }) : null;
|
||||||
const noUsers = (await Users.countBy({
|
if (me == null) {
|
||||||
host: IsNull(),
|
// check if this is the initial setup
|
||||||
})) === 0;
|
const noUsers = (await Users.countBy({
|
||||||
if (!noUsers && !me?.isAdmin) throw new Error('access denied');
|
host: IsNull(),
|
||||||
|
})) === 0;
|
||||||
|
if (!noUsers) {
|
||||||
|
throw new ApiError('ACCESS_DENIED');
|
||||||
|
}
|
||||||
|
} else if (!me.isAdmin) {
|
||||||
|
throw new ApiError('ACCESS_DENIED');
|
||||||
|
}
|
||||||
|
|
||||||
const { account, secret } = await signup({
|
const { account, secret } = await signup({
|
||||||
username: ps.username,
|
username: ps.username,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Users } from '@/models/index.js';
|
import { Users } from '@/models/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import { doPostSuspend } from '@/services/suspend-user.js';
|
import { doPostSuspend } from '@/services/suspend-user.js';
|
||||||
import { publishUserEvent } from '@/services/stream.js';
|
import { publishUserEvent } from '@/services/stream.js';
|
||||||
import { createDeleteAccountJob } from '@/queue/index.js';
|
import { createDeleteAccountJob } from '@/queue/index.js';
|
||||||
|
@ -9,6 +10,8 @@ export const meta = {
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireModerator: true,
|
requireModerator: true,
|
||||||
|
|
||||||
|
errors: ['NO_SUCH_USER', 'IS_ADMIN', 'IS_MODERATOR'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -24,15 +27,11 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
const user = await Users.findOneBy({ id: ps.userId });
|
const user = await Users.findOneBy({ id: ps.userId });
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new Error('user not found');
|
throw new ApiError('NO_SUCH_USER');
|
||||||
}
|
} else if (user.isAdmin) {
|
||||||
|
throw new ApiError('IS_ADMIN');
|
||||||
if (user.isAdmin) {
|
} else if(user.isModerator) {
|
||||||
throw new Error('cannot suspend admin');
|
throw new ApiError('IS_MODERATOR');
|
||||||
}
|
|
||||||
|
|
||||||
if (user.isModerator) {
|
|
||||||
throw new Error('cannot suspend moderator');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Users.isLocalUser(user)) {
|
if (Users.isLocalUser(user)) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Instances } from '@/models/index.js';
|
import { Instances } from '@/models/index.js';
|
||||||
import { toPuny } from '@/misc/convert-host.js';
|
import { toPuny } from '@/misc/convert-host.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata.js';
|
import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
|
|
||||||
|
@ -8,6 +9,8 @@ export const meta = {
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireModerator: true,
|
requireModerator: true,
|
||||||
|
|
||||||
|
errors: ['NO_SUCH_OBJECT'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -23,7 +26,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
const instance = await Instances.findOneBy({ host: toPuny(ps.host) });
|
const instance = await Instances.findOneBy({ host: toPuny(ps.host) });
|
||||||
|
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
throw new Error('instance not found');
|
throw new ApiError('NO_SUCH_OBJECT');
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchInstanceMetadata(instance, true);
|
fetchInstanceMetadata(instance, true);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Instances } from '@/models/index.js';
|
import { Instances } from '@/models/index.js';
|
||||||
import { toPuny } from '@/misc/convert-host.js';
|
import { toPuny } from '@/misc/convert-host.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -7,6 +8,8 @@ export const meta = {
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireModerator: true,
|
requireModerator: true,
|
||||||
|
|
||||||
|
errors: ['NO_SUCH_OBJECT'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -23,7 +26,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
const instance = await Instances.findOneBy({ host: toPuny(ps.host) });
|
const instance = await Instances.findOneBy({ host: toPuny(ps.host) });
|
||||||
|
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
throw new Error('instance not found');
|
throw new ApiError('NO_SUCH_OBJECT');
|
||||||
}
|
}
|
||||||
|
|
||||||
Instances.update({ host: toPuny(ps.host) }, {
|
Instances.update({ host: toPuny(ps.host) }, {
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
import { Users } from '@/models/index.js';
|
import { Users } from '@/models/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import { publishInternalEvent } from '@/services/stream.js';
|
import { publishInternalEvent } from '@/services/stream.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
|
description: 'Grants a user moderator privileges. Administrators cannot be granted moderator privileges.',
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireAdmin: true,
|
requireAdmin: true,
|
||||||
|
|
||||||
|
errors: ['NO_SUCH_USER', 'IS_ADMIN'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -22,11 +27,11 @@ export default define(meta, paramDef, async (ps) => {
|
||||||
const user = await Users.findOneBy({ id: ps.userId });
|
const user = await Users.findOneBy({ id: ps.userId });
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new Error('user not found');
|
throw new ApiError('NO_SUCH_USER');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.isAdmin) {
|
if (user.isAdmin) {
|
||||||
throw new Error('cannot mark as moderator if admin user');
|
throw new ApiError('IS_ADMIN');
|
||||||
}
|
}
|
||||||
|
|
||||||
await Users.update(user.id, {
|
await Users.update(user.id, {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Users } from '@/models/index.js';
|
import { Users } from '@/models/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import { publishInternalEvent } from '@/services/stream.js';
|
import { publishInternalEvent } from '@/services/stream.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
|
|
||||||
|
@ -7,6 +8,8 @@ export const meta = {
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireAdmin: true,
|
requireAdmin: true,
|
||||||
|
|
||||||
|
errors: ['NO_SUCH_USER'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -22,7 +25,7 @@ export default define(meta, paramDef, async (ps) => {
|
||||||
const user = await Users.findOneBy({ id: ps.userId });
|
const user = await Users.findOneBy({ id: ps.userId });
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new Error('user not found');
|
throw new ApiError('NO_SUCH_USER');
|
||||||
}
|
}
|
||||||
|
|
||||||
await Users.update(user.id, {
|
await Users.update(user.id, {
|
||||||
|
|
|
@ -51,7 +51,7 @@ export const paramDef = {
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
try {
|
try {
|
||||||
if (new URL(ps.inbox).protocol !== 'https:') throw new Error('https only');
|
if (new URL(ps.inbox).protocol !== 'https:') throw new ApiError('INVALID_URL', 'https only');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new ApiError('INVALID_URL', e);
|
throw new ApiError('INVALID_URL', e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||||
import { Users, UserProfiles } from '@/models/index.js';
|
import { Users, UserProfiles } from '@/models/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -21,6 +22,8 @@ export const meta = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
errors: ['NO_SUCH_USER', 'IS_ADMIN'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -36,11 +39,11 @@ export default define(meta, paramDef, async (ps) => {
|
||||||
const user = await Users.findOneBy({ id: ps.userId });
|
const user = await Users.findOneBy({ id: ps.userId });
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new Error('user not found');
|
throw new ApiError('NO_SUCH_USER');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.isAdmin) {
|
if (user.isAdmin) {
|
||||||
throw new Error('cannot reset password of admin');
|
throw new ApiError('IS_ADMIN');
|
||||||
}
|
}
|
||||||
|
|
||||||
const passwd = secureRndstr(8, true);
|
const passwd = secureRndstr(8, true);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Signins, UserProfiles, Users } from '@/models/index.js';
|
import { Signins, UserProfiles, Users } from '@/models/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -11,6 +12,8 @@ export const meta = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
errors: ['NO_SUCH_USER', 'IS_ADMIN'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -29,12 +32,12 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (user == null || profile == null) {
|
if (user == null || profile == null) {
|
||||||
throw new Error('user not found');
|
throw new ApiError('NO_SUCH_USER');
|
||||||
}
|
}
|
||||||
|
|
||||||
const _me = await Users.findOneByOrFail({ id: me.id });
|
const _me = await Users.findOneByOrFail({ id: me.id });
|
||||||
if ((_me.isModerator && !_me.isAdmin) && user.isAdmin) {
|
if ((_me.isModerator && !_me.isAdmin) && user.isAdmin) {
|
||||||
throw new Error('cannot show info of admin');
|
throw new ApiError('IS_ADMIN');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_me.isAdmin) {
|
if (!_me.isAdmin) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Users } from '@/models/index.js';
|
import { Users } from '@/models/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import { insertModerationLog } from '@/services/insert-moderation-log.js';
|
import { insertModerationLog } from '@/services/insert-moderation-log.js';
|
||||||
import { publishInternalEvent } from '@/services/stream.js';
|
import { publishInternalEvent } from '@/services/stream.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
|
@ -8,6 +9,8 @@ export const meta = {
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireModerator: true,
|
requireModerator: true,
|
||||||
|
|
||||||
|
errors: ['NO_SUCH_USER', 'IS_ADMIN'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -23,11 +26,11 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
const user = await Users.findOneBy({ id: ps.userId });
|
const user = await Users.findOneBy({ id: ps.userId });
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new Error('user not found');
|
throw new ApiError('NO_SUCH_USER');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.isAdmin) {
|
if (user.isAdmin) {
|
||||||
throw new Error('cannot silence admin');
|
throw new ApiError('IS_ADMIN');
|
||||||
}
|
}
|
||||||
|
|
||||||
await Users.update(user.id, {
|
await Users.update(user.id, {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import deleteFollowing from '@/services/following/delete.js';
|
import deleteFollowing from '@/services/following/delete.js';
|
||||||
import { Users, Followings, Notifications } from '@/models/index.js';
|
import { Users, Followings, Notifications } from '@/models/index.js';
|
||||||
import { User } from '@/models/entities/user.js';
|
import { User } from '@/models/entities/user.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import { insertModerationLog } from '@/services/insert-moderation-log.js';
|
import { insertModerationLog } from '@/services/insert-moderation-log.js';
|
||||||
import { doPostSuspend } from '@/services/suspend-user.js';
|
import { doPostSuspend } from '@/services/suspend-user.js';
|
||||||
import { publishUserEvent } from '@/services/stream.js';
|
import { publishUserEvent } from '@/services/stream.js';
|
||||||
|
@ -11,6 +12,8 @@ export const meta = {
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireModerator: true,
|
requireModerator: true,
|
||||||
|
|
||||||
|
errors: ['NO_SUCH_USER', 'IS_ADMIN', 'IS_MODERATOR'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -26,15 +29,11 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
const user = await Users.findOneBy({ id: ps.userId });
|
const user = await Users.findOneBy({ id: ps.userId });
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new Error('user not found');
|
throw new ApiError('NO_SUCH_USER');
|
||||||
}
|
} else if (user.isAdmin) {
|
||||||
|
throw new ApiError('IS_ADMIN');
|
||||||
if (user.isAdmin) {
|
} else if (user.isModerator) {
|
||||||
throw new Error('cannot suspend admin');
|
throw new ApiError('IS_MODERATOR');
|
||||||
}
|
|
||||||
|
|
||||||
if (user.isModerator) {
|
|
||||||
throw new Error('cannot suspend moderator');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Users.update(user.id, {
|
await Users.update(user.id, {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Users } from '@/models/index.js';
|
import { Users } from '@/models/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import { insertModerationLog } from '@/services/insert-moderation-log.js';
|
import { insertModerationLog } from '@/services/insert-moderation-log.js';
|
||||||
import { publishInternalEvent } from '@/services/stream.js';
|
import { publishInternalEvent } from '@/services/stream.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
|
@ -8,6 +9,8 @@ export const meta = {
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireModerator: true,
|
requireModerator: true,
|
||||||
|
|
||||||
|
errors: ['NO_SUCH_USER'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -23,7 +26,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
const user = await Users.findOneBy({ id: ps.userId });
|
const user = await Users.findOneBy({ id: ps.userId });
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new Error('user not found');
|
throw new ApiError('NO_SUCH_USER');
|
||||||
}
|
}
|
||||||
|
|
||||||
await Users.update(user.id, {
|
await Users.update(user.id, {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Users } from '@/models/index.js';
|
import { Users } from '@/models/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import { insertModerationLog } from '@/services/insert-moderation-log.js';
|
import { insertModerationLog } from '@/services/insert-moderation-log.js';
|
||||||
import { doPostUnsuspend } from '@/services/unsuspend-user.js';
|
import { doPostUnsuspend } from '@/services/unsuspend-user.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
|
@ -8,6 +9,8 @@ export const meta = {
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireModerator: true,
|
requireModerator: true,
|
||||||
|
|
||||||
|
errors: ['NO_SUCH_USER'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -23,7 +26,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
const user = await Users.findOneBy({ id: ps.userId });
|
const user = await Users.findOneBy({ id: ps.userId });
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new Error('user not found');
|
throw new ApiError('NO_SUCH_USER');
|
||||||
}
|
}
|
||||||
|
|
||||||
await Users.update(user.id, {
|
await Users.update(user.id, {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { genId } from '@/misc/gen-id.js';
|
||||||
import { GalleryPost } from '@/models/entities/gallery-post.js';
|
import { GalleryPost } from '@/models/entities/gallery-post.js';
|
||||||
import { DriveFile } from '@/models/entities/drive-file.js';
|
import { DriveFile } from '@/models/entities/drive-file.js';
|
||||||
import { HOUR } from '@/const.js';
|
import { HOUR } from '@/const.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -46,8 +47,14 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
}),
|
}),
|
||||||
))).filter((file): file is DriveFile => file != null);
|
))).filter((file): file is DriveFile => file != null);
|
||||||
|
|
||||||
if (files.length === 0) {
|
if (files.length !== ps.fileIds.length) {
|
||||||
throw new Error();
|
throw new ApiError(
|
||||||
|
'INVALID_PARAM',
|
||||||
|
{
|
||||||
|
param: '#/properties/fileIds/items',
|
||||||
|
reason: 'contains invalid file IDs',
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const post = await GalleryPosts.insert(new GalleryPost({
|
const post = await GalleryPosts.insert(new GalleryPost({
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { DriveFiles, GalleryPosts } from '@/models/index.js';
|
import { DriveFiles, GalleryPosts } from '@/models/index.js';
|
||||||
import { DriveFile } from '@/models/entities/drive-file.js';
|
import { DriveFile } from '@/models/entities/drive-file.js';
|
||||||
import { HOUR } from '@/const.js';
|
import { HOUR } from '@/const.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -20,6 +21,8 @@ export const meta = {
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'GalleryPost',
|
ref: 'GalleryPost',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
errors: ['INVALID_PARAM'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -45,8 +48,14 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
}),
|
}),
|
||||||
))).filter((file): file is DriveFile => file != null);
|
))).filter((file): file is DriveFile => file != null);
|
||||||
|
|
||||||
if (files.length === 0) {
|
if (files.length !== ps.fileIds.length) {
|
||||||
throw new Error();
|
throw new ApiError(
|
||||||
|
'INVALID_PARAM',
|
||||||
|
{
|
||||||
|
param: '#/properties/fileIds/items',
|
||||||
|
reason: 'contains invalid file IDs',
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await GalleryPosts.update({
|
await GalleryPosts.update({
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import * as speakeasy from 'speakeasy';
|
import * as speakeasy from 'speakeasy';
|
||||||
import { UserProfiles } from '@/models/index.js';
|
import { UserProfiles } from '@/models/index.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
errors: ['INTERNAL_ERROR', 'ACCESS_DENIED'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -23,7 +26,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
||||||
|
|
||||||
if (profile.twoFactorTempSecret == null) {
|
if (profile.twoFactorTempSecret == null) {
|
||||||
throw new Error('二段階認証の設定が開始されていません');
|
throw new ApiError('INTERNAL_ERROR', 'Two-step verification has not been initiated.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const verified = (speakeasy as any).totp.verify({
|
const verified = (speakeasy as any).totp.verify({
|
||||||
|
@ -33,7 +36,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!verified) {
|
if (!verified) {
|
||||||
throw new Error('not verified');
|
throw new ApiError('ACCESS_DENIED', 'TOTP missmatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
await UserProfiles.update(user.id, {
|
await UserProfiles.update(user.id, {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
Users,
|
Users,
|
||||||
} from '@/models/index.js';
|
} from '@/models/index.js';
|
||||||
import config from '@/config/index.js';
|
import config from '@/config/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import { publishMainStream } from '@/services/stream.js';
|
import { publishMainStream } from '@/services/stream.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
import { procedures, hash } from '../../../2fa.js';
|
import { procedures, hash } from '../../../2fa.js';
|
||||||
|
@ -20,6 +21,8 @@ export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
errors: ['ACCESS_DENIED', 'INTERNAL_ERROR', 'NO_SUCH_OBJECT'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -42,20 +45,20 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||||
|
|
||||||
if (!same) {
|
if (!same) {
|
||||||
throw new Error('incorrect password');
|
throw new ApiError('ACCESS_DENIED');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!profile.twoFactorEnabled) {
|
if (!profile.twoFactorEnabled) {
|
||||||
throw new Error('2fa not enabled');
|
throw new ApiError('INTERNAL_ERROR', '2fa not enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientData = JSON.parse(ps.clientDataJSON);
|
const clientData = JSON.parse(ps.clientDataJSON);
|
||||||
|
|
||||||
if (clientData.type !== 'webauthn.create') {
|
if (clientData.type !== 'webauthn.create') {
|
||||||
throw new Error('not a creation attestation');
|
throw new ApiError('INTERNAL_ERROR', 'not a creation attestation');
|
||||||
}
|
}
|
||||||
if (clientData.origin !== config.scheme + '://' + config.host) {
|
if (clientData.origin !== config.scheme + '://' + config.host) {
|
||||||
throw new Error('origin mismatch');
|
throw new ApiError('INTERNAL_ERROR', 'origin mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientDataJSONHash = hash(Buffer.from(ps.clientDataJSON, 'utf-8'));
|
const clientDataJSONHash = hash(Buffer.from(ps.clientDataJSON, 'utf-8'));
|
||||||
|
@ -64,14 +67,14 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
|
|
||||||
const rpIdHash = attestation.authData.slice(0, 32);
|
const rpIdHash = attestation.authData.slice(0, 32);
|
||||||
if (!rpIdHashReal.equals(rpIdHash)) {
|
if (!rpIdHashReal.equals(rpIdHash)) {
|
||||||
throw new Error('rpIdHash mismatch');
|
throw new ApiError('INTERNAL_ERROR', 'rpIdHash mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
const flags = attestation.authData[32];
|
const flags = attestation.authData[32];
|
||||||
|
|
||||||
// eslint:disable-next-line:no-bitwise
|
// eslint:disable-next-line:no-bitwise
|
||||||
if (!(flags & 1)) {
|
if (!(flags & 1)) {
|
||||||
throw new Error('user not present');
|
throw new ApiError('INTERNAL_ERROR', 'user not present');
|
||||||
}
|
}
|
||||||
|
|
||||||
const authData = Buffer.from(attestation.authData);
|
const authData = Buffer.from(attestation.authData);
|
||||||
|
@ -80,11 +83,11 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const publicKeyData = authData.slice(55 + credentialIdLength);
|
const publicKeyData = authData.slice(55 + credentialIdLength);
|
||||||
const publicKey: Map<number, any> = await cborDecodeFirst(publicKeyData);
|
const publicKey: Map<number, any> = await cborDecodeFirst(publicKeyData);
|
||||||
if (publicKey.get(3) !== -7) {
|
if (publicKey.get(3) !== -7) {
|
||||||
throw new Error('alg mismatch');
|
throw new ApiError('INTERNAL_ERROR', 'algorithm mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(procedures as any)[attestation.fmt]) {
|
if (!(procedures as any)[attestation.fmt]) {
|
||||||
throw new Error('unsupported fmt');
|
throw new ApiError('INTERNAL_ERROR', 'unsupported fmt');
|
||||||
}
|
}
|
||||||
|
|
||||||
const verificationData = (procedures as any)[attestation.fmt].verify({
|
const verificationData = (procedures as any)[attestation.fmt].verify({
|
||||||
|
@ -95,7 +98,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
publicKey,
|
publicKey,
|
||||||
rpIdHash,
|
rpIdHash,
|
||||||
});
|
});
|
||||||
if (!verificationData.valid) throw new Error('signature invalid');
|
if (!verificationData.valid) throw new ApiError('INTERNAL_ERROR', 'signature invalid');
|
||||||
|
|
||||||
const attestationChallenge = await AttestationChallenges.findOneBy({
|
const attestationChallenge = await AttestationChallenges.findOneBy({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
@ -105,7 +108,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!attestationChallenge) {
|
if (!attestationChallenge) {
|
||||||
throw new Error('non-existent challenge');
|
throw new ApiError('NO_SUCH_OBJECT', 'Attestation challenge not found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
await AttestationChallenges.delete({
|
await AttestationChallenges.delete({
|
||||||
|
@ -118,7 +121,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
new Date().getTime() - attestationChallenge.createdAt.getTime() >=
|
new Date().getTime() - attestationChallenge.createdAt.getTime() >=
|
||||||
5 * MINUTE
|
5 * MINUTE
|
||||||
) {
|
) {
|
||||||
throw new Error('expired challenge');
|
throw new ApiError('NO_SUCH_OBJECT', 'Attestation challenge expired.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const credentialIdString = credentialId.toString('hex');
|
const credentialIdString = credentialId.toString('hex');
|
||||||
|
|
|
@ -3,6 +3,7 @@ import * as crypto from 'node:crypto';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { UserProfiles, AttestationChallenges } from '@/models/index.js';
|
import { UserProfiles, AttestationChallenges } from '@/models/index.js';
|
||||||
import { genId } from '@/misc/gen-id.js';
|
import { genId } from '@/misc/gen-id.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
import { hash } from '../../../2fa.js';
|
import { hash } from '../../../2fa.js';
|
||||||
|
|
||||||
|
@ -12,6 +13,8 @@ export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
errors: ['ACCESS_DENIED', 'INTERNAL_ERROR'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -30,11 +33,11 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||||
|
|
||||||
if (!same) {
|
if (!same) {
|
||||||
throw new Error('incorrect password');
|
throw new ApiError('ACCESS_DENIED');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!profile.twoFactorEnabled) {
|
if (!profile.twoFactorEnabled) {
|
||||||
throw new Error('2fa not enabled');
|
throw new ApiError('INTERNAL_ERROR', '2fa not enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 32 byte challenge
|
// 32 byte challenge
|
||||||
|
|
|
@ -3,12 +3,15 @@ import * as speakeasy from 'speakeasy';
|
||||||
import * as QRCode from 'qrcode';
|
import * as QRCode from 'qrcode';
|
||||||
import config from '@/config/index.js';
|
import config from '@/config/index.js';
|
||||||
import { UserProfiles } from '@/models/index.js';
|
import { UserProfiles } from '@/models/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
errors: ['ACCESS_DENIED'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -27,7 +30,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||||
|
|
||||||
if (!same) {
|
if (!same) {
|
||||||
throw new Error('incorrect password');
|
throw new ApiError('ACCESS_DENIED');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate user's secret key
|
// Generate user's secret key
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { UserProfiles, UserSecurityKeys, Users } from '@/models/index.js';
|
import { UserProfiles, UserSecurityKeys, Users } from '@/models/index.js';
|
||||||
import { publishMainStream } from '@/services/stream.js';
|
import { publishMainStream } from '@/services/stream.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
errors: ['ACCESS_DENIED'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -26,7 +29,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||||
|
|
||||||
if (!same) {
|
if (!same) {
|
||||||
throw new Error('incorrect password');
|
throw new ApiError('ACCESS_DENIED');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we only delete the user's own creds
|
// Make sure we only delete the user's own creds
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { UserProfiles } from '@/models/index.js';
|
import { UserProfiles } from '@/models/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
errors: ['ACCESS_DENIED'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -24,7 +27,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||||
|
|
||||||
if (!same) {
|
if (!same) {
|
||||||
throw new Error('incorrect password');
|
throw new ApiError('ACCESS_DENIED');
|
||||||
}
|
}
|
||||||
|
|
||||||
await UserProfiles.update(user.id, {
|
await UserProfiles.update(user.id, {
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { UserProfiles } from '@/models/index.js';
|
import { UserProfiles } from '@/models/index.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
errors: ['ACCESS_DENIED'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -25,7 +28,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const same = await bcrypt.compare(ps.currentPassword, profile.password!);
|
const same = await bcrypt.compare(ps.currentPassword, profile.password!);
|
||||||
|
|
||||||
if (!same) {
|
if (!same) {
|
||||||
throw new Error('incorrect password');
|
throw new ApiError('ACCESS_DENIED');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate hash of password
|
// Generate hash of password
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { UserProfiles, Users } from '@/models/index.js';
|
import { UserProfiles, Users } from '@/models/index.js';
|
||||||
import { deleteAccount } from '@/services/delete-account.js';
|
import { deleteAccount } from '@/services/delete-account.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
errors: ['ACCESS_DENIED'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -29,7 +32,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||||
|
|
||||||
if (!same) {
|
if (!same) {
|
||||||
throw new Error('incorrect password');
|
throw new ApiError('ACCESS_DENIED');
|
||||||
}
|
}
|
||||||
|
|
||||||
await deleteAccount(user);
|
await deleteAccount(user);
|
||||||
|
|
|
@ -2,12 +2,15 @@ import bcrypt from 'bcryptjs';
|
||||||
import { publishInternalEvent, publishMainStream, publishUserEvent } from '@/services/stream.js';
|
import { publishInternalEvent, publishMainStream, publishUserEvent } from '@/services/stream.js';
|
||||||
import { Users, UserProfiles } from '@/models/index.js';
|
import { Users, UserProfiles } from '@/models/index.js';
|
||||||
import generateUserToken from '../../common/generate-native-user-token.js';
|
import generateUserToken from '../../common/generate-native-user-token.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
errors: ['ACCESS_DENIED'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -29,7 +32,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||||
|
|
||||||
if (!same) {
|
if (!same) {
|
||||||
throw new Error('incorrect password');
|
throw new ApiError('ACCESS_DENIED');
|
||||||
}
|
}
|
||||||
|
|
||||||
const newToken = generateUserToken();
|
const newToken = generateUserToken();
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { resetDb } from '@/db/postgre.js';
|
import { resetDb } from '@/db/postgre.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import define from '../define.js';
|
import define from '../define.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
|
@ -17,7 +18,7 @@ export const paramDef = {
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
if (process.env.NODE_ENV !== 'test') throw new Error('NODE_ENV is not a test');
|
if (process.env.NODE_ENV !== 'test') throw new ApiError('ACCESS_DENIED');
|
||||||
|
|
||||||
await resetDb();
|
await resetDb();
|
||||||
|
|
||||||
|
|
|
@ -187,6 +187,14 @@ export const errors: Record<string, { message: string, httpStatusCode: number }>
|
||||||
message: 'Invalid username.',
|
message: 'Invalid username.',
|
||||||
httpStatusCode: 400,
|
httpStatusCode: 400,
|
||||||
},
|
},
|
||||||
|
IS_ADMIN: {
|
||||||
|
message: 'This action cannot be done to an administrator account.',
|
||||||
|
httpStatusCode: 400,
|
||||||
|
},
|
||||||
|
IS_MODERATOR: {
|
||||||
|
message: 'This action cannot be done to a moderator account.',
|
||||||
|
httpStatusCode: 400,
|
||||||
|
},
|
||||||
LESS_RESTRICTIVE_VISIBILITY: {
|
LESS_RESTRICTIVE_VISIBILITY: {
|
||||||
message: 'The visibility cannot be less restrictive than the parent note.',
|
message: 'The visibility cannot be less restrictive than the parent note.',
|
||||||
httpStatusCode: 400,
|
httpStatusCode: 400,
|
||||||
|
|
Loading…
Reference in a new issue