diff --git a/migrations/20260217120000-mailPasswords-create-table.js b/migrations/20260217120000-mailPasswords-create-table.js index ae1210109..4a047e575 100644 --- a/migrations/20260217120000-mailPasswords-create-table.js +++ b/migrations/20260217120000-mailPasswords-create-table.js @@ -2,13 +2,13 @@ exports.up = async function(db) { const cmd = 'CREATE TABLE mailPasswords(' + - 'appId VARCHAR(128) NOT NULL,' + + 'clientId VARCHAR(128) NOT NULL,' + 'userId VARCHAR(128) NOT NULL,' + 'password VARCHAR(1024) NOT NULL,' + 'creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,' + - 'FOREIGN KEY(appId) REFERENCES apps(id),' + + 'FOREIGN KEY(clientId) REFERENCES oidcClients(id),' + 'FOREIGN KEY(userId) REFERENCES users(id),' + - 'PRIMARY KEY (appId, userId)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin'; + 'PRIMARY KEY (clientId, userId)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin'; await db.runSql(cmd); }; diff --git a/migrations/schema.sql b/migrations/schema.sql index 3d8645c85..e180cdf42 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -301,13 +301,13 @@ CREATE TABLE IF NOT EXISTS appPasswords( ); CREATE TABLE IF NOT EXISTS mailPasswords( - appId VARCHAR(128) NOT NULL, + clientId VARCHAR(128) NOT NULL, userId VARCHAR(128) NOT NULL, password VARCHAR(1024) NOT NULL, creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY(appId) REFERENCES apps(id), + FOREIGN KEY(clientId) REFERENCES oidcClients(id), FOREIGN KEY(userId) REFERENCES users(id), - PRIMARY KEY (appId, userId) + PRIMARY KEY (clientId, userId) ); CREATE TABLE IF NOT EXISTS dockerRegistries( diff --git a/src/mailpasswords.js b/src/mailpasswords.js index 1fc16f085..87f2cfcbc 100644 --- a/src/mailpasswords.js +++ b/src/mailpasswords.js @@ -3,13 +3,13 @@ import BoxError from './boxerror.js'; import database from './database.js'; import safe from 'safetydance'; -const MAIL_PASSWORD_FIELDS = [ 'appId', 'userId', 'password', 'creationTime' ].join(','); +const MAIL_PASSWORD_FIELDS = [ 'clientId', 'userId', 'password', 'creationTime' ].join(','); -async function get(appId, userId) { - assert.strictEqual(typeof appId, 'string'); +async function get(clientId, userId) { + assert.strictEqual(typeof clientId, 'string'); assert.strictEqual(typeof userId, 'string'); - const result = await database.query('SELECT ' + MAIL_PASSWORD_FIELDS + ' FROM mailPasswords WHERE appId = ? AND userId = ?', [ appId, userId ]); + const result = await database.query('SELECT ' + MAIL_PASSWORD_FIELDS + ' FROM mailPasswords WHERE clientId = ? AND userId = ?', [ clientId, userId ]); if (result.length === 0) return null; return result[0]; } @@ -20,37 +20,37 @@ async function getByUserId(userId) { return await database.query('SELECT ' + MAIL_PASSWORD_FIELDS + ' FROM mailPasswords WHERE userId = ?', [ userId ]); } -async function add(appId, userId, password) { - assert.strictEqual(typeof appId, 'string'); +async function add(clientId, userId, password) { + assert.strictEqual(typeof clientId, 'string'); assert.strictEqual(typeof userId, 'string'); assert.strictEqual(typeof password, 'string'); - if (appId.length < 1) throw new BoxError(BoxError.BAD_FIELD, 'appId must be at least 1 char'); + if (clientId.length < 1) throw new BoxError(BoxError.BAD_FIELD, 'clientId must be at least 1 char'); if (userId.length < 1) throw new BoxError(BoxError.BAD_FIELD, 'userId must be at least 1 char'); if (password.length < 1) throw new BoxError(BoxError.BAD_FIELD, 'password must be at least 1 char'); - const query = 'INSERT INTO mailPasswords (appId, userId, password) VALUES (?, ?, ?)'; - const args = [ appId, userId, password ]; + const query = 'INSERT INTO mailPasswords (clientId, userId, password) VALUES (?, ?, ?)'; + const args = [ clientId, userId, password ]; const [error] = await safe(database.query(query, args)); if (error && error.sqlCode === 'ER_DUP_ENTRY') throw new BoxError(BoxError.ALREADY_EXISTS, 'mail password for this app and user already exists'); if (error && error.sqlCode === 'ER_NO_REFERENCED_ROW_2') throw new BoxError(BoxError.NOT_FOUND, 'app or user not found'); if (error) throw error; - return { appId, userId }; + return { clientId, userId }; } async function list(userId) { assert.strictEqual(typeof userId, 'string'); - return await database.query('SELECT ' + MAIL_PASSWORD_FIELDS + ' FROM mailPasswords WHERE userId = ? ORDER BY appId', [ userId ]); + return await database.query('SELECT ' + MAIL_PASSWORD_FIELDS + ' FROM mailPasswords WHERE userId = ? ORDER BY clientId', [ userId ]); } -async function del(appId, userId) { - assert.strictEqual(typeof appId, 'string'); +async function del(clientId, userId) { + assert.strictEqual(typeof clientId, 'string'); assert.strictEqual(typeof userId, 'string'); - const result = await database.query('DELETE FROM mailPasswords WHERE appId = ? AND userId = ?', [ appId, userId ]); + const result = await database.query('DELETE FROM mailPasswords WHERE clientId = ? AND userId = ?', [ clientId, userId ]); if (result.affectedRows !== 1) throw new BoxError(BoxError.NOT_FOUND, 'mail password not found'); } diff --git a/src/oidcserver.js b/src/oidcserver.js index 53842241a..73bfb57ef 100644 --- a/src/oidcserver.js +++ b/src/oidcserver.js @@ -470,7 +470,7 @@ async function interactionConfirm(req, res, next) { return await gOidcProvider.interactionFinished(req, res, result, { mergeWithLastSubmission: false }); } - if (!app.manifest.addons.email && params.scope.indexOf('mailclient') !== -1) { + if (!app.manifest.addons?.email && params.scope.includes('mailclient')) { const result = { error: 'access_denied', error_description: 'App has no access to mailclient claims', @@ -538,16 +538,13 @@ async function getClaims(username, use, scope, clientId) { let mailPassword = null; if (clientId) { - const [clientError, client] = await safe(oidcClients.get(clientId)); - if (!clientError && client && client.appId) { - let mailPw = await mailpasswords.get(client.appId, user.id); - if (!mailPw) { - const generatedPassword = crypto.randomBytes(48).toString('hex'); - await mailpasswords.add(client.appId, user.id, generatedPassword); - mailPw = await mailpasswords.get(client.appId, user.id); - } - if (mailPw) mailPassword = mailPw.password; + let mailPw = await mailpasswords.get(clientId, user.id); + if (!mailPw) { + const generatedPassword = crypto.randomBytes(48).toString('hex'); + await mailpasswords.add(clientId, user.id, generatedPassword); + mailPw = await mailpasswords.get(clientId, user.id); } + if (mailPw) mailPassword = mailPw.password; } const displayName = user.displayName || user.username || ''; // displayName can be empty and username can be null