summaryrefslogtreecommitdiff
path: root/src/server/api
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2019-07-05 02:13:02 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2019-07-05 02:13:02 +0900
commitd078f786028c066d536990c280091f9c43b67ec9 (patch)
tree4892e2dd4d94cb5d8dd3ce81d4c7ba6b190f7960 /src/server/api
parentMerge branch 'develop' (diff)
parent11.24.0 (diff)
downloadsharkey-d078f786028c066d536990c280091f9c43b67ec9.tar.gz
sharkey-d078f786028c066d536990c280091f9c43b67ec9.tar.bz2
sharkey-d078f786028c066d536990c280091f9c43b67ec9.zip
Merge branch 'develop'
Diffstat (limited to 'src/server/api')
-rw-r--r--src/server/api/2fa.ts422
-rw-r--r--src/server/api/endpoints/admin/drive/clean-remote-files.ts13
-rw-r--r--src/server/api/endpoints/admin/send-email.ts26
-rw-r--r--src/server/api/endpoints/admin/vacuum.ts33
-rw-r--r--src/server/api/endpoints/ap/show.ts4
-rw-r--r--src/server/api/endpoints/app/create.ts5
-rw-r--r--src/server/api/endpoints/app/show.ts5
-rw-r--r--src/server/api/endpoints/auth/session/generate.ts13
-rw-r--r--src/server/api/endpoints/auth/session/userkey.ts13
-rw-r--r--src/server/api/endpoints/blocking/list.ts9
-rw-r--r--src/server/api/endpoints/drive.ts13
-rw-r--r--src/server/api/endpoints/drive/files.ts9
-rw-r--r--src/server/api/endpoints/drive/files/attached-notes.ts9
-rw-r--r--src/server/api/endpoints/drive/files/check-existence.ts5
-rw-r--r--src/server/api/endpoints/drive/files/create.ts5
-rw-r--r--src/server/api/endpoints/drive/files/find-by-hash.ts9
-rw-r--r--src/server/api/endpoints/drive/files/find.ts9
-rw-r--r--src/server/api/endpoints/drive/files/show.ts5
-rw-r--r--src/server/api/endpoints/drive/folders.ts9
-rw-r--r--src/server/api/endpoints/drive/folders/find.ts9
-rw-r--r--src/server/api/endpoints/drive/folders/show.ts5
-rw-r--r--src/server/api/endpoints/drive/stream.ts9
-rw-r--r--src/server/api/endpoints/hashtags/list.ts9
-rw-r--r--src/server/api/endpoints/hashtags/search.ts9
-rw-r--r--src/server/api/endpoints/hashtags/show.ts5
-rw-r--r--src/server/api/endpoints/hashtags/trend.ts25
-rw-r--r--src/server/api/endpoints/hashtags/users.ts9
-rw-r--r--src/server/api/endpoints/i.ts5
-rw-r--r--src/server/api/endpoints/i/2fa/getkeys.ts67
-rw-r--r--src/server/api/endpoints/i/2fa/key-done.ts151
-rw-r--r--src/server/api/endpoints/i/2fa/register-key.ts60
-rw-r--r--src/server/api/endpoints/i/2fa/remove-key.ts46
-rw-r--r--src/server/api/endpoints/i/favorites.ts9
-rw-r--r--src/server/api/endpoints/i/notifications.ts9
-rw-r--r--src/server/api/endpoints/messaging/history.ts9
-rw-r--r--src/server/api/endpoints/messaging/messages.ts9
-rw-r--r--src/server/api/endpoints/messaging/messages/create.ts5
-rw-r--r--src/server/api/endpoints/meta.ts49
-rw-r--r--src/server/api/endpoints/mute/list.ts9
-rw-r--r--src/server/api/endpoints/notes.ts9
-rw-r--r--src/server/api/endpoints/notes/children.ts9
-rw-r--r--src/server/api/endpoints/notes/conversation.ts9
-rw-r--r--src/server/api/endpoints/notes/create.ts9
-rw-r--r--src/server/api/endpoints/notes/featured.ts9
-rw-r--r--src/server/api/endpoints/notes/global-timeline.ts9
-rw-r--r--src/server/api/endpoints/notes/hybrid-timeline.ts9
-rw-r--r--src/server/api/endpoints/notes/local-timeline.ts9
-rw-r--r--src/server/api/endpoints/notes/mentions.ts9
-rw-r--r--src/server/api/endpoints/notes/reactions.ts9
-rw-r--r--src/server/api/endpoints/notes/renotes.ts9
-rw-r--r--src/server/api/endpoints/notes/replies.ts9
-rw-r--r--src/server/api/endpoints/notes/search-by-tag.ts9
-rw-r--r--src/server/api/endpoints/notes/search.ts9
-rw-r--r--src/server/api/endpoints/notes/show.ts5
-rw-r--r--src/server/api/endpoints/notes/timeline.ts9
-rw-r--r--src/server/api/endpoints/notes/user-list-timeline.ts9
-rw-r--r--src/server/api/endpoints/pages/create.ts5
-rw-r--r--src/server/api/endpoints/pages/show.ts5
-rw-r--r--src/server/api/endpoints/pinned-users.ts9
-rw-r--r--src/server/api/endpoints/stats.ts25
-rw-r--r--src/server/api/endpoints/users.ts9
-rw-r--r--src/server/api/endpoints/users/followers.ts9
-rw-r--r--src/server/api/endpoints/users/following.ts9
-rw-r--r--src/server/api/endpoints/users/get-frequently-replied-users.ts9
-rw-r--r--src/server/api/endpoints/users/groups/create.ts5
-rw-r--r--src/server/api/endpoints/users/groups/joined.ts9
-rw-r--r--src/server/api/endpoints/users/groups/owned.ts9
-rw-r--r--src/server/api/endpoints/users/groups/show.ts5
-rw-r--r--src/server/api/endpoints/users/groups/transfer.ts5
-rw-r--r--src/server/api/endpoints/users/groups/update.ts5
-rw-r--r--src/server/api/endpoints/users/lists/create.ts5
-rw-r--r--src/server/api/endpoints/users/lists/list.ts9
-rw-r--r--src/server/api/endpoints/users/lists/show.ts5
-rw-r--r--src/server/api/endpoints/users/notes.ts9
-rw-r--r--src/server/api/endpoints/users/recommendation.ts9
-rw-r--r--src/server/api/endpoints/users/search.ts9
-rw-r--r--src/server/api/endpoints/users/show.ts7
-rw-r--r--src/server/api/private/signin.ts137
78 files changed, 1193 insertions, 388 deletions
diff --git a/src/server/api/2fa.ts b/src/server/api/2fa.ts
new file mode 100644
index 0000000000..3bc4627a62
--- /dev/null
+++ b/src/server/api/2fa.ts
@@ -0,0 +1,422 @@
+import * as crypto from 'crypto';
+import config from '../../config';
+import * as jsrsasign from 'jsrsasign';
+
+const ECC_PRELUDE = Buffer.from([0x04]);
+const NULL_BYTE = Buffer.from([0]);
+const PEM_PRELUDE = Buffer.from(
+ '3059301306072a8648ce3d020106082a8648ce3d030107034200',
+ 'hex'
+);
+
+// Android Safetynet attestations are signed with this cert:
+const GSR2 = `-----BEGIN CERTIFICATE-----
+MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
+MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
+A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
+v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
+eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
+tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
+C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
+zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
+mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
+V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
+bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
+3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
+J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
+291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
+ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
+AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
+TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
+-----END CERTIFICATE-----\n`;
+
+function base64URLDecode(source: string) {
+ return Buffer.from(source.replace(/\-/g, '+').replace(/_/g, '/'), 'base64');
+}
+
+function getCertSubject(certificate: string) {
+ const subjectCert = new jsrsasign.X509();
+ subjectCert.readCertPEM(certificate);
+
+ const subjectString = subjectCert.getSubjectString();
+ const subjectFields = subjectString.slice(1).split('/');
+
+ const fields = {} as Record<string, string>;
+ for (const field of subjectFields) {
+ const eqIndex = field.indexOf('=');
+ fields[field.substring(0, eqIndex)] = field.substring(eqIndex + 1);
+ }
+
+ return fields;
+}
+
+function verifyCertificateChain(certificates: string[]) {
+ let valid = true;
+
+ for (let i = 0; i < certificates.length; i++) {
+ const Cert = certificates[i];
+ const certificate = new jsrsasign.X509();
+ certificate.readCertPEM(Cert);
+
+ const CACert = i + 1 >= certificates.length ? Cert : certificates[i + 1];
+
+ const certStruct = jsrsasign.ASN1HEX.getTLVbyList(certificate.hex!, 0, [0]);
+ const algorithm = certificate.getSignatureAlgorithmField();
+ const signatureHex = certificate.getSignatureValueHex();
+
+ // Verify against CA
+ const Signature = new jsrsasign.KJUR.crypto.Signature({alg: algorithm});
+ Signature.init(CACert);
+ Signature.updateHex(certStruct);
+ valid = valid && !!Signature.verify(signatureHex); // true if CA signed the certificate
+ }
+
+ return valid;
+}
+
+function PEMString(pemBuffer: Buffer, type = 'CERTIFICATE') {
+ if (pemBuffer.length == 65 && pemBuffer[0] == 0x04) {
+ pemBuffer = Buffer.concat([PEM_PRELUDE, pemBuffer], 91);
+ type = 'PUBLIC KEY';
+ }
+ const cert = pemBuffer.toString('base64');
+
+ const keyParts = [];
+ const max = Math.ceil(cert.length / 64);
+ let start = 0;
+ for (let i = 0; i < max; i++) {
+ keyParts.push(cert.substring(start, start + 64));
+ start += 64;
+ }
+
+ return (
+ `-----BEGIN ${type}-----\n` +
+ keyParts.join('\n') +
+ `\n-----END ${type}-----\n`
+ );
+}
+
+export function hash(data: Buffer) {
+ return crypto
+ .createHash('sha256')
+ .update(data)
+ .digest();
+}
+
+export function verifyLogin({
+ publicKey,
+ authenticatorData,
+ clientDataJSON,
+ clientData,
+ signature,
+ challenge
+}: {
+ publicKey: Buffer,
+ authenticatorData: Buffer,
+ clientDataJSON: Buffer,
+ clientData: any,
+ signature: Buffer,
+ challenge: string
+}) {
+ if (clientData.type != 'webauthn.get') {
+ throw new Error('type is not webauthn.get');
+ }
+
+ if (hash(clientData.challenge).toString('hex') != challenge) {
+ throw new Error('challenge mismatch');
+ }
+ if (clientData.origin != config.scheme + '://' + config.host) {
+ throw new Error('origin mismatch');
+ }
+
+ const verificationData = Buffer.concat(
+ [authenticatorData, hash(clientDataJSON)],
+ 32 + authenticatorData.length
+ );
+
+ return crypto
+ .createVerify('SHA256')
+ .update(verificationData)
+ .verify(PEMString(publicKey), signature);
+}
+
+export const procedures = {
+ none: {
+ verify({publicKey}: {publicKey: Map<number, Buffer>}) {
+ const negTwo = publicKey.get(-2);
+
+ if (!negTwo || negTwo.length != 32) {
+ throw new Error('invalid or no -2 key given');
+ }
+ const negThree = publicKey.get(-3);
+ if (!negThree || negThree.length != 32) {
+ throw new Error('invalid or no -3 key given');
+ }
+
+ const publicKeyU2F = Buffer.concat(
+ [ECC_PRELUDE, negTwo, negThree],
+ 1 + 32 + 32
+ );
+
+ return {
+ publicKey: publicKeyU2F,
+ valid: true
+ };
+ }
+ },
+ 'android-key': {
+ verify({
+ attStmt,
+ authenticatorData,
+ clientDataHash,
+ publicKey,
+ rpIdHash,
+ credentialId
+ }: {
+ attStmt: any,
+ authenticatorData: Buffer,
+ clientDataHash: Buffer,
+ publicKey: Map<number, any>;
+ rpIdHash: Buffer,
+ credentialId: Buffer,
+ }) {
+ if (attStmt.alg != -7) {
+ throw new Error('alg mismatch');
+ }
+
+ const verificationData = Buffer.concat([
+ authenticatorData,
+ clientDataHash
+ ]);
+
+ const attCert: Buffer = attStmt.x5c[0];
+
+ const negTwo = publicKey.get(-2);
+
+ if (!negTwo || negTwo.length != 32) {
+ throw new Error('invalid or no -2 key given');
+ }
+ const negThree = publicKey.get(-3);
+ if (!negThree || negThree.length != 32) {
+ throw new Error('invalid or no -3 key given');
+ }
+
+ const publicKeyData = Buffer.concat(
+ [ECC_PRELUDE, negTwo, negThree],
+ 1 + 32 + 32
+ );
+
+ if (!attCert.equals(publicKeyData)) {
+ throw new Error('public key mismatch');
+ }
+
+ const isValid = crypto
+ .createVerify('SHA256')
+ .update(verificationData)
+ .verify(PEMString(attCert), attStmt.sig);
+
+ // TODO: Check 'attestationChallenge' field in extension of cert matches hash(clientDataJSON)
+
+ return {
+ valid: isValid,
+ publicKey: publicKeyData
+ };
+ }
+ },
+ // what a stupid attestation
+ 'android-safetynet': {
+ verify({
+ attStmt,
+ authenticatorData,
+ clientDataHash,
+ publicKey,
+ rpIdHash,
+ credentialId
+ }: {
+ attStmt: any,
+ authenticatorData: Buffer,
+ clientDataHash: Buffer,
+ publicKey: Map<number, any>;
+ rpIdHash: Buffer,
+ credentialId: Buffer,
+ }) {
+ const verificationData = hash(
+ Buffer.concat([authenticatorData, clientDataHash])
+ );
+
+ const jwsParts = attStmt.response.toString('utf-8').split('.');
+
+ const header = JSON.parse(base64URLDecode(jwsParts[0]).toString('utf-8'));
+ const response = JSON.parse(
+ base64URLDecode(jwsParts[1]).toString('utf-8')
+ );
+ const signature = jwsParts[2];
+
+ if (!verificationData.equals(Buffer.from(response.nonce, 'base64'))) {
+ throw new Error('invalid nonce');
+ }
+
+ const certificateChain = header.x5c
+ .map((key: any) => PEMString(key))
+ .concat([GSR2]);
+
+ if (getCertSubject(certificateChain[0]).CN != 'attest.android.com') {
+ throw new Error('invalid common name');
+ }
+
+ if (!verifyCertificateChain(certificateChain)) {
+ throw new Error('Invalid certificate chain!');
+ }
+
+ const signatureBase = Buffer.from(
+ jwsParts[0] + '.' + jwsParts[1],
+ 'utf-8'
+ );
+
+ const valid = crypto
+ .createVerify('sha256')
+ .update(signatureBase)
+ .verify(certificateChain[0], base64URLDecode(signature));
+
+ const negTwo = publicKey.get(-2);
+
+ if (!negTwo || negTwo.length != 32) {
+ throw new Error('invalid or no -2 key given');
+ }
+ const negThree = publicKey.get(-3);
+ if (!negThree || negThree.length != 32) {
+ throw new Error('invalid or no -3 key given');
+ }
+
+ const publicKeyData = Buffer.concat(
+ [ECC_PRELUDE, negTwo, negThree],
+ 1 + 32 + 32
+ );
+ return {
+ valid,
+ publicKey: publicKeyData
+ };
+ }
+ },
+ packed: {
+ verify({
+ attStmt,
+ authenticatorData,
+ clientDataHash,
+ publicKey,
+ rpIdHash,
+ credentialId
+ }: {
+ attStmt: any,
+ authenticatorData: Buffer,
+ clientDataHash: Buffer,
+ publicKey: Map<number, any>;
+ rpIdHash: Buffer,
+ credentialId: Buffer,
+ }) {
+ const verificationData = Buffer.concat([
+ authenticatorData,
+ clientDataHash
+ ]);
+
+ if (attStmt.x5c) {
+ const attCert = attStmt.x5c[0];
+
+ const validSignature = crypto
+ .createVerify('SHA256')
+ .update(verificationData)
+ .verify(PEMString(attCert), attStmt.sig);
+
+ const negTwo = publicKey.get(-2);
+
+ if (!negTwo || negTwo.length != 32) {
+ throw new Error('invalid or no -2 key given');
+ }
+ const negThree = publicKey.get(-3);
+ if (!negThree || negThree.length != 32) {
+ throw new Error('invalid or no -3 key given');
+ }
+
+ const publicKeyData = Buffer.concat(
+ [ECC_PRELUDE, negTwo, negThree],
+ 1 + 32 + 32
+ );
+
+ return {
+ valid: validSignature,
+ publicKey: publicKeyData
+ };
+ } else if (attStmt.ecdaaKeyId) {
+ // https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-ecdaa-algorithm-v2.0-id-20180227.html#ecdaa-verify-operation
+ throw new Error('ECDAA-Verify is not supported');
+ } else {
+ if (attStmt.alg != -7) throw new Error('alg mismatch');
+
+ throw new Error('self attestation is not supported');
+ }
+ }
+ },
+
+ 'fido-u2f': {
+ verify({
+ attStmt,
+ authenticatorData,
+ clientDataHash,
+ publicKey,
+ rpIdHash,
+ credentialId
+ }: {
+ attStmt: any,
+ authenticatorData: Buffer,
+ clientDataHash: Buffer,
+ publicKey: Map<number, any>,
+ rpIdHash: Buffer,
+ credentialId: Buffer
+ }) {
+ const x5c: Buffer[] = attStmt.x5c;
+ if (x5c.length != 1) {
+ throw new Error('x5c length does not match expectation');
+ }
+
+ const attCert = x5c[0];
+
+ // TODO: make sure attCert is an Elliptic Curve (EC) public key over the P-256 curve
+
+ const negTwo: Buffer = publicKey.get(-2);
+
+ if (!negTwo || negTwo.length != 32) {
+ throw new Error('invalid or no -2 key given');
+ }
+ const negThree: Buffer = publicKey.get(-3);
+ if (!negThree || negThree.length != 32) {
+ throw new Error('invalid or no -3 key given');
+ }
+
+ const publicKeyU2F = Buffer.concat(
+ [ECC_PRELUDE, negTwo, negThree],
+ 1 + 32 + 32
+ );
+
+ const verificationData = Buffer.concat([
+ NULL_BYTE,
+ rpIdHash,
+ clientDataHash,
+ credentialId,
+ publicKeyU2F
+ ]);
+
+ const validSignature = crypto
+ .createVerify('SHA256')
+ .update(verificationData)
+ .verify(PEMString(attCert), attStmt.sig);
+
+ return {
+ valid: validSignature,
+ publicKey: publicKeyU2F
+ };
+ }
+ }
+};
diff --git a/src/server/api/endpoints/admin/drive/clean-remote-files.ts b/src/server/api/endpoints/admin/drive/clean-remote-files.ts
index 69cfe0db94..e837ae1bb6 100644
--- a/src/server/api/endpoints/admin/drive/clean-remote-files.ts
+++ b/src/server/api/endpoints/admin/drive/clean-remote-files.ts
@@ -1,7 +1,5 @@
-import { Not, IsNull } from 'typeorm';
import define from '../../../define';
-import { deleteFile } from '../../../../../services/drive/delete-file';
-import { DriveFiles } from '../../../../../models';
+import { createCleanRemoteFilesJob } from '../../../../../queue';
export const meta = {
tags: ['admin'],
@@ -11,12 +9,5 @@ export const meta = {
};
export default define(meta, async (ps, me) => {
- const files = await DriveFiles.find({
- userHost: Not(IsNull()),
- isLink: false,
- });
-
- for (const file of files) {
- deleteFile(file, true);
- }
+ createCleanRemoteFilesJob();
});
diff --git a/src/server/api/endpoints/admin/send-email.ts b/src/server/api/endpoints/admin/send-email.ts
new file mode 100644
index 0000000000..fed4f2df14
--- /dev/null
+++ b/src/server/api/endpoints/admin/send-email.ts
@@ -0,0 +1,26 @@
+import $ from 'cafy';
+import define from '../../define';
+import { sendEmail } from '../../../../services/send-email';
+
+export const meta = {
+ tags: ['admin'],
+
+ requireCredential: true,
+ requireModerator: true,
+
+ params: {
+ to: {
+ validator: $.str,
+ },
+ subject: {
+ validator: $.str,
+ },
+ text: {
+ validator: $.str,
+ },
+ }
+};
+
+export default define(meta, async (ps) => {
+ await sendEmail(ps.to, ps.subject, ps.text);
+});
diff --git a/src/server/api/endpoints/admin/vacuum.ts b/src/server/api/endpoints/admin/vacuum.ts
new file mode 100644
index 0000000000..6990706282
--- /dev/null
+++ b/src/server/api/endpoints/admin/vacuum.ts
@@ -0,0 +1,33 @@
+import $ from 'cafy';
+import define from '../../define';
+import { getConnection } from 'typeorm';
+
+export const meta = {
+ tags: ['admin'],
+
+ requireCredential: true,
+ requireModerator: true,
+
+ params: {
+ full: {
+ validator: $.bool,
+ },
+ analyze: {
+ validator: $.bool,
+ },
+ }
+};
+
+export default define(meta, async (ps) => {
+ const params: string[] = [];
+
+ if (ps.full) {
+ params.push('FULL');
+ }
+
+ if (ps.analyze) {
+ params.push('ANALYZE');
+ }
+
+ getConnection().query('VACUUM ' + params.join(' '));
+});
diff --git a/src/server/api/endpoints/ap/show.ts b/src/server/api/endpoints/ap/show.ts
index 9724a044b1..bbaa1fa109 100644
--- a/src/server/api/endpoints/ap/show.ts
+++ b/src/server/api/endpoints/ap/show.ts
@@ -10,7 +10,7 @@ import { Users, Notes } from '../../../../models';
import { Note } from '../../../../models/entities/note';
import { User } from '../../../../models/entities/user';
import { fetchMeta } from '../../../../misc/fetch-meta';
-import { validActor } from '../../../../remote/activitypub/type';
+import { validActor, validPost } from '../../../../remote/activitypub/type';
export const meta = {
tags: ['federation'],
@@ -145,7 +145,7 @@ async function fetchAny(uri: string) {
};
}
- if (['Note', 'Question', 'Article'].includes(object.type)) {
+ if (validPost.includes(object.type)) {
const note = await createNote(object.id, undefined, true);
return {
type: 'Note',
diff --git a/src/server/api/endpoints/app/create.ts b/src/server/api/endpoints/app/create.ts
index 833d5060c5..81c851f3a3 100644
--- a/src/server/api/endpoints/app/create.ts
+++ b/src/server/api/endpoints/app/create.ts
@@ -4,7 +4,6 @@ import define from '../../define';
import { Apps } from '../../../../models';
import { genId } from '../../../../misc/gen-id';
import { unique } from '../../../../prelude/array';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
tags: ['app'],
@@ -53,8 +52,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'App',
},
};
diff --git a/src/server/api/endpoints/app/show.ts b/src/server/api/endpoints/app/show.ts
index e7d3e84388..2c8cdbe396 100644
--- a/src/server/api/endpoints/app/show.ts
+++ b/src/server/api/endpoints/app/show.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { ApiError } from '../../error';
import { Apps } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
tags: ['app'],
@@ -15,8 +14,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'App',
},
diff --git a/src/server/api/endpoints/auth/session/generate.ts b/src/server/api/endpoints/auth/session/generate.ts
index 9bf27c8e77..b38c275deb 100644
--- a/src/server/api/endpoints/auth/session/generate.ts
+++ b/src/server/api/endpoints/auth/session/generate.ts
@@ -5,7 +5,6 @@ import define from '../../../define';
import { ApiError } from '../../../error';
import { Apps, AuthSessions } from '../../../../../models';
import { genId } from '../../../../../misc/gen-id';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
tags: ['auth'],
@@ -28,17 +27,17 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
properties: {
token: {
- type: types.string,
- optional: bool.false, nullable: bool.false,
+ type: 'string' as const,
+ optional: false as const, nullable: false as const,
description: 'セッションのトークン'
},
url: {
- type: types.string,
- optional: bool.false, nullable: bool.false,
+ type: 'string' as const,
+ optional: false as const, nullable: false as const,
format: 'url',
description: 'セッションのURL'
},
diff --git a/src/server/api/endpoints/auth/session/userkey.ts b/src/server/api/endpoints/auth/session/userkey.ts
index b7a58c4750..1dc78eeabd 100644
--- a/src/server/api/endpoints/auth/session/userkey.ts
+++ b/src/server/api/endpoints/auth/session/userkey.ts
@@ -3,7 +3,6 @@ import define from '../../../define';
import { ApiError } from '../../../error';
import { Apps, AuthSessions, AccessTokens, Users } from '../../../../../models';
import { ensure } from '../../../../../prelude/ensure';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
tags: ['auth'],
@@ -29,18 +28,18 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
properties: {
accessToken: {
- type: types.string,
- optional: bool.false, nullable: bool.false,
+ type: 'string' as const,
+ optional: false as const, nullable: false as const,
description: 'ユーザーのアクセストークン',
},
user: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'User',
description: '認証したユーザー'
},
diff --git a/src/server/api/endpoints/blocking/list.ts b/src/server/api/endpoints/blocking/list.ts
index 5ff1dc0c49..c99ba09df0 100644
--- a/src/server/api/endpoints/blocking/list.ts
+++ b/src/server/api/endpoints/blocking/list.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { Blockings } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -33,11 +32,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Blocking',
}
},
diff --git a/src/server/api/endpoints/drive.ts b/src/server/api/endpoints/drive.ts
index 4d4516bd80..bb85bab148 100644
--- a/src/server/api/endpoints/drive.ts
+++ b/src/server/api/endpoints/drive.ts
@@ -1,7 +1,6 @@
import define from '../define';
import { fetchMeta } from '../../../misc/fetch-meta';
import { DriveFiles } from '../../../models';
-import { types, bool } from '../../../misc/schema';
export const meta = {
desc: {
@@ -16,16 +15,16 @@ export const meta = {
kind: 'read:drive',
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
properties: {
capacity: {
- type: types.number,
- optional: bool.false, nullable: bool.false,
+ type: 'number' as const,
+ optional: false as const, nullable: false as const,
},
usage: {
- type: types.number,
- optional: bool.false, nullable: bool.false,
+ type: 'number' as const,
+ optional: false as const, nullable: false as const,
}
}
}
diff --git a/src/server/api/endpoints/drive/files.ts b/src/server/api/endpoints/drive/files.ts
index d10c2a3ef4..77cefdfbe3 100644
--- a/src/server/api/endpoints/drive/files.ts
+++ b/src/server/api/endpoints/drive/files.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { DriveFiles } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -42,11 +41,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'DriveFile',
}
},
diff --git a/src/server/api/endpoints/drive/files/attached-notes.ts b/src/server/api/endpoints/drive/files/attached-notes.ts
index f770bc7136..2b84e114b3 100644
--- a/src/server/api/endpoints/drive/files/attached-notes.ts
+++ b/src/server/api/endpoints/drive/files/attached-notes.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { ApiError } from '../../../error';
import { DriveFiles, Notes } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
stability: 'stable',
@@ -30,11 +29,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/drive/files/check-existence.ts b/src/server/api/endpoints/drive/files/check-existence.ts
index ab19566f1c..a6cd14caf2 100644
--- a/src/server/api/endpoints/drive/files/check-existence.ts
+++ b/src/server/api/endpoints/drive/files/check-existence.ts
@@ -1,7 +1,6 @@
import $ from 'cafy';
import define from '../../../define';
import { DriveFiles } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
desc: {
@@ -25,8 +24,8 @@ export const meta = {
},
res: {
- type: types.boolean,
- optional: bool.false, nullable: bool.false,
+ type: 'boolean' as const,
+ optional: false as const, nullable: false as const,
},
};
diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts
index 0f81a1da99..664a2b87b2 100644
--- a/src/server/api/endpoints/drive/files/create.ts
+++ b/src/server/api/endpoints/drive/files/create.ts
@@ -6,7 +6,6 @@ import define from '../../../define';
import { apiLogger } from '../../../logger';
import { ApiError } from '../../../error';
import { DriveFiles } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
desc: {
@@ -57,8 +56,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'DriveFile',
},
diff --git a/src/server/api/endpoints/drive/files/find-by-hash.ts b/src/server/api/endpoints/drive/files/find-by-hash.ts
index d56e63bc59..84cc4f92b1 100644
--- a/src/server/api/endpoints/drive/files/find-by-hash.ts
+++ b/src/server/api/endpoints/drive/files/find-by-hash.ts
@@ -1,7 +1,6 @@
import $ from 'cafy';
import define from '../../../define';
import { DriveFiles } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
desc: {
@@ -24,11 +23,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'DriveFile',
}
},
diff --git a/src/server/api/endpoints/drive/files/find.ts b/src/server/api/endpoints/drive/files/find.ts
index 82b9a97b6d..732596a33f 100644
--- a/src/server/api/endpoints/drive/files/find.ts
+++ b/src/server/api/endpoints/drive/files/find.ts
@@ -2,7 +2,6 @@ import $ from 'cafy';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { DriveFiles } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
requireCredential: true,
@@ -26,11 +25,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'DriveFile',
}
},
diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts
index 8e74361f9c..4384b2114d 100644
--- a/src/server/api/endpoints/drive/files/show.ts
+++ b/src/server/api/endpoints/drive/files/show.ts
@@ -4,7 +4,6 @@ import define from '../../../define';
import { ApiError } from '../../../error';
import { DriveFile } from '../../../../../models/entities/drive-file';
import { DriveFiles } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
stability: 'stable',
@@ -39,8 +38,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'DriveFile',
},
diff --git a/src/server/api/endpoints/drive/folders.ts b/src/server/api/endpoints/drive/folders.ts
index dc3174cd2a..e2d22e7081 100644
--- a/src/server/api/endpoints/drive/folders.ts
+++ b/src/server/api/endpoints/drive/folders.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { DriveFolders } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -38,11 +37,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'DriveFolder',
}
},
diff --git a/src/server/api/endpoints/drive/folders/find.ts b/src/server/api/endpoints/drive/folders/find.ts
index 0368d026c3..04bec1b170 100644
--- a/src/server/api/endpoints/drive/folders/find.ts
+++ b/src/server/api/endpoints/drive/folders/find.ts
@@ -2,7 +2,6 @@ import $ from 'cafy';
import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { DriveFolders } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
tags: ['drive'],
@@ -26,11 +25,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'DriveFolder',
}
},
diff --git a/src/server/api/endpoints/drive/folders/show.ts b/src/server/api/endpoints/drive/folders/show.ts
index a020b46aa9..f48f21d730 100644
--- a/src/server/api/endpoints/drive/folders/show.ts
+++ b/src/server/api/endpoints/drive/folders/show.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { ApiError } from '../../../error';
import { DriveFolders } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
stability: 'stable',
@@ -30,8 +29,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'DriveFolder',
},
diff --git a/src/server/api/endpoints/drive/stream.ts b/src/server/api/endpoints/drive/stream.ts
index f75c4273c3..b93ee11a14 100644
--- a/src/server/api/endpoints/drive/stream.ts
+++ b/src/server/api/endpoints/drive/stream.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { DriveFiles } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
tags: ['drive'],
@@ -32,11 +31,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'DriveFile',
}
},
diff --git a/src/server/api/endpoints/hashtags/list.ts b/src/server/api/endpoints/hashtags/list.ts
index 9023f11913..9bc2677793 100644
--- a/src/server/api/endpoints/hashtags/list.ts
+++ b/src/server/api/endpoints/hashtags/list.ts
@@ -1,7 +1,6 @@
import $ from 'cafy';
import define from '../../define';
import { Hashtags } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
tags: ['hashtags'],
@@ -48,11 +47,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Hashtag',
}
},
diff --git a/src/server/api/endpoints/hashtags/search.ts b/src/server/api/endpoints/hashtags/search.ts
index 0d2704d01c..7caaf34846 100644
--- a/src/server/api/endpoints/hashtags/search.ts
+++ b/src/server/api/endpoints/hashtags/search.ts
@@ -1,7 +1,6 @@
import $ from 'cafy';
import define from '../../define';
import { Hashtags } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -38,11 +37,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.string,
- optional: bool.false, nullable: bool.false,
+ type: 'string' as const,
+ optional: false as const, nullable: false as const,
}
},
};
diff --git a/src/server/api/endpoints/hashtags/show.ts b/src/server/api/endpoints/hashtags/show.ts
index 72a4cc7c87..5de906fb1f 100644
--- a/src/server/api/endpoints/hashtags/show.ts
+++ b/src/server/api/endpoints/hashtags/show.ts
@@ -2,7 +2,6 @@ import $ from 'cafy';
import define from '../../define';
import { ApiError } from '../../error';
import { Hashtags } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -24,8 +23,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Hashtag',
},
diff --git a/src/server/api/endpoints/hashtags/trend.ts b/src/server/api/endpoints/hashtags/trend.ts
index 53a3514718..3154461e5a 100644
--- a/src/server/api/endpoints/hashtags/trend.ts
+++ b/src/server/api/endpoints/hashtags/trend.ts
@@ -2,7 +2,6 @@ import define from '../../define';
import { fetchMeta } from '../../../../misc/fetch-meta';
import { Notes } from '../../../../models';
import { Note } from '../../../../models/entities/note';
-import { types, bool } from '../../../../misc/schema';
/*
トレンドに載るためには「『直近a分間のユニーク投稿数が今からa分前~今からb分前の間のユニーク投稿数のn倍以上』のハッシュタグの上位5位以内に入る」ことが必要
@@ -24,27 +23,27 @@ export const meta = {
requireCredential: false,
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
properties: {
tag: {
- type: types.string,
- optional: bool.false, nullable: bool.false,
+ type: 'string' as const,
+ optional: false as const, nullable: false as const,
},
chart: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.number,
- optional: bool.false, nullable: bool.false,
+ type: 'number' as const,
+ optional: false as const, nullable: false as const,
}
},
usersCount: {
- type: types.number,
- optional: bool.false, nullable: bool.false,
+ type: 'number' as const,
+ optional: false as const, nullable: false as const,
}
}
}
diff --git a/src/server/api/endpoints/hashtags/users.ts b/src/server/api/endpoints/hashtags/users.ts
index b842f9de64..59210f4604 100644
--- a/src/server/api/endpoints/hashtags/users.ts
+++ b/src/server/api/endpoints/hashtags/users.ts
@@ -1,7 +1,6 @@
import $ from 'cafy';
import define from '../../define';
import { Users } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
requireCredential: false,
@@ -48,11 +47,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'User',
}
},
diff --git a/src/server/api/endpoints/i.ts b/src/server/api/endpoints/i.ts
index 4ecd507e16..20a0c604f6 100644
--- a/src/server/api/endpoints/i.ts
+++ b/src/server/api/endpoints/i.ts
@@ -1,6 +1,5 @@
import define from '../define';
import { Users } from '../../../models';
-import { types, bool } from '../../../misc/schema';
export const meta = {
stability: 'stable',
@@ -16,8 +15,8 @@ export const meta = {
params: {},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'User',
},
};
diff --git a/src/server/api/endpoints/i/2fa/getkeys.ts b/src/server/api/endpoints/i/2fa/getkeys.ts
new file mode 100644
index 0000000000..bb1585d795
--- /dev/null
+++ b/src/server/api/endpoints/i/2fa/getkeys.ts
@@ -0,0 +1,67 @@
+import $ from 'cafy';
+import * as bcrypt from 'bcryptjs';
+import * as crypto from 'crypto';
+import define from '../../../define';
+import { UserProfiles, UserSecurityKeys, AttestationChallenges } from '../../../../../models';
+import { ensure } from '../../../../../prelude/ensure';
+import { promisify } from 'util';
+import { hash } from '../../../2fa';
+import { genId } from '../../../../../misc/gen-id';
+
+export const meta = {
+ requireCredential: true,
+
+ secure: true,
+
+ params: {
+ password: {
+ validator: $.str
+ }
+ }
+};
+
+const randomBytes = promisify(crypto.randomBytes);
+
+export default define(meta, async (ps, user) => {
+ const profile = await UserProfiles.findOne(user.id).then(ensure);
+
+ // Compare password
+ const same = await bcrypt.compare(ps.password, profile.password!);
+
+ if (!same) {
+ throw new Error('incorrect password');
+ }
+
+ const keys = await UserSecurityKeys.find({
+ userId: user.id
+ });
+
+ if (keys.length === 0) {
+ throw new Error('no keys found');
+ }
+
+ // 32 byte challenge
+ const entropy = await randomBytes(32);
+ const challenge = entropy.toString('base64')
+ .replace(/=/g, '')
+ .replace(/\+/g, '-')
+ .replace(/\//g, '_');
+
+ const challengeId = genId();
+
+ await AttestationChallenges.save({
+ userId: user.id,
+ id: challengeId,
+ challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'),
+ createdAt: new Date(),
+ registrationChallenge: false
+ });
+
+ return {
+ challenge,
+ challengeId,
+ securityKeys: keys.map(key => ({
+ id: key.id
+ }))
+ };
+});
diff --git a/src/server/api/endpoints/i/2fa/key-done.ts b/src/server/api/endpoints/i/2fa/key-done.ts
new file mode 100644
index 0000000000..d751dabc41
--- /dev/null
+++ b/src/server/api/endpoints/i/2fa/key-done.ts
@@ -0,0 +1,151 @@
+import $ from 'cafy';
+import * as bcrypt from 'bcryptjs';
+import { promisify } from 'util';
+import * as cbor from 'cbor';
+import define from '../../../define';
+import {
+ UserProfiles,
+ UserSecurityKeys,
+ AttestationChallenges,
+ Users
+} from '../../../../../models';
+import { ensure } from '../../../../../prelude/ensure';
+import config from '../../../../../config';
+import { procedures, hash } from '../../../2fa';
+import { publishMainStream } from '../../../../../services/stream';
+
+const cborDecodeFirst = promisify(cbor.decodeFirst) as any;
+
+export const meta = {
+ requireCredential: true,
+
+ secure: true,
+
+ params: {
+ clientDataJSON: {
+ validator: $.str
+ },
+ attestationObject: {
+ validator: $.str
+ },
+ password: {
+ validator: $.str
+ },
+ challengeId: {
+ validator: $.str
+ },
+ name: {
+ validator: $.str
+ }
+ }
+};
+
+const rpIdHashReal = hash(Buffer.from(config.hostname, 'utf-8'));
+
+export default define(meta, async (ps, user) => {
+ const profile = await UserProfiles.findOne(user.id).then(ensure);
+
+ // Compare password
+ const same = await bcrypt.compare(ps.password, profile.password!);
+
+ if (!same) {
+ throw new Error('incorrect password');
+ }
+
+ if (!profile.twoFactorEnabled) {
+ throw new Error('2fa not enabled');
+ }
+
+ const clientData = JSON.parse(ps.clientDataJSON);
+
+ if (clientData.type != 'webauthn.create') {
+ throw new Error('not a creation attestation');
+ }
+ if (clientData.origin != config.scheme + '://' + config.host) {
+ throw new Error('origin mismatch');
+ }
+
+ const clientDataJSONHash = hash(Buffer.from(ps.clientDataJSON, 'utf-8'));
+
+ const attestation = await cborDecodeFirst(ps.attestationObject);
+
+ const rpIdHash = attestation.authData.slice(0, 32);
+ if (!rpIdHashReal.equals(rpIdHash)) {
+ throw new Error('rpIdHash mismatch');
+ }
+
+ const flags = attestation.authData[32];
+
+ // tslint:disable-next-line:no-bitwise
+ if (!(flags & 1)) {
+ throw new Error('user not present');
+ }
+
+ const authData = Buffer.from(attestation.authData);
+ const credentialIdLength = authData.readUInt16BE(53);
+ const credentialId = authData.slice(55, 55 + credentialIdLength);
+ const publicKeyData = authData.slice(55 + credentialIdLength);
+ const publicKey: Map<number, any> = await cborDecodeFirst(publicKeyData);
+ if (publicKey.get(3) != -7) {
+ throw new Error('alg mismatch');
+ }
+
+ if (!(procedures as any)[attestation.fmt]) {
+ throw new Error('unsupported fmt');
+ }
+
+ const verificationData = (procedures as any)[attestation.fmt].verify({
+ attStmt: attestation.attStmt,
+ authenticatorData: authData,
+ clientDataHash: clientDataJSONHash,
+ credentialId,
+ publicKey,
+ rpIdHash
+ });
+ if (!verificationData.valid) throw new Error('signature invalid');
+
+ const attestationChallenge = await AttestationChallenges.findOne({
+ userId: user.id,
+ id: ps.challengeId,
+ registrationChallenge: true,
+ challenge: hash(clientData.challenge).toString('hex')
+ });
+
+ if (!attestationChallenge) {
+ throw new Error('non-existent challenge');
+ }
+
+ await AttestationChallenges.delete({
+ userId: user.id,
+ id: ps.challengeId
+ });
+
+ // Expired challenge (> 5min old)
+ if (
+ new Date().getTime() - attestationChallenge.createdAt.getTime() >=
+ 5 * 60 * 1000
+ ) {
+ throw new Error('expired challenge');
+ }
+
+ const credentialIdString = credentialId.toString('hex');
+
+ await UserSecurityKeys.save({
+ userId: user.id,
+ id: credentialIdString,
+ lastUsed: new Date(),
+ name: ps.name,
+ publicKey: verificationData.publicKey.toString('hex')
+ });
+
+ // Publish meUpdated event
+ publishMainStream(user.id, 'meUpdated', await Users.pack(user.id, user, {
+ detail: true,
+ includeSecrets: true
+ }));
+
+ return {
+ id: credentialIdString,
+ name: ps.name
+ };
+});
diff --git a/src/server/api/endpoints/i/2fa/register-key.ts b/src/server/api/endpoints/i/2fa/register-key.ts
new file mode 100644
index 0000000000..1c2cc32e37
--- /dev/null
+++ b/src/server/api/endpoints/i/2fa/register-key.ts
@@ -0,0 +1,60 @@
+import $ from 'cafy';
+import * as bcrypt from 'bcryptjs';
+import define from '../../../define';
+import { UserProfiles, AttestationChallenges } from '../../../../../models';
+import { ensure } from '../../../../../prelude/ensure';
+import { promisify } from 'util';
+import * as crypto from 'crypto';
+import { genId } from '../../../../../misc/gen-id';
+import { hash } from '../../../2fa';
+
+const randomBytes = promisify(crypto.randomBytes);
+
+export const meta = {
+ requireCredential: true,
+
+ secure: true,
+
+ params: {
+ password: {
+ validator: $.str
+ }
+ }
+};
+
+export default define(meta, async (ps, user) => {
+ const profile = await UserProfiles.findOne(user.id).then(ensure);
+
+ // Compare password
+ const same = await bcrypt.compare(ps.password, profile.password!);
+
+ if (!same) {
+ throw new Error('incorrect password');
+ }
+
+ if (!profile.twoFactorEnabled) {
+ throw new Error('2fa not enabled');
+ }
+
+ // 32 byte challenge
+ const entropy = await randomBytes(32);
+ const challenge = entropy.toString('base64')
+ .replace(/=/g, '')
+ .replace(/\+/g, '-')
+ .replace(/\//g, '_');
+
+ const challengeId = genId();
+
+ await AttestationChallenges.save({
+ userId: user.id,
+ id: challengeId,
+ challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'),
+ createdAt: new Date(),
+ registrationChallenge: true
+ });
+
+ return {
+ challengeId,
+ challenge
+ };
+});
diff --git a/src/server/api/endpoints/i/2fa/remove-key.ts b/src/server/api/endpoints/i/2fa/remove-key.ts
new file mode 100644
index 0000000000..cb28c8fbfb
--- /dev/null
+++ b/src/server/api/endpoints/i/2fa/remove-key.ts
@@ -0,0 +1,46 @@
+import $ from 'cafy';
+import * as bcrypt from 'bcryptjs';
+import define from '../../../define';
+import { UserProfiles, UserSecurityKeys, Users } from '../../../../../models';
+import { ensure } from '../../../../../prelude/ensure';
+import { publishMainStream } from '../../../../../services/stream';
+
+export const meta = {
+ requireCredential: true,
+
+ secure: true,
+
+ params: {
+ password: {
+ validator: $.str
+ },
+ credentialId: {
+ validator: $.str
+ },
+ }
+};
+
+export default define(meta, async (ps, user) => {
+ const profile = await UserProfiles.findOne(user.id).then(ensure);
+
+ // Compare password
+ const same = await bcrypt.compare(ps.password, profile.password!);
+
+ if (!same) {
+ throw new Error('incorrect password');
+ }
+
+ // Make sure we only delete the user's own creds
+ await UserSecurityKeys.delete({
+ userId: user.id,
+ id: ps.credentialId
+ });
+
+ // Publish meUpdated event
+ publishMainStream(user.id, 'meUpdated', await Users.pack(user.id, user, {
+ detail: true,
+ includeSecrets: true
+ }));
+
+ return {};
+});
diff --git a/src/server/api/endpoints/i/favorites.ts b/src/server/api/endpoints/i/favorites.ts
index d1e90dd15c..1bbc16256a 100644
--- a/src/server/api/endpoints/i/favorites.ts
+++ b/src/server/api/endpoints/i/favorites.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { NoteFavorites } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -33,11 +32,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'NoteFavorite',
}
},
diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts
index 41513e5daa..aa72e9a176 100644
--- a/src/server/api/endpoints/i/notifications.ts
+++ b/src/server/api/endpoints/i/notifications.ts
@@ -4,7 +4,6 @@ import { readNotification } from '../../common/read-notification';
import define from '../../define';
import { makePaginationQuery } from '../../common/make-pagination-query';
import { Notifications, Followings, Mutings } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -54,11 +53,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Notification',
}
},
diff --git a/src/server/api/endpoints/messaging/history.ts b/src/server/api/endpoints/messaging/history.ts
index 833ec37e4c..2c143c26b5 100644
--- a/src/server/api/endpoints/messaging/history.ts
+++ b/src/server/api/endpoints/messaging/history.ts
@@ -3,7 +3,6 @@ import define from '../../define';
import { MessagingMessage } from '../../../../models/entities/messaging-message';
import { MessagingMessages, Mutings, UserGroupJoinings } from '../../../../models';
import { Brackets } from 'typeorm';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -30,11 +29,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'MessagingMessage',
}
},
diff --git a/src/server/api/endpoints/messaging/messages.ts b/src/server/api/endpoints/messaging/messages.ts
index ffd58c714e..b0b3e20d02 100644
--- a/src/server/api/endpoints/messaging/messages.ts
+++ b/src/server/api/endpoints/messaging/messages.ts
@@ -5,7 +5,6 @@ import { ApiError } from '../../error';
import { getUser } from '../../common/getters';
import { MessagingMessages, UserGroups, UserGroupJoinings } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
-import { types, bool } from '../../../../misc/schema';
import { Brackets } from 'typeorm';
import { readUserMessagingMessage, readGroupMessagingMessage } from '../../common/read-messaging-message';
@@ -58,11 +57,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'MessagingMessage',
}
},
diff --git a/src/server/api/endpoints/messaging/messages/create.ts b/src/server/api/endpoints/messaging/messages/create.ts
index feffc9a0c6..d0c1ee4941 100644
--- a/src/server/api/endpoints/messaging/messages/create.ts
+++ b/src/server/api/endpoints/messaging/messages/create.ts
@@ -9,7 +9,6 @@ import { getUser } from '../../../common/getters';
import { MessagingMessages, DriveFiles, Mutings, UserGroups, UserGroupJoinings } from '../../../../../models';
import { MessagingMessage } from '../../../../../models/entities/messaging-message';
import { genId } from '../../../../../misc/gen-id';
-import { types, bool } from '../../../../../misc/schema';
import { User } from '../../../../../models/entities/user';
import { UserGroup } from '../../../../../models/entities/user-group';
import { Not } from 'typeorm';
@@ -53,8 +52,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'MessagingMessage',
},
diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts
index 1aa9a855dd..a3390a011d 100644
--- a/src/server/api/endpoints/meta.ts
+++ b/src/server/api/endpoints/meta.ts
@@ -5,7 +5,6 @@ import define from '../define';
import { fetchMeta } from '../../../misc/fetch-meta';
import * as pkg from '../../../../package.json';
import { Emojis } from '../../../models';
-import { types, bool } from '../../../misc/schema';
import { getConnection } from 'typeorm';
import redis from '../../../db/redis';
@@ -29,40 +28,40 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
properties: {
version: {
- type: types.string,
- optional: bool.false, nullable: bool.false,
+ type: 'string' as const,
+ optional: false as const, nullable: false as const,
description: 'The version of Misskey of this instance.',
example: pkg.version
},
name: {
- type: types.string,
- optional: bool.false, nullable: bool.false,
+ type: 'string' as const,
+ optional: false as const, nullable: false as const,
description: 'The name of this instance.',
},
description: {
- type: types.string,
- optional: bool.false, nullable: bool.false,
+ type: 'string' as const,
+ optional: false as const, nullable: false as const,
description: 'The description of this instance.',
},
announcements: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
properties: {
title: {
- type: types.string,
- optional: bool.false, nullable: bool.false,
+ type: 'string' as const,
+ optional: false as const, nullable: false as const,
description: 'The title of the announcement.',
},
text: {
- type: types.string,
- optional: bool.false, nullable: bool.false,
+ type: 'string' as const,
+ optional: false as const, nullable: false as const,
description: 'The text of the announcement. (can be HTML)',
},
}
@@ -70,23 +69,23 @@ export const meta = {
description: 'The announcements of this instance.',
},
disableRegistration: {
- type: types.boolean,
- optional: bool.false, nullable: bool.false,
+ type: 'boolean' as const,
+ optional: false as const, nullable: false as const,
description: 'Whether disabled open registration.',
},
disableLocalTimeline: {
- type: types.boolean,
- optional: bool.false, nullable: bool.false,
+ type: 'boolean' as const,
+ optional: false as const, nullable: false as const,
description: 'Whether disabled LTL and STL.',
},
disableGlobalTimeline: {
- type: types.boolean,
- optional: bool.false, nullable: bool.false,
+ type: 'boolean' as const,
+ optional: false as const, nullable: false as const,
description: 'Whether disabled GTL.',
},
enableEmojiReaction: {
- type: types.boolean,
- optional: bool.false, nullable: bool.false,
+ type: 'boolean' as const,
+ optional: false as const, nullable: false as const,
description: 'Whether enabled emoji reaction.',
},
}
diff --git a/src/server/api/endpoints/mute/list.ts b/src/server/api/endpoints/mute/list.ts
index f9ea380c76..1afc120f5f 100644
--- a/src/server/api/endpoints/mute/list.ts
+++ b/src/server/api/endpoints/mute/list.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { makePaginationQuery } from '../../common/make-pagination-query';
import { Mutings } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -33,11 +32,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Muting',
}
},
diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts
index 7aa19c9a2b..fab8455d78 100644
--- a/src/server/api/endpoints/notes.ts
+++ b/src/server/api/endpoints/notes.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../misc/cafy-id';
import define from '../define';
import { makePaginationQuery } from '../common/make-pagination-query';
import { Notes } from '../../../models';
-import { types, bool } from '../../../misc/schema';
export const meta = {
desc: {
@@ -63,11 +62,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/children.ts b/src/server/api/endpoints/notes/children.ts
index e8861eafa1..bc8407f31c 100644
--- a/src/server/api/endpoints/notes/children.ts
+++ b/src/server/api/endpoints/notes/children.ts
@@ -6,7 +6,6 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
import { generateMuteQuery } from '../../common/generate-mute-query';
import { Brackets } from 'typeorm';
import { Notes } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -42,11 +41,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/conversation.ts b/src/server/api/endpoints/notes/conversation.ts
index acd3ac75ef..245d427923 100644
--- a/src/server/api/endpoints/notes/conversation.ts
+++ b/src/server/api/endpoints/notes/conversation.ts
@@ -5,7 +5,6 @@ import { ApiError } from '../../error';
import { getNote } from '../../common/getters';
import { Note } from '../../../../models/entities/note';
import { Notes } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -38,11 +37,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts
index 46db274581..1650de9071 100644
--- a/src/server/api/endpoints/notes/create.ts
+++ b/src/server/api/endpoints/notes/create.ts
@@ -10,7 +10,6 @@ import { User } from '../../../../models/entities/user';
import { Users, DriveFiles, Notes } from '../../../../models';
import { DriveFile } from '../../../../models/entities/drive-file';
import { Note } from '../../../../models/entities/note';
-import { types, bool } from '../../../../misc/schema';
let maxNoteTextLength = 1000;
@@ -175,12 +174,12 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
properties: {
createdNote: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
description: '作成した投稿'
}
diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts
index 64750815b0..0a1d8668b0 100644
--- a/src/server/api/endpoints/notes/featured.ts
+++ b/src/server/api/endpoints/notes/featured.ts
@@ -2,7 +2,6 @@ import $ from 'cafy';
import define from '../../define';
import { generateMuteQuery } from '../../common/generate-mute-query';
import { Notes } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -25,11 +24,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts
index f46fa208df..8654cf889a 100644
--- a/src/server/api/endpoints/notes/global-timeline.ts
+++ b/src/server/api/endpoints/notes/global-timeline.ts
@@ -7,7 +7,6 @@ import { makePaginationQuery } from '../../common/make-pagination-query';
import { Notes } from '../../../../models';
import { generateMuteQuery } from '../../common/generate-mute-query';
import { activeUsersChart } from '../../../../services/chart';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -47,11 +46,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts
index 7be13fc47f..8c4c7a60b9 100644
--- a/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -9,7 +9,6 @@ import { Brackets } from 'typeorm';
import { generateVisibilityQuery } from '../../common/generate-visibility-query';
import { generateMuteQuery } from '../../common/generate-mute-query';
import { activeUsersChart } from '../../../../services/chart';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -90,11 +89,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts
index 73cbebace2..c688b9325e 100644
--- a/src/server/api/endpoints/notes/local-timeline.ts
+++ b/src/server/api/endpoints/notes/local-timeline.ts
@@ -9,7 +9,6 @@ import { makePaginationQuery } from '../../common/make-pagination-query';
import { generateVisibilityQuery } from '../../common/generate-visibility-query';
import { activeUsersChart } from '../../../../services/chart';
import { Brackets } from 'typeorm';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -64,11 +63,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts
index 02e44492ba..fd3767f632 100644
--- a/src/server/api/endpoints/notes/mentions.ts
+++ b/src/server/api/endpoints/notes/mentions.ts
@@ -7,7 +7,6 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
import { generateMuteQuery } from '../../common/generate-mute-query';
import { makePaginationQuery } from '../../common/make-pagination-query';
import { Brackets } from 'typeorm';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -44,11 +43,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/reactions.ts b/src/server/api/endpoints/notes/reactions.ts
index 0773b4faa2..7bea24d316 100644
--- a/src/server/api/endpoints/notes/reactions.ts
+++ b/src/server/api/endpoints/notes/reactions.ts
@@ -4,7 +4,6 @@ import define from '../../define';
import { getNote } from '../../common/getters';
import { ApiError } from '../../error';
import { NoteReactions } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -45,11 +44,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'NoteReaction',
}
},
diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts
index 00dfac3770..a5db706e32 100644
--- a/src/server/api/endpoints/notes/renotes.ts
+++ b/src/server/api/endpoints/notes/renotes.ts
@@ -7,7 +7,6 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
import { generateMuteQuery } from '../../common/generate-mute-query';
import { makePaginationQuery } from '../../common/make-pagination-query';
import { Notes } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -43,11 +42,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts
index 5fb0fd989f..cd38d41652 100644
--- a/src/server/api/endpoints/notes/replies.ts
+++ b/src/server/api/endpoints/notes/replies.ts
@@ -5,7 +5,6 @@ import { Notes } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
import { generateVisibilityQuery } from '../../common/generate-visibility-query';
import { generateMuteQuery } from '../../common/generate-mute-query';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -47,11 +46,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts
index 0b49f896ad..7f53b26995 100644
--- a/src/server/api/endpoints/notes/search-by-tag.ts
+++ b/src/server/api/endpoints/notes/search-by-tag.ts
@@ -6,7 +6,6 @@ import { Notes } from '../../../../models';
import { generateMuteQuery } from '../../common/generate-mute-query';
import { generateVisibilityQuery } from '../../common/generate-visibility-query';
import { Brackets } from 'typeorm';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -82,11 +81,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts
index 65ce20074a..d3fb33c420 100644
--- a/src/server/api/endpoints/notes/search.ts
+++ b/src/server/api/endpoints/notes/search.ts
@@ -4,7 +4,6 @@ import define from '../../define';
import { ApiError } from '../../error';
import { Notes } from '../../../../models';
import { In } from 'typeorm';
-import { types, bool } from '../../../../misc/schema';
import { ID } from '../../../../misc/cafy-id';
export const meta = {
@@ -44,11 +43,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/show.ts b/src/server/api/endpoints/notes/show.ts
index 54b420813d..75abbae55f 100644
--- a/src/server/api/endpoints/notes/show.ts
+++ b/src/server/api/endpoints/notes/show.ts
@@ -4,7 +4,6 @@ import define from '../../define';
import { getNote } from '../../common/getters';
import { ApiError } from '../../error';
import { Notes } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
stability: 'stable',
@@ -29,8 +28,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
},
diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index f9442f8b90..25876f655a 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -7,7 +7,6 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
import { generateMuteQuery } from '../../common/generate-mute-query';
import { activeUsersChart } from '../../../../services/chart';
import { Brackets } from 'typeorm';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -89,11 +88,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts
index c16018d434..f66221537d 100644
--- a/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -6,7 +6,6 @@ import { UserLists, UserListJoinings, Notes } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
import { generateVisibilityQuery } from '../../common/generate-visibility-query';
import { activeUsersChart } from '../../../../services/chart';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -95,11 +94,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/pages/create.ts b/src/server/api/endpoints/pages/create.ts
index e6b813648b..ffe0d38ea6 100644
--- a/src/server/api/endpoints/pages/create.ts
+++ b/src/server/api/endpoints/pages/create.ts
@@ -2,7 +2,6 @@ import $ from 'cafy';
import * as ms from 'ms';
import define from '../../define';
import { ID } from '../../../../misc/cafy-id';
-import { types, bool } from '../../../../misc/schema';
import { Pages, DriveFiles } from '../../../../models';
import { genId } from '../../../../misc/gen-id';
import { Page } from '../../../../models/entities/page';
@@ -61,8 +60,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Page',
},
diff --git a/src/server/api/endpoints/pages/show.ts b/src/server/api/endpoints/pages/show.ts
index e3d6e6a15f..84808418f3 100644
--- a/src/server/api/endpoints/pages/show.ts
+++ b/src/server/api/endpoints/pages/show.ts
@@ -2,7 +2,6 @@ import $ from 'cafy';
import define from '../../define';
import { ApiError } from '../../error';
import { Pages, Users } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
import { ID } from '../../../../misc/cafy-id';
import { Page } from '../../../../models/entities/page';
@@ -34,8 +33,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Page',
},
diff --git a/src/server/api/endpoints/pinned-users.ts b/src/server/api/endpoints/pinned-users.ts
index de0e17a2ec..853e1cd4b8 100644
--- a/src/server/api/endpoints/pinned-users.ts
+++ b/src/server/api/endpoints/pinned-users.ts
@@ -1,6 +1,5 @@
import define from '../define';
import { Users } from '../../../models';
-import { types, bool } from '../../../misc/schema';
import { fetchMeta } from '../../../misc/fetch-meta';
import parseAcct from '../../../misc/acct/parse';
import { User } from '../../../models/entities/user';
@@ -14,11 +13,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'User',
}
},
diff --git a/src/server/api/endpoints/stats.ts b/src/server/api/endpoints/stats.ts
index 4dca62445f..c86d893d5e 100644
--- a/src/server/api/endpoints/stats.ts
+++ b/src/server/api/endpoints/stats.ts
@@ -1,7 +1,6 @@
import define from '../define';
import { Notes, Users } from '../../../models';
import { federationChart, driveChart } from '../../../services/chart';
-import { bool, types } from '../../../misc/schema';
export const meta = {
requireCredential: false,
@@ -16,32 +15,32 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
properties: {
notesCount: {
- type: types.number,
- optional: bool.false, nullable: bool.false,
+ type: 'number' as const,
+ optional: false as const, nullable: false as const,
description: 'The count of all (local/remote) notes of this instance.',
},
originalNotesCount: {
- type: types.number,
- optional: bool.false, nullable: bool.false,
+ type: 'number' as const,
+ optional: false as const, nullable: false as const,
description: 'The count of all local notes of this instance.',
},
usersCount: {
- type: types.number,
- optional: bool.false, nullable: bool.false,
+ type: 'number' as const,
+ optional: false as const, nullable: false as const,
description: 'The count of all (local/remote) accounts of this instance.',
},
originalUsersCount: {
- type: types.number,
- optional: bool.false, nullable: bool.false,
+ type: 'number' as const,
+ optional: false as const, nullable: false as const,
description: 'The count of all local accounts of this instance.',
},
instances: {
- type: types.number,
- optional: bool.false, nullable: bool.false,
+ type: 'number' as const,
+ optional: false as const, nullable: false as const,
description: 'The count of federated instances.',
},
}
diff --git a/src/server/api/endpoints/users.ts b/src/server/api/endpoints/users.ts
index 539f2ef897..93cf01a6f8 100644
--- a/src/server/api/endpoints/users.ts
+++ b/src/server/api/endpoints/users.ts
@@ -2,7 +2,6 @@ import $ from 'cafy';
import define from '../define';
import { Users } from '../../../models';
import { generateMuteQueryForUsers } from '../common/generate-mute-query';
-import { types, bool } from '../../../misc/schema';
export const meta = {
tags: ['users'],
@@ -53,11 +52,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'User',
}
},
diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts
index 465b71e2e6..68c32fe983 100644
--- a/src/server/api/endpoints/users/followers.ts
+++ b/src/server/api/endpoints/users/followers.ts
@@ -5,7 +5,6 @@ import { ApiError } from '../../error';
import { Users, Followings } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
import { toPunyNullable } from '../../../../misc/convert-host';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -49,11 +48,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Following',
}
},
diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts
index 2a7748ac64..eb699b2903 100644
--- a/src/server/api/endpoints/users/following.ts
+++ b/src/server/api/endpoints/users/following.ts
@@ -5,7 +5,6 @@ import { ApiError } from '../../error';
import { Users, Followings } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
import { toPunyNullable } from '../../../../misc/convert-host';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -49,11 +48,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Following',
}
},
diff --git a/src/server/api/endpoints/users/get-frequently-replied-users.ts b/src/server/api/endpoints/users/get-frequently-replied-users.ts
index 24d1bd194c..1a17b488f0 100644
--- a/src/server/api/endpoints/users/get-frequently-replied-users.ts
+++ b/src/server/api/endpoints/users/get-frequently-replied-users.ts
@@ -6,7 +6,6 @@ import { ApiError } from '../../error';
import { getUser } from '../../common/getters';
import { Not, In, IsNull } from 'typeorm';
import { Notes, Users } from '../../../../models';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
tags: ['users'],
@@ -29,11 +28,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'User',
}
},
diff --git a/src/server/api/endpoints/users/groups/create.ts b/src/server/api/endpoints/users/groups/create.ts
index ee6cade8d0..2a6e5135e5 100644
--- a/src/server/api/endpoints/users/groups/create.ts
+++ b/src/server/api/endpoints/users/groups/create.ts
@@ -3,7 +3,6 @@ import define from '../../../define';
import { UserGroups, UserGroupJoinings } from '../../../../../models';
import { genId } from '../../../../../misc/gen-id';
import { UserGroup } from '../../../../../models/entities/user-group';
-import { types, bool } from '../../../../../misc/schema';
import { UserGroupJoining } from '../../../../../models/entities/user-group-joining';
export const meta = {
@@ -25,8 +24,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'UserGroup',
},
};
diff --git a/src/server/api/endpoints/users/groups/joined.ts b/src/server/api/endpoints/users/groups/joined.ts
index 97d168e527..c60ba57c09 100644
--- a/src/server/api/endpoints/users/groups/joined.ts
+++ b/src/server/api/endpoints/users/groups/joined.ts
@@ -1,6 +1,5 @@
import define from '../../../define';
import { UserGroups, UserGroupJoinings } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
import { Not, In } from 'typeorm';
export const meta = {
@@ -15,11 +14,11 @@ export const meta = {
kind: 'read:user-groups',
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'UserGroup',
}
},
diff --git a/src/server/api/endpoints/users/groups/owned.ts b/src/server/api/endpoints/users/groups/owned.ts
index 6cf39a142b..e2c0bf2fc1 100644
--- a/src/server/api/endpoints/users/groups/owned.ts
+++ b/src/server/api/endpoints/users/groups/owned.ts
@@ -1,6 +1,5 @@
import define from '../../../define';
import { UserGroups } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
desc: {
@@ -14,11 +13,11 @@ export const meta = {
kind: 'read:user-groups',
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'UserGroup',
}
},
diff --git a/src/server/api/endpoints/users/groups/show.ts b/src/server/api/endpoints/users/groups/show.ts
index 4f8374a222..643f1acf7a 100644
--- a/src/server/api/endpoints/users/groups/show.ts
+++ b/src/server/api/endpoints/users/groups/show.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { ApiError } from '../../../error';
import { UserGroups, UserGroupJoinings } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
desc: {
@@ -24,8 +23,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'UserGroup',
},
diff --git a/src/server/api/endpoints/users/groups/transfer.ts b/src/server/api/endpoints/users/groups/transfer.ts
index b4284ab484..012f9f4ef3 100644
--- a/src/server/api/endpoints/users/groups/transfer.ts
+++ b/src/server/api/endpoints/users/groups/transfer.ts
@@ -4,7 +4,6 @@ import define from '../../../define';
import { ApiError } from '../../../error';
import { getUser } from '../../../common/getters';
import { UserGroups, UserGroupJoinings } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
desc: {
@@ -33,8 +32,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'UserGroup',
},
diff --git a/src/server/api/endpoints/users/groups/update.ts b/src/server/api/endpoints/users/groups/update.ts
index bc974621a3..2fc0a803a1 100644
--- a/src/server/api/endpoints/users/groups/update.ts
+++ b/src/server/api/endpoints/users/groups/update.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { ApiError } from '../../../error';
import { UserGroups } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
desc: {
@@ -36,8 +35,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'UserGroup',
},
diff --git a/src/server/api/endpoints/users/lists/create.ts b/src/server/api/endpoints/users/lists/create.ts
index 79efffbf9e..28af3c8d86 100644
--- a/src/server/api/endpoints/users/lists/create.ts
+++ b/src/server/api/endpoints/users/lists/create.ts
@@ -3,7 +3,6 @@ import define from '../../../define';
import { UserLists } from '../../../../../models';
import { genId } from '../../../../../misc/gen-id';
import { UserList } from '../../../../../models/entities/user-list';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
desc: {
@@ -24,8 +23,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'UserList',
},
};
diff --git a/src/server/api/endpoints/users/lists/list.ts b/src/server/api/endpoints/users/lists/list.ts
index 684086b5c6..7b89d34314 100644
--- a/src/server/api/endpoints/users/lists/list.ts
+++ b/src/server/api/endpoints/users/lists/list.ts
@@ -1,6 +1,5 @@
import define from '../../../define';
import { UserLists } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
desc: {
@@ -14,11 +13,11 @@ export const meta = {
kind: 'read:account',
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'UserList',
}
},
diff --git a/src/server/api/endpoints/users/lists/show.ts b/src/server/api/endpoints/users/lists/show.ts
index 395f9352d4..01d03d1bfb 100644
--- a/src/server/api/endpoints/users/lists/show.ts
+++ b/src/server/api/endpoints/users/lists/show.ts
@@ -3,7 +3,6 @@ import { ID } from '../../../../../misc/cafy-id';
import define from '../../../define';
import { ApiError } from '../../../error';
import { UserLists } from '../../../../../models';
-import { types, bool } from '../../../../../misc/schema';
export const meta = {
desc: {
@@ -24,8 +23,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'UserList',
},
diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts
index fdc50e4dae..ef564b3c34 100644
--- a/src/server/api/endpoints/users/notes.ts
+++ b/src/server/api/endpoints/users/notes.ts
@@ -8,7 +8,6 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
import { Notes } from '../../../../models';
import { generateMuteQuery } from '../../common/generate-mute-query';
import { Brackets } from 'typeorm';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -120,11 +119,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'Note',
}
},
diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts
index 38b420c332..73d8fe6208 100644
--- a/src/server/api/endpoints/users/recommendation.ts
+++ b/src/server/api/endpoints/users/recommendation.ts
@@ -3,7 +3,6 @@ import $ from 'cafy';
import define from '../../define';
import { Users, Followings } from '../../../../models';
import { generateMuteQueryForUsers } from '../../common/generate-mute-query';
-import { types, bool } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -29,11 +28,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'User',
}
},
diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts
index 5c413defbc..9aa9e398e3 100644
--- a/src/server/api/endpoints/users/search.ts
+++ b/src/server/api/endpoints/users/search.ts
@@ -2,7 +2,6 @@ import $ from 'cafy';
import define from '../../define';
import { Users } from '../../../../models';
import { User } from '../../../../models/entities/user';
-import { bool, types } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -55,11 +54,11 @@ export const meta = {
},
res: {
- type: types.array,
- optional: bool.false, nullable: bool.false,
+ type: 'array' as const,
+ optional: false as const, nullable: false as const,
items: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'User',
}
},
diff --git a/src/server/api/endpoints/users/show.ts b/src/server/api/endpoints/users/show.ts
index 820c44e81b..d17dd51c0a 100644
--- a/src/server/api/endpoints/users/show.ts
+++ b/src/server/api/endpoints/users/show.ts
@@ -6,7 +6,6 @@ import { ApiError } from '../../error';
import { ID } from '../../../../misc/cafy-id';
import { Users } from '../../../../models';
import { In } from 'typeorm';
-import { bool, types } from '../../../../misc/schema';
export const meta = {
desc: {
@@ -43,8 +42,8 @@ export const meta = {
},
res: {
- type: types.object,
- optional: bool.false, nullable: bool.false,
+ type: 'object' as const,
+ optional: false as const, nullable: false as const,
ref: 'User',
},
@@ -53,7 +52,7 @@ export const meta = {
message: 'Failed to resolve remote user.',
code: 'FAILED_TO_RESOLVE_REMOTE_USER',
id: 'ef7b9be4-9cba-4e6f-ab41-90ed171c7d3c',
- kind: 'server' as 'server'
+ kind: 'server' as const
},
noSuchUser: {
diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts
index 02361a139d..cd9fe5bb9d 100644
--- a/src/server/api/private/signin.ts
+++ b/src/server/api/private/signin.ts
@@ -4,10 +4,11 @@ import * as speakeasy from 'speakeasy';
import { publishMainStream } from '../../../services/stream';
import signin from '../common/signin';
import config from '../../../config';
-import { Users, Signins, UserProfiles } from '../../../models';
+import { Users, Signins, UserProfiles, UserSecurityKeys, AttestationChallenges } from '../../../models';
import { ILocalUser } from '../../../models/entities/user';
import { genId } from '../../../misc/gen-id';
import { ensure } from '../../../prelude/ensure';
+import { verifyLogin, hash } from '../2fa';
export default async (ctx: Koa.BaseContext) => {
ctx.set('Access-Control-Allow-Origin', config.url);
@@ -51,40 +52,116 @@ export default async (ctx: Koa.BaseContext) => {
// Compare password
const same = await bcrypt.compare(password, profile.password!);
- if (same) {
- if (profile.twoFactorEnabled) {
- const verified = (speakeasy as any).totp.verify({
- secret: profile.twoFactorSecret,
- encoding: 'base32',
- token: token
- });
+ async function fail(status?: number, failure?: {error: string}) {
+ // Append signin history
+ const record = await Signins.save({
+ id: genId(),
+ createdAt: new Date(),
+ userId: user.id,
+ ip: ctx.ip,
+ headers: ctx.headers,
+ success: !!(status || failure)
+ });
- if (verified) {
- signin(ctx, user);
- } else {
- ctx.throw(403, {
- error: 'invalid token'
- });
- }
- } else {
- signin(ctx, user);
+ // Publish signin event
+ publishMainStream(user.id, 'signin', await Signins.pack(record));
+
+ if (status && failure) {
+ ctx.throw(status, failure);
}
- } else {
- ctx.throw(403, {
+ }
+
+ if (!same) {
+ await fail(403, {
error: 'incorrect password'
});
+ return;
}
- // Append signin history
- const record = await Signins.save({
- id: genId(),
- createdAt: new Date(),
- userId: user.id,
- ip: ctx.ip,
- headers: ctx.headers,
- success: same
- });
+ if (!profile.twoFactorEnabled) {
+ signin(ctx, user);
+ return;
+ }
+
+ if (token) {
+ const verified = (speakeasy as any).totp.verify({
+ secret: profile.twoFactorSecret,
+ encoding: 'base32',
+ token: token
+ });
+
+ if (verified) {
+ signin(ctx, user);
+ return;
+ } else {
+ await fail(403, {
+ error: 'invalid token'
+ });
+ return;
+ }
+ } else {
+ const clientDataJSON = Buffer.from(body.clientDataJSON, 'hex');
+ const clientData = JSON.parse(clientDataJSON.toString('utf-8'));
+ const challenge = await AttestationChallenges.findOne({
+ userId: user.id,
+ id: body.challengeId,
+ registrationChallenge: false,
+ challenge: hash(clientData.challenge).toString('hex')
+ });
+
+ if (!challenge) {
+ await fail(403, {
+ error: 'non-existent challenge'
+ });
+ return;
+ }
+
+ await AttestationChallenges.delete({
+ userId: user.id,
+ id: body.challengeId
+ });
+
+ if (new Date().getTime() - challenge.createdAt.getTime() >= 5 * 60 * 1000) {
+ await fail(403, {
+ error: 'non-existent challenge'
+ });
+ return;
+ }
+
+ const securityKey = await UserSecurityKeys.findOne({
+ id: Buffer.from(
+ body.credentialId
+ .replace(/\-/g, '+')
+ .replace(/_/g, '/'),
+ 'base64'
+ ).toString('hex')
+ });
+
+ if (!securityKey) {
+ await fail(403, {
+ error: 'invalid credentialId'
+ });
+ return;
+ }
+
+ const isValid = verifyLogin({
+ publicKey: Buffer.from(securityKey.publicKey, 'hex'),
+ authenticatorData: Buffer.from(body.authenticatorData, 'hex'),
+ clientDataJSON,
+ clientData,
+ signature: Buffer.from(body.signature, 'hex'),
+ challenge: challenge.challenge
+ });
+
+ if (isValid) {
+ signin(ctx, user);
+ } else {
+ await fail(403, {
+ error: 'invalid challenge data'
+ });
+ return;
+ }
+ }
- // Publish signin event
- publishMainStream(user.id, 'signin', await Signins.pack(record));
+ await fail();
};