* fix: use nullish coalescing for recurrence weekday to allow Sunday selection Fixes #812 When creating a "Monthly on weekday" recurring task, the selector would jump back to Monday when trying to select Sunday. This was caused by using the logical OR operator (||) instead of the nullish coalescing operator (??) when handling the recurrence_weekday value. Since Sunday is represented as 0, the || operator treated it as falsy and defaulted to null/undefined, which then defaulted to 1 (Monday). Changes: - Replace || with ?? for recurrence_weekday in TaskRecurrenceCard.tsx - Replace || with ?? for recurrence_weekday in TaskDetails.tsx - Also fix recurrence_week_of_month and recurrence_month_day for consistency * fix: correct Sequelize alias case for OIDCIdentity-User association Fixes #1013 Changed all instances of lowercase 'user' to 'User' to match the association defined in models/index.js. This resolves the Sequelize error during OIDC callback: "User is associated to OIDCIdentity using an alias. You've included an alias (user), but it does not match the alias(es) defined in your association (User)." Changes: - oidcIdentityService.js: 'user' -> 'User' - provisioningService.js: 'user' -> 'User' (2 instances)
124 lines
3 KiB
JavaScript
124 lines
3 KiB
JavaScript
const { OIDCIdentity, User } = require('../../models');
|
|
|
|
async function getUserIdentities(userId) {
|
|
return await OIDCIdentity.findAll({
|
|
where: { user_id: userId },
|
|
order: [['created_at', 'DESC']],
|
|
attributes: [
|
|
'id',
|
|
'provider_slug',
|
|
'email',
|
|
'name',
|
|
'picture',
|
|
'first_login_at',
|
|
'last_login_at',
|
|
'created_at',
|
|
],
|
|
});
|
|
}
|
|
|
|
async function getIdentityById(identityId) {
|
|
return await OIDCIdentity.findByPk(identityId, {
|
|
include: [
|
|
{
|
|
model: User,
|
|
as: 'User',
|
|
attributes: ['id', 'email', 'username', 'is_admin'],
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
async function unlinkIdentity(identityId, userId) {
|
|
const identity = await OIDCIdentity.findOne({
|
|
where: {
|
|
id: identityId,
|
|
user_id: userId,
|
|
},
|
|
});
|
|
|
|
if (!identity) {
|
|
throw new Error('Identity not found or does not belong to this user');
|
|
}
|
|
|
|
const user = await User.findByPk(userId);
|
|
|
|
const hasPassword = !!user.password_digest;
|
|
|
|
const otherIdentities = await OIDCIdentity.count({
|
|
where: {
|
|
user_id: userId,
|
|
id: { [require('sequelize').Op.ne]: identityId },
|
|
},
|
|
});
|
|
|
|
if (!hasPassword && otherIdentities === 0) {
|
|
throw new Error(
|
|
'Cannot unlink the last authentication method. Please set a password first or link another provider.'
|
|
);
|
|
}
|
|
|
|
await identity.destroy();
|
|
|
|
return true;
|
|
}
|
|
|
|
async function canUnlink(identityId, userId) {
|
|
const identity = await OIDCIdentity.findOne({
|
|
where: {
|
|
id: identityId,
|
|
user_id: userId,
|
|
},
|
|
});
|
|
|
|
if (!identity) {
|
|
return { canUnlink: false, reason: 'Identity not found' };
|
|
}
|
|
|
|
const user = await User.findByPk(userId);
|
|
const hasPassword = !!user.password_digest;
|
|
|
|
const otherIdentities = await OIDCIdentity.count({
|
|
where: {
|
|
user_id: userId,
|
|
id: { [require('sequelize').Op.ne]: identityId },
|
|
},
|
|
});
|
|
|
|
if (!hasPassword && otherIdentities === 0) {
|
|
return {
|
|
canUnlink: false,
|
|
reason: 'This is your only authentication method',
|
|
};
|
|
}
|
|
|
|
return { canUnlink: true };
|
|
}
|
|
|
|
async function updateIdentityClaims(identityId, claims) {
|
|
const identity = await OIDCIdentity.findByPk(identityId);
|
|
|
|
if (!identity) {
|
|
throw new Error('Identity not found');
|
|
}
|
|
|
|
await identity.update({
|
|
email: claims.email || identity.email,
|
|
name: claims.name || identity.name,
|
|
given_name: claims.given_name || identity.given_name,
|
|
family_name: claims.family_name || identity.family_name,
|
|
picture: claims.picture || identity.picture,
|
|
raw_claims: claims,
|
|
last_login_at: new Date(),
|
|
});
|
|
|
|
return identity;
|
|
}
|
|
|
|
module.exports = {
|
|
getUserIdentities,
|
|
getIdentityById,
|
|
unlinkIdentity,
|
|
canUnlink,
|
|
updateIdentityClaims,
|
|
};
|