fix: complete ORM port — token/oauth-client API mismatches, use published orm 0.2.8
- Use published @simpleworkjs/orm ^0.2.8 (fixes redis adapter write path)
and model-redis ^1.6.0 instead of a local file: link that broke docker
npm ci with a misleading "no lockfile" error.
- OtpToken.issue/verify: replace nonexistent find()/listDetail() with
list({where}).
- routes/auth.js: ImpersonationToken.listDetail() -> list({where}).
- routes/token.js: drop listDetail() call; 404 on missing token instead
of returning {results: null} with 200 (orm get() returns null, does
not throw like model-redis Table.get did).
- OAuthClient: Resource has no is_valid column, so every client read as
disabled and all /oauth/authorize requests 400'd — validity now lives
in metadata (absent = valid). Also generate a unique slug on create
(Resource.slug is required+unique) and use Resource.get() for lookup.
- User.login: 401 cleanly when neither uid nor username is supplied.
- models/index.js: log ORM init and surface init failures.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -16,11 +16,18 @@ class OAuthClient {
|
||||
const raw_secret = crypto.randomUUID();
|
||||
const client_id = crypto.randomUUID();
|
||||
const client_secret_hash = await bcrypt.hash(raw_secret, 10);
|
||||
|
||||
|
||||
// Generate a unique slug from the client name
|
||||
let slug = data.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'oauth-client';
|
||||
// Ensure uniqueness by appending a suffix if needed
|
||||
const existing = await Resource.list({ where: { slug } });
|
||||
if (existing.length) slug = `${slug}-${client_id.slice(0, 8)}`;
|
||||
|
||||
const r = await Resource.create({
|
||||
id: client_id,
|
||||
kind: 'oauth',
|
||||
name: data.name,
|
||||
slug: slug,
|
||||
description: data.description || '',
|
||||
owner: data.created_by,
|
||||
metadata: {
|
||||
@@ -37,10 +44,13 @@ class OAuthClient {
|
||||
return r;
|
||||
}
|
||||
static async get(client_id) {
|
||||
const resources = await Resource.list({ where: { id: client_id, kind: 'oauth' } });
|
||||
if (!resources.length) throw new Error('OAuthClient not found');
|
||||
|
||||
const r = resources[0];
|
||||
let r;
|
||||
try {
|
||||
r = await Resource.get(client_id);
|
||||
} catch (_) {
|
||||
throw new Error('OAuthClient not found');
|
||||
}
|
||||
if (r.kind !== 'oauth') throw new Error('OAuthClient not found');
|
||||
// Map metadata to top-level properties to satisfy routes/oauth.js without rewriting it
|
||||
r.client_id = r.id;
|
||||
r.client_secret_hash = r.metadata.client_secret_hash;
|
||||
@@ -48,6 +58,8 @@ class OAuthClient {
|
||||
r.scopes = r.metadata.scopes || ['openid', 'profile', 'email', 'groups'];
|
||||
r.allowed_groups = r.metadata.allowed_groups || [];
|
||||
r.token_lifetime = r.metadata.token_lifetime || { ...defaultLifetime };
|
||||
// Resource has no is_valid column; validity lives in metadata (absent = valid)
|
||||
r.is_valid = r.metadata.is_valid !== false;
|
||||
r.verifySecret = async (secret) => bcrypt.compare(secret, r.client_secret_hash);
|
||||
|
||||
r.rotateSecret = async () => {
|
||||
@@ -64,11 +76,11 @@ class OAuthClient {
|
||||
if (data.scopes !== undefined) r.metadata.scopes = data.scopes;
|
||||
if (data.allowed_groups !== undefined) r.metadata.allowed_groups = data.allowed_groups;
|
||||
if (data.token_lifetime !== undefined) r.metadata.token_lifetime = data.token_lifetime;
|
||||
|
||||
if (data.is_valid !== undefined) r.metadata.is_valid = data.is_valid;
|
||||
|
||||
const updateData = { metadata: r.metadata };
|
||||
if (data.name !== undefined) updateData.name = data.name;
|
||||
if (data.description !== undefined) updateData.description = data.description;
|
||||
if (data.is_valid !== undefined) updateData.is_valid = data.is_valid;
|
||||
|
||||
return originalUpdate(updateData);
|
||||
};
|
||||
@@ -81,6 +93,10 @@ class OAuthClient {
|
||||
return Promise.all(resources.map(r => this.get(r.id)));
|
||||
}
|
||||
|
||||
static async listDetail() {
|
||||
return this.list();
|
||||
}
|
||||
|
||||
static async verifySecret(client_id, secret) {
|
||||
const client = await this.get(client_id);
|
||||
return client.verifySecret(secret);
|
||||
|
||||
Reference in New Issue
Block a user