commit 62ed8744f251294297f29dbb5cd7438511718128 Author: ADEBISI, Foluso A. (Dell-XPS) Date: Wed Dec 8 14:35:55 2021 +0100 * Ported from NodeJS to Python * Migrated to libfprint-2 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4a305a9 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# YOUreka IDID Fingerprint Service + +This service uses the libfprint library (version 2 and newwer) to capture fingerprints for profiles in YOUreka ID Domains. + +## Dependencies + +## Debian Packages: +1. sudo apt install libfprint-2-dev +2. sudo apt install python3-pip + +## PIP Packages +1. pip3 install python-socketio +2. pip3 install eventlet + +# How to run +```python3 app.py``` \ No newline at end of file diff --git a/adebisi-fa.jpg b/adebisi-fa.jpg new file mode 100644 index 0000000..51a8ab8 Binary files /dev/null and b/adebisi-fa.jpg differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..4abd244 --- /dev/null +++ b/app.py @@ -0,0 +1,149 @@ +import eventlet +import socketio +import uuid +import base64 + +import gi +gi.require_version("FPrint", "2.0") +gi.require_version("Gio", "2.0") +from gi.repository import FPrint, GLib, Gio + + +sio = socketio.Server() +static_files = { + '/assets': './public', + '/': {'filename': './public/index.html'} +} +app = socketio.WSGIApp(sio, static_files=static_files) + +stopEnrollment = False +def progress(device, completed_stages, prnt, sessionId, error): + print({'completedStages': completed_stages, 'sessionId': sessionId}) + global stopEnrollment + if (error): + stopEnrollment = True + print({'error': error}) + else: + if (completed_stages == 5): + if (not stopEnrollment): + sio.emit("fprint-enroll-status", { 'stage': completed_stages }, room=sessionId) + else: + print('Completed with an error.') + +def enroll(sessionId, data): + ctx = FPrint.Context() + devices = ctx.get_devices() + if (len(devices) == 0): + print("There is no attached device.") + else: + device = devices[0] + if (not device): + print("There is no attached device") + + if (not device.is_open()): + device.open_sync() + while (not device.is_open()): + print('Opening...') + try: + prt = FPrint.Print.new(device) + prt = device.enroll_sync(prt, None, progress_cb=progress, progress_data=sessionId) + img = prt.serialize() + sio.emit('fprint-enroll-done', { 'ok': True, "fingerprint": base64.b64encode(img).decode('utf-8'), 'id': str(uuid.uuid4()), 'data': data }) + with open('adebisi-fa.jpg', 'w+b') as f: + f.write(img) + except Exception as e: + print({'error': e}); + sio.emit('fprint-enroll-done', { 'ok': False, 'message': e}) + finally: + device.close_sync() + +def open_dev(sessionId): + device = None; + ctx = FPrint.Context() + devices = ctx.get_devices() + if (len(devices) == 0): + print("There is no attached device.") + else: + device = devices[0] + if (not device): + print("There is no attached device") + + if (not device.is_open()): + device.open_sync() + while (not device.is_open()): + print('Opening...') + return device + +def verify(sessionId, data): + ctx = FPrint.Context() + devices = ctx.get_devices() + if (len(devices) == 0): + print("There is no attached device.") + else: + device = devices[0] + if (not device): + print("There is no attached device") + + if (not device.is_open()): + device.open_sync() + while (not device.is_open()): + print('Opening...') + + with open('adebisi-fa.jpg', 'r+b') as f: + img = f.read() + prtRecord = FPrint.Print.deserialize(img) + canceller = Gio.Cancellable.new() + (match, prtFound) = device.verify_sync(prtRecord, canceller, None) + if (match): + sio.emit('fprint-verify-done', { 'ok': True, 'data': data }, room=sessionId) + else: + sio.emit('fprint-verify-done', { 'ok': False }, room=sessionId) + device.close_sync() + +def verify_short(sessionId, data): + d = open_dev(sessionId) + with open('adebisi-fa.jpg', 'r+b') as f: + img = f.read() + prtRecord = FPrint.Print.deserialize(img) + (match, print) = d.verify_sync(prtRecord, None, None) + if (match): + sio.emit('fprint-verify-done', { 'ok': True, 'data': data }, room=sessionId) + else: + sio.emit('fprint-verify-done', { 'ok': False }, room=sessionId) + d.close_sync() + +# events + +@sio.on("fprint-verify") +def fprint_verify(sid, data): + verify_short(sid, data) + pass + +@sio.on("fprint-enroll") +def fprint_enroll(sid, data): + print('Enrolling {}'.format(data['finger'])) + enroll(sid, data) + pass + +@sio.event +def capture(sid, data): + print(data) + pass + +@sio.event +def connect(sid, data): + # print({ 'sid': sid, 'event': 'connect', 'data': data}); + pass + +@sio.event +def disconnect(sid): + print({ 'sid': sid, 'event': 'disconnect'}); + pass + + +@sio.on('capture-other') +def capture_other(sid, data): + print({ "event": "capture_other", "data": data }) + pass + +eventlet.wsgi.server(eventlet.listen(('127.0.0.1', 9000), reuse_port=False), app) diff --git a/public/app.js b/public/app.js new file mode 100644 index 0000000..0c1de1e --- /dev/null +++ b/public/app.js @@ -0,0 +1,131 @@ + +var app = new Vue({ + el: '#app', + data: { + step: 0, + phone: '', + errorMessage: '', + otp: '', + busy: { + login: false + }, + user: { + phone: '', + username: '', + roles: [] + }, + staff: { + reference: '' + }, + fingerDetails: { + 'right-thumb': { }, + 'right-index-finger': { } + }, + socket: null + }, + created: function () { + var userStr = localStorage.getItem("user"); + if (userStr) { + this.user = JSON.parse(userStr); + this.step = 1; + } + + this.socket = io(); + + this.socket.on("connect", () => { }) + + this.socket.on("fprint-enroll-done", (data) => { + console.log({ event: 'enroll-done', data}); + if (data.ok) { + let { id, fingerprint, data: { finger }} = data; + this.step = 1.2; + this.fingerDetails[finger] = { id, fingerprint, finger } + } + this.busy.login = false; + }) + + this.socket.on("fprint-verify-done", (data) => { + console.log({ event: 'verify-done', data}); + }); + }, + methods: { + authenticateUrl: function (api, authenticate = true) { + return `https://apis.a2loft.net/identity${api}` + (authenticate ? `?phone=${this.user.phone}&otp=${this.otp}` : ''); + }, + login: async function () { + this.busy.login = true; + var { data } = await axios.get( + this.authenticateUrl(`/validate/uc/${this.phone}/${this.otp}`, false) + ); + this.errorMessage = ''; + if (!data.ok) + this.errorMessage = "Invalid OTP specified."; + else { + localStorage.setItem("user", JSON.stringify(data.data)); + this.user = data.data; + this.step = 1.0; + } + this.busy.login = false; + }, + logout: function () { + localStorage.removeItem("user"); + this.step = 0; + }, + loadUserByReference: async function () { + this.busy.login = true; + try { + var { data } = await axios.get( + this.authenticateUrl(`/uc/${this.staff.reference}`) + ); + if (!data.ok) { + swal("Oops!", `Operation failed. ${data.message}.` + this.staff.reference, "error"); + } else { + this.staff = data.data; + // this.staff.verified = false; + this.step = 1.1; + } + } finally { + this.busy.login = false; + } + }, + captureFingerPrint: async function (finger) { + this.busy.login = true; + this.step = 1.1; + // var { data: { success, message: { id, data, fingerprint } } } = await axios.post("http://localhost:9000/api/user", { + // name: this.staff.reference + // }); + // this.fingerDetails = { id, fingerprint }; + // var { data } = await axios.delete(`http://localhost:9000/api/user/${id}`); + // console.log(`${id} purged.`); + this.socket.emit("fprint-enroll", { finger }); + }, + saveFingerPrint: async function () { + this.busy.login = true; + try { + var fingerprints = Object.keys(this.fingerprints).map(finger => { + return { + finger, + fingerprint: this.fingerprints[finger] + } + }); + var { data } = await axios.post( + this.authenticateUrl(`/uc/fingerprints/${this.staff.reference}`), + { fingerprints } + ); + + if (data.ok) { + swal("Saved", `Fingerprint saved for ${this.staff.name} with reference ${this.staff.reference}.`, "success"); + this.newEnrolment(); + } else { + swal("Oops!", `Operation failed. ${data.message}.` + this.staff.reference, "error"); + } + } finally { + this.busy.login = false; + } + }, + newEnrolment: function () { + this.step = 1.0; + this.staff.reference = ""; + } + } +}) \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..47b0044 --- /dev/null +++ b/public/index.html @@ -0,0 +1,175 @@ + + + Fingerprint Service + + + + + + +
+
+
+ +
+

+ UNICAL Biometrics Enrolment Wizard +

+
+
+ + New + Enrolment  •   + ~{{user.username.substring(7)}}   •   Logout +
+
+
+
+
+

Agent Login

+
+
+ + +
+ +
+ + +
+ +
+
+ Login + Please wait... +
+
+ +
+
{{errorMessage}}
+
+
+
+
+
+
+
+

New Profile Enrollment

+
+
+ + +
+
+ + +
+
+
+ Get Details + Please wait... +
+
+
+
+
+
+

Enroll Profile

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + +
+
+ + +
+
+ Capture Fingerprint + Please wait... +
+
+ Recapture Fingerprint + Please wait... +
+
+
+
+ + +
+
+ + +
+
+ Capture Fingerprint + Please wait... +
+
+ Recapture Fingerprint + Please wait... +
+
+
+ +
+
+
+ + +
+
+
+ Save Finger Print + Please wait... +
+
+
+
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/requirement.txt b/requirement.txt new file mode 100644 index 0000000..fa88e45 --- /dev/null +++ b/requirement.txt @@ -0,0 +1,2 @@ +python-socketio +eventlet \ No newline at end of file