| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Attendances (hr_attendance)
• Discuss (mail) • Employees (hr) |
| Community Apps Dependencies | Show |
| Lines of code | 4541 |
| Technical Name |
eh_hr_face_liveness |
| License | LGPL-3 |
| Website | https://www.erpheritage.com.au/ |
| Versions | 16.0 17.0 18.0 19.0 |
Attended Face Capture
An auditable record of every face-kiosk capture attempt, recorded as the browser reports it. An attendance and audit layer, not a server-verified anti-spoofing control.
Why this module
Attended Face Capture
What it is, and what it is not
The capture outcome is computed in the browser and recorded as reported. The server does no liveness detection, no anti-spoofing, and no ML verification. Anyone who controls the device can report any outcome, and a printed photo held to the camera is not detected. The module ships this disclaimer in the manifest and on every record, because a recorded passed outcome is not proof of a real person.
One row per attempt, read-only
Every kiosk attempt writes an eh.hr.liveness.check row carrying the client-asserted outcome, the prompt shown, the duration, the reported blink count, the fail reason, the kiosk device, and the site. Records are create-and-read only in the UI, flagged client-asserted, and scoped to the company, so the attendance trail stands up as dispute evidence.
Yours to keep
LGPL-3 source on disk. No activation key, no phone-home, no recurring licence. It layers on the ERP Heritage face kiosk and attendance base, reuses their device tokens and rate limiter, and is the attendance-audit layer beneath a genuine liveness model, not a substitute for one.
Day in the life
A payroll officer settles a contested clock-in.
An employee disputes a kiosk check-in time. The officer opens Attended captures, filters to that site and day, and finds the row: outcome as reported by the browser, the prompt that was shown, the reported blink count, the duration the client ran before reporting, the kiosk device, and a matching kiosk event in the audit trail. The record is read-only and flagged client-asserted, so it is clear evidence of what the kiosk reported at that moment, without anyone being able to claim the row was edited after the fact. The capture itself proves a prompt was answered on that device, not that a live person was present, and the officer treats it exactly that way.
Edge cases
The cases most modules quietly ignore.
In the shipped code today, each one a place where a cheaper module silently does the wrong thing.
Every record carries client_asserted = True and an outcome help text stating the server does not verify the result. The model description and manifest summary both say plainly this is not a liveness or anti-spoofing result, only a record of what the kiosk client reported.
The daily retention sweep walks each company on its own eh_hr_liveness_retention_days horizon, defaulting to 180 days, and deletes only rows past that company's cutoff. It honours a shared batch_limit across companies so one tenant cannot starve another, and reports progress through ir.cron._commit_progress so the cron stays isolated.
Both endpoints require a valid X-EH-Kiosk-Token resolving to an active kiosk terminal, returning 401 otherwise. The recorded company and site are taken from the authenticated device, never from the request body, so a client cannot write a row into another company.
The config and log endpoints are throttled per token, or per client IP when no token is present, through the shared eh.hr.rate.limit service, returning 429 when a caller exceeds budget so a misbehaving or hostile device cannot flood the audit table.
The log endpoint rejects a malformed JSON body with 400 and accepts only the three outcomes passed, failed, or aborted, rejecting anything else with 400. Free-text prompt and fail-reason fields are truncated to 255 characters before storage.
If the kiosk cannot load the in-browser face model or the camera is denied, the client reports failed or aborted with a reason rather than blocking the employee, and the kiosk check-in flow continues. The disabled state and any config error fall through to allow check-in.
What is inside
Built to do the job, end to end.
- eh.hr.liveness.check model. One row per attempt: outcome (passed, failed, aborted), recorded-on timestamp, prompt shown, duration in ms, reported blink count, fail reason, kiosk device, site, optional employee, and company. Outcome is indexed, the row is read-only and flagged client-asserted, and a computed reference names it by outcome, employee, and time.
- Kiosk capture prompt. A capture screen is injected into the face kiosk shell that asks the user to blink, shows a live progress counter against the configured prompt, and on completion records the result. The prompt text and required blink count are driven by per-company settings served to the device.
- Config and log endpoints. /eh_hr/kiosk/liveness/config returns the per-company enabled flag, prompted blink count, and timeout to an authenticated device. /eh_hr/kiosk/liveness/log records the reported outcome, throttled, and writes a paired kiosk event tagged liveness_pass or liveness_fail into the existing kiosk audit trail.
- Per-company settings. Settings on res.company, surfaced in res.config.settings: enable the capture prompt, set prompted blinks, set the capture timeout in ms, and set the retention horizon in days. All default to safe values and the feature ships disabled.
- Retention sweep cron. A daily ir.cron runs _cron_retention_sweep, deleting attended-capture rows older than each company's retention horizon under a shared batch limit, with progress committed through the cron framework.
- Security and views. A global ir.rule scopes records to the user's companies. ACLs grant read to HR manager, admin, and auditor groups, with no UI write or create and unlink reserved to admin. A read-only list, search with outcome, site, and day grouping, and a menu under Attendances expose the audit trail, defaulting to today.
Honest about the edges
What this does not do, so nothing surprises you.
- Not an anti-spoofing or liveness-verification control. The outcome is computed entirely in the browser and recorded as reported. The server does no detection. A printed photo or a screen held to the camera is not detected, and anyone who controls the device can report any outcome.
- A recorded passed outcome is not proof that a live person was present. Treat it as an attendance audit record, not as identity or presence assurance.
- Employee linkage is optional and is set by the kiosk only after a separate face match identifies the employee. This module does not perform the face match.
- The in-browser blink prompt depends on the face model and camera being available on the kiosk device. If they are not, the attempt is recorded as failed or aborted and check-in continues.
- Requires the ERP Heritage face kiosk and attendance base modules. It is a layer on that kiosk, not a standalone attendance system.
- Targets Odoo 17 Community. No cross-version guarantees are made here.
Odoo 17 face attendance, kiosk check-in audit, face kiosk attendance record, hr_attendance face capture, attendance dispute evidence, Odoo Community HR kiosk, attended face capture, kiosk capture log, per-company attendance retention, blink prompt kiosk
Please log in to comment on this module