mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Add userMiddleware tests
This commit is contained in:
parent
403b16a67b
commit
d4fc10fe3e
5 changed files with 144 additions and 4 deletions
9
packages/db/adapters/DummyDB.test.ts
Normal file
9
packages/db/adapters/DummyDB.test.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { assertEquals } from '@std/assert';
|
||||
import { DummyDB } from './DummyDB.ts';
|
||||
|
||||
Deno.test('DummyDB', async () => {
|
||||
const db = DummyDB.create();
|
||||
const rows = await db.kysely.selectFrom('nostr_events').selectAll().execute();
|
||||
|
||||
assertEquals(rows, []);
|
||||
});
|
||||
29
packages/db/adapters/DummyDB.ts
Normal file
29
packages/db/adapters/DummyDB.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { DummyDriver, Kysely, PostgresAdapter, PostgresIntrospector, PostgresQueryCompiler } from 'kysely';
|
||||
|
||||
import type { DittoDB } from '../DittoDB.ts';
|
||||
import type { DittoTables } from '../DittoTables.ts';
|
||||
|
||||
export class DummyDB implements DittoDB {
|
||||
readonly kysely: Kysely<DittoTables>;
|
||||
readonly poolSize = 0;
|
||||
readonly availableConnections = 0;
|
||||
|
||||
constructor() {
|
||||
this.kysely = new Kysely<DittoTables>({
|
||||
dialect: {
|
||||
createAdapter: () => new PostgresAdapter(),
|
||||
createDriver: () => new DummyDriver(),
|
||||
createIntrospector: (db) => new PostgresIntrospector(db),
|
||||
createQueryCompiler: () => new PostgresQueryCompiler(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
listen(): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
[Symbol.asyncDispose](): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
export { DittoPglite } from './adapters/DittoPglite.ts';
|
||||
export { DittoPolyPg } from './adapters/DittoPolyPg.ts';
|
||||
export { DittoPostgres } from './adapters/DittoPostgres.ts';
|
||||
export { DummyDB } from './adapters/DummyDB.ts';
|
||||
|
||||
export type { DittoDB } from './DittoDB.ts';
|
||||
export type { DittoTables } from './DittoTables.ts';
|
||||
|
|
|
|||
99
packages/mastoapi/middleware/userMiddleware.test.ts
Normal file
99
packages/mastoapi/middleware/userMiddleware.test.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import { DittoConf } from '@ditto/conf';
|
||||
import { DummyDB } from '@ditto/db';
|
||||
import { DittoApp, type DittoMiddleware } from '@ditto/router';
|
||||
import { type NostrSigner, NSecSigner } from '@nostrify/nostrify';
|
||||
import { MockRelay } from '@nostrify/nostrify/test';
|
||||
import { assertEquals } from '@std/assert';
|
||||
import { generateSecretKey, nip19 } from 'nostr-tools';
|
||||
|
||||
import { userMiddleware } from './userMiddleware.ts';
|
||||
import { ReadOnlySigner } from '../signers/ReadOnlySigner.ts';
|
||||
|
||||
import type { User } from './User.ts';
|
||||
|
||||
Deno.test('no user 401', async () => {
|
||||
const { app } = testApp();
|
||||
const response = await app.use(userMiddleware()).request('/');
|
||||
assertEquals(response.status, 401);
|
||||
});
|
||||
|
||||
Deno.test('unsupported signer 400', async () => {
|
||||
const { app, relay } = testApp();
|
||||
const signer = new ReadOnlySigner('0461fcbecc4c3374439932d6b8f11269ccdb7cc973ad7a50ae362db135a474dd');
|
||||
|
||||
const response = await app
|
||||
.use(setUser({ signer, relay }))
|
||||
.use(userMiddleware({ enc: 'nip44' }))
|
||||
.use((c, next) => {
|
||||
c.var.user.signer.nip44.encrypt; // test that the type is set
|
||||
return next();
|
||||
})
|
||||
.request('/');
|
||||
|
||||
assertEquals(response.status, 400);
|
||||
});
|
||||
|
||||
Deno.test('with user 200', async () => {
|
||||
const { app, user } = testApp();
|
||||
|
||||
const response = await app
|
||||
.use(setUser(user))
|
||||
.use(userMiddleware())
|
||||
.get('/', (c) => c.text('ok'))
|
||||
.request('/');
|
||||
|
||||
assertEquals(response.status, 200);
|
||||
});
|
||||
|
||||
Deno.test('user and role 403', async () => {
|
||||
const { app, user } = testApp();
|
||||
|
||||
const response = await app
|
||||
.use(setUser(user))
|
||||
.use(userMiddleware({ role: 'admin' }))
|
||||
.request('/');
|
||||
|
||||
assertEquals(response.status, 403);
|
||||
});
|
||||
|
||||
Deno.test('admin role 200', async () => {
|
||||
const { conf, app, user, relay } = testApp();
|
||||
|
||||
const event = await conf.signer.signEvent({
|
||||
kind: 30382,
|
||||
tags: [
|
||||
['d', await user.signer.getPublicKey()],
|
||||
['n', 'admin'],
|
||||
],
|
||||
content: '',
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
|
||||
await relay.event(event);
|
||||
|
||||
const response = await app
|
||||
.use(setUser(user))
|
||||
.use(userMiddleware({ role: 'admin' }))
|
||||
.get('/', (c) => c.text('ok'))
|
||||
.request('/');
|
||||
|
||||
assertEquals(response.status, 200);
|
||||
});
|
||||
|
||||
function testApp() {
|
||||
const relay = new MockRelay();
|
||||
const signer = new NSecSigner(generateSecretKey());
|
||||
const conf = new DittoConf(new Map([['DITTO_NSEC', nip19.nsecEncode(generateSecretKey())]]));
|
||||
const db = new DummyDB();
|
||||
const app = new DittoApp({ conf, relay, db });
|
||||
const user = { signer, relay };
|
||||
|
||||
return { app, relay, conf, db, user };
|
||||
}
|
||||
|
||||
function setUser<S extends NostrSigner>(user: User<S>): DittoMiddleware<{ user: User<S> }> {
|
||||
return async (c, next) => {
|
||||
c.set('user', user);
|
||||
await next();
|
||||
};
|
||||
}
|
||||
|
|
@ -26,11 +26,11 @@ export function userMiddleware(opts: UserMiddlewareOpts = {}): DittoMiddleware<{
|
|||
const { enc, role, verify } = opts;
|
||||
|
||||
if (!user) {
|
||||
throw new HTTPException(403, { message: 'Authorization required.' });
|
||||
throw new HTTPException(401, { message: 'Authorization required' });
|
||||
}
|
||||
|
||||
if (enc && !user.signer[enc]) {
|
||||
throw new HTTPException(403, { message: `User does not have a ${enc} signer` });
|
||||
throw new HTTPException(400, { message: `User does not have a ${enc} signer` });
|
||||
}
|
||||
|
||||
if (role || verify) {
|
||||
|
|
@ -40,7 +40,7 @@ export function userMiddleware(opts: UserMiddlewareOpts = {}): DittoMiddleware<{
|
|||
const result = await validateAuthEvent(req, resEvent);
|
||||
|
||||
if (!result.success) {
|
||||
throw new HTTPException(403, { message: 'Verification failed.' });
|
||||
throw new HTTPException(401, { message: 'Verification failed' });
|
||||
}
|
||||
|
||||
// Prevent people from accidentally using the wrong account. This has no other security implications.
|
||||
|
|
@ -57,7 +57,7 @@ export function userMiddleware(opts: UserMiddlewareOpts = {}): DittoMiddleware<{
|
|||
}]);
|
||||
|
||||
if (!user || !matchesRole(user, role)) {
|
||||
throw new HTTPException(403, { message: `Must have ${role} role.` });
|
||||
throw new HTTPException(403, { message: `Must have ${role} role` });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue