Corrige la collecte de l'IP des consentements
This commit is contained in:
1 parent
9aff45ce8d
commit
65213e812a
8 files changed
+59
-12
No files matched your search
@@ -0,0 +1,30 @@
|
||||
export function normalizeClientIp(input) {
|
||||
let ip = String(input ?? '').split(',')[0].trim();
|
||||
if (ip.startsWith('::ffff:')) ip = ip.slice(7);
|
||||
if (ip.startsWith('[') && ip.includes(']')) ip = ip.slice(1, ip.indexOf(']'));
|
||||
return ip.slice(0, 64);
|
||||
}
|
||||
|
||||
export function isPrivateClientIp(input) {
|
||||
const ip = normalizeClientIp(input).toLowerCase();
|
||||
if (!ip) return false;
|
||||
|
||||
const parts = ip.split('.').map(Number);
|
||||
if (parts.length === 4 && parts.every((part) => Number.isInteger(part) && part >= 0 && part <= 255)) {
|
||||
return parts[0] === 10
|
||||
|| parts[0] === 127
|
||||
|| (parts[0] === 169 && parts[1] === 254)
|
||||
|| (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31)
|
||||
|| (parts[0] === 192 && parts[1] === 168);
|
||||
}
|
||||
|
||||
return ip === '::1'
|
||||
|| ip === '::'
|
||||
|| ip.startsWith('fc')
|
||||
|| ip.startsWith('fd')
|
||||
|| /^fe[89ab]/.test(ip);
|
||||
}
|
||||
|
||||
export function requestClientIp(req) {
|
||||
return normalizeClientIp(req.ip || req.socket?.remoteAddress);
|
||||
}
|
||||
@@ -36,8 +36,9 @@ export const config = {
|
||||
},
|
||||
jwtSecret,
|
||||
cookieSecure: process.env.COOKIE_SECURE === 'true',
|
||||
// set to "true" only when running behind a reverse proxy (TLS termination)
|
||||
trustProxy: process.env.TRUST_PROXY === 'true',
|
||||
// The standard deployment has one reverse-proxy hop. Set this to false
|
||||
// only when the app is deliberately exposed without a proxy.
|
||||
trustProxy: process.env.TRUST_PROXY !== 'false',
|
||||
// when "true" (production behind a TLS proxy), plain-HTTP requests coming
|
||||
// through the proxy are 301-redirected to HTTPS. Direct requests without
|
||||
// an X-Forwarded-Proto header (e.g. the container healthcheck) are never
|
||||
|
||||
@@ -24,6 +24,7 @@ import { buildInvitationEmail } from '../emailTemplate.js';
|
||||
import { buildCustomEmail } from '../customEmailTemplate.js';
|
||||
import { buildProcessingRegister, buildImageConsentForm } from '../complianceDocuments.js';
|
||||
import { buildMembersPdf, buildMembersWorkbook } from '../memberDocuments.js';
|
||||
import { isPrivateClientIp, normalizeClientIp } from '../clientIp.js';
|
||||
import { billingRouter } from './billing.js';
|
||||
|
||||
export const adminRouter = Router();
|
||||
@@ -223,7 +224,18 @@ adminRouter.get('/members/:id/image-consent', validate(idParam, 'params'), async
|
||||
FROM image_consents WHERE member_id = $1 ORDER BY created_at DESC LIMIT 1`,
|
||||
[req.params.id]
|
||||
);
|
||||
res.json({ consent: result.rows[0] || null });
|
||||
const consent = result.rows[0] || null;
|
||||
if (!consent) return res.json({ consent: null });
|
||||
|
||||
const storedIp = normalizeClientIp(consent.ip);
|
||||
const ipUnavailable = Boolean(storedIp && isPrivateClientIp(storedIp));
|
||||
res.json({
|
||||
consent: {
|
||||
...consent,
|
||||
ip: ipUnavailable ? '' : storedIp,
|
||||
ip_unavailable: ipUnavailable,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { validate } from '../middleware/validate.js';
|
||||
import { memberProfileSchema, imageConsentSchema } from '../schemas.js';
|
||||
import { imageUpload, saveImage, deleteImage } from '../uploads.js';
|
||||
import { IMAGE_CONSENT_VERSION } from '../imageConsent.js';
|
||||
import { requestClientIp } from '../clientIp.js';
|
||||
|
||||
export const memberRouter = Router();
|
||||
|
||||
@@ -114,7 +115,7 @@ memberRouter.post('/image-consent', validate(imageConsentSchema), async (req, re
|
||||
signatoryName,
|
||||
decision === 'accepted' ? signaturePng : null,
|
||||
IMAGE_CONSENT_VERSION,
|
||||
req.ip || '',
|
||||
requestClientIp(req),
|
||||
(req.headers['user-agent'] || '').slice(0, 400),
|
||||
]
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user