New CLI and debian package structure
This commit is contained in:
commit
e700de775a
33 changed files with 759 additions and 417 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -101,4 +101,10 @@ ENV/
|
|||
.mypy_cache/
|
||||
|
||||
# generated models
|
||||
/models
|
||||
/src/models
|
||||
|
||||
# build files
|
||||
debian/howdy.substvars
|
||||
debian/files
|
||||
debian/debhelper-build-stamp
|
||||
debian/howdy
|
||||
|
|
|
|||
25
.travis.yml
Normal file
25
.travis.yml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
sudo: required
|
||||
language: python
|
||||
python:
|
||||
- "3.4"
|
||||
- "3.6"
|
||||
|
||||
install:
|
||||
# Install build tools and ack-grep for checks
|
||||
- sudo apt install devscripts dh-make ack-grep -y
|
||||
|
||||
script:
|
||||
# Build the binary (.deb)
|
||||
- debuild -i -us -uc -b
|
||||
# Install the binary, also fireing the debian scripts
|
||||
- sudo apt install ../*.deb -y
|
||||
# Check if the username passthough works correctly with sudo
|
||||
- 'howdy | ack-grep --passthru --color "current active user: travis"'
|
||||
- 'sudo howdy | ack-grep --passthru --color "current active user: travis"'
|
||||
# Remove howdy from the installation
|
||||
- sudo apt purge howdy -y
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: always
|
||||
41
README.md
41
README.md
|
|
@ -1,4 +1,4 @@
|
|||
# Howdy for Ubuntu
|
||||
# Howdy for Ubuntu [](https://travis-ci.org/Boltgolt/howdy) [](https://github.com/Boltgolt/howdy/releases) [](https://github.com/Boltgolt/howdy/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)
|
||||
|
||||
Windows Hello™ style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are.
|
||||
|
||||
|
|
@ -6,21 +6,40 @@ Using the central authentication system in Linux (PAM), this works everywhere yo
|
|||
|
||||
### Installation
|
||||
|
||||
Run the installer by pasting (`ctrl+shift+V`) the following command into the terminal:
|
||||
Run the installer by pasting (`ctrl+shift+V`) the following commands into the terminal one at a time:
|
||||
|
||||
```
|
||||
wget -O /tmp/howdy_install.py https://raw.githubusercontent.com/Boltgolt/howdy/master/installer.py && sudo python3 /tmp/howdy_install.py
|
||||
sudo add-apt-repository ppa:boltgolt/howdy
|
||||
sudo apt update
|
||||
sudo apt install howdy
|
||||
```
|
||||
|
||||
This will guide you through the installation. When that's done run `sudo howdy USER add` and replace `USER` with your username to add a face model.
|
||||
|
||||
If nothing went wrong we should be able to run sudo by just showing your face. Open a new terminal and run `sudo -i` to see it in action.
|
||||
|
||||
**Note:** The build of dlib can hang on 100% for over a minute, give it time.
|
||||
|
||||
This will guide you through the installation. When that's done run `sudo howdy add` to add a face model.
|
||||
|
||||
If nothing went wrong we should be able to run sudo by just showing your face. Open a new terminal and run `sudo -i` to see it in action.
|
||||
|
||||
If you're curious you can run `sudo howdy config` to open the central config file and see the options Howdy has.
|
||||
|
||||
### Command line
|
||||
|
||||
The installer adds a `howdy` command to manage face models for the current user. Use `howdy help` to list the available options.
|
||||
The installer adds a `howdy` command to manage face models for the current user. Use `howdy --help` or `man howdy` to list the available options.
|
||||
|
||||
Usage:
|
||||
```
|
||||
howdy [-U user] [-y] command [argument]
|
||||
```
|
||||
|
||||
| Command | Description |
|
||||
|-----------|-----------------------------------------------|
|
||||
| `add` | Add a new face model for an user |
|
||||
| `clear` | Remove all face models for an user |
|
||||
| `config` | Open the config file in gedit |
|
||||
| `disable` | Disable or enable howdy |
|
||||
| `list` | List all saved face models for an user |
|
||||
| `remove` | Remove a specific model for an user |
|
||||
| `test` | Test the camera and recognition methods |
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
|
|
@ -28,14 +47,10 @@ Any python errors get logged directly into the console and should indicate what
|
|||
|
||||
If you encounter an error that hasn't been reported yet, don't be afraid to open a new issue.
|
||||
|
||||
### Uninstalling
|
||||
|
||||
There is an uninstaller available, run `sudo python3 /lib/security/howdy/uninstall.py` to remove Howdy from your system.
|
||||
|
||||
### A note on security
|
||||
|
||||
This script is in no way as secure as a password and will never be. Although it's harder to fool than normal face recognition, a person who looks similar to you or well-printed photo of you could be enough to do it.
|
||||
|
||||
To minimize the chance of this script being compromised, it's recommend to leave this repo in /lib/security and to keep it read only.
|
||||
To minimize the chance of this program being compromised, it's recommend to leave Howdy in /lib/security and to keep it read only.
|
||||
|
||||
DO NOT USE HOWDY AS THE SOLE AUTHENTICATION METHOD FOR YOUR SYSTEM.
|
||||
|
|
|
|||
1
autocomplete.sh → autocomplete/howdy
Normal file → Executable file
1
autocomplete.sh → autocomplete/howdy
Normal file → Executable file
|
|
@ -1,3 +1,4 @@
|
|||
#!/bin/bash
|
||||
# Autocomplete file run in bash
|
||||
# Will sugest arguments on tab
|
||||
|
||||
53
cli.py
53
cli.py
|
|
@ -1,53 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# CLI directly called by running the howdy command
|
||||
|
||||
# Import required modules
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Check if if a command has been given and print help otherwise
|
||||
if (len(sys.argv) < 2):
|
||||
print("Howdy IR face recognition help")
|
||||
import cli.help
|
||||
sys.exit()
|
||||
|
||||
# The command given
|
||||
cmd = sys.argv[1]
|
||||
|
||||
# Call the right files for commands that don't need root
|
||||
if cmd == "help":
|
||||
print("Howdy IR face recognition")
|
||||
import cli.help
|
||||
elif cmd == "test":
|
||||
import cli.test
|
||||
else:
|
||||
# Check if the minimum of 3 arugemnts has been met and print help otherwise
|
||||
if (len(sys.argv) < 3):
|
||||
print("Howdy IR face recognition help")
|
||||
import cli.help
|
||||
sys.exit()
|
||||
|
||||
# Requre sudo for comamnds that need root rights to read the model files
|
||||
if os.getenv("SUDO_USER") is None:
|
||||
print("Please run this command with sudo")
|
||||
sys.exit()
|
||||
|
||||
# Frome here on we require the second argument to be the username,
|
||||
# switching the command to the 3rd
|
||||
cmd = sys.argv[2]
|
||||
|
||||
if cmd == "list":
|
||||
import cli.list
|
||||
elif cmd == "add":
|
||||
import cli.add
|
||||
elif cmd == "remove":
|
||||
import cli.remove
|
||||
elif cmd == "clear":
|
||||
import cli.clear
|
||||
else:
|
||||
# If the comand is invalid, check if the user hasn't swapped the username and command
|
||||
if sys.argv[1] in ["list", "add", "remove", "clear"]:
|
||||
print("Usage: howdy <user> <command>")
|
||||
else:
|
||||
print('Unknown command "' + cmd + '"')
|
||||
import cli.help
|
||||
17
cli/help.py
17
cli/help.py
|
|
@ -1,17 +0,0 @@
|
|||
# Prints a simple help page for the CLI
|
||||
|
||||
print("""
|
||||
Usage:
|
||||
howdy <user> <command> [argument]
|
||||
|
||||
Commands:
|
||||
help Show this help page
|
||||
list List all saved face models for the current user
|
||||
add Add a new face model for the current user
|
||||
remove [id] Remove a specific model
|
||||
clear Remove all face models for the current user
|
||||
test Test the camera and recognition methods
|
||||
|
||||
For support please visit
|
||||
https://github.com/Boltgolt/howdy\
|
||||
""")
|
||||
25
debian/changelog
vendored
Normal file
25
debian/changelog
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
howdy (2.1.0) xenial; urgency=medium
|
||||
|
||||
* First complete PPA release
|
||||
* Reworked CLI
|
||||
|
||||
-- boltgolt <boltgolt@gmail.com> Fri, 13 Apr 2018 22:22:27 +0200
|
||||
|
||||
howdy (2.0.0-alpha+3) xenial; urgency=medium
|
||||
|
||||
* Fixed issue where dlib dependency failed to install on some installations
|
||||
* Added preinst script for camera detection
|
||||
|
||||
-- boltgolt <boltgolt@gmail.com> Thu, 12 Apr 2018 21:42:42 +0000
|
||||
|
||||
howdy (2.0.0-alpha+2) xenial; urgency=medium
|
||||
|
||||
* Fixed build dependency issue
|
||||
|
||||
-- boltgolt <boltgolt@gmail.com> Sat, 07 Apr 2018 21:30:48 +0200
|
||||
|
||||
howdy (2.0.0-alpha+1) xenial; urgency=low
|
||||
|
||||
* Initial packaged release.
|
||||
|
||||
-- boltgolt <boltgolt@gmail.com> Wed, 04 Apr 2018 18:13:15 +0200
|
||||
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
10
|
||||
15
debian/control
vendored
Normal file
15
debian/control
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
Source: howdy
|
||||
Section: misc
|
||||
Priority: optional
|
||||
Standards-Version: 3.9.7
|
||||
Build-Depends: python, dh-python, devscripts, dh-make, debhelper
|
||||
Maintainer: boltgolt <boltgolt@gmail.com>
|
||||
Vcs-Git: https://github.com/Boltgolt/howdy
|
||||
|
||||
Package: howdy
|
||||
Homepage: https://github.com/Boltgolt/howdy
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, git, python3, python3-pip, python3-dev, python3-setuptools, build-essential, libpam-python, fswebcam, libopencv-dev, python-opencv, cmake
|
||||
Description: Windows Hello style authentication for Ubuntu.
|
||||
Use your built-in IR emitters and camera in combination with face recognition
|
||||
to prove who you are.
|
||||
21
debian/copyright
vendored
Normal file
21
debian/copyright
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 boltgolt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
47
debian/howdy.1
vendored
Normal file
47
debian/howdy.1
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
.\" Please adjust this date whenever revising the manpage.
|
||||
.TH HOWDY 1 "April 9, 2018" "Howdy help" "User Commands"
|
||||
.SH NAME
|
||||
howdy \- Windows Hello style authentication for Ubuntu
|
||||
.SH DESCRIPTION
|
||||
Howdy IR face recognition implements a PAM module to use your face as a authentication method.
|
||||
.SS "Usage:"
|
||||
.IP
|
||||
howdy [\-U USER] [\-y] [\-h] command [argument]
|
||||
.SS "Commands:"
|
||||
.TP
|
||||
add
|
||||
Add a new face model for an user.
|
||||
.TP
|
||||
clear
|
||||
Remove all face models for an user.
|
||||
.TP
|
||||
config
|
||||
Open the config file in gedit.
|
||||
.TP
|
||||
disable
|
||||
Disable or enable howdy.
|
||||
.TP
|
||||
list
|
||||
List all saved face models for an user.
|
||||
.TP
|
||||
remove
|
||||
Remove a specific model for an user.
|
||||
.TP
|
||||
clear
|
||||
Remove all face models for an user.
|
||||
.TP
|
||||
test
|
||||
Test the camera and recognition methods.
|
||||
.SS "Optional arguments:"
|
||||
.TP
|
||||
\fB\-U\fR USER, \fB\-\-user\fR USER
|
||||
Set the user account to use.
|
||||
.TP
|
||||
\fB\-y\fR
|
||||
Skip all questions.
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Show this help message and exit.
|
||||
.PP
|
||||
.SH AUTHOR
|
||||
Howdy was written by boltgolt. For more information visit https://github.com/Boltgolt/howdy
|
||||
1
debian/howdy.manpages
vendored
Normal file
1
debian/howdy.manpages
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
debian/howdy.1
|
||||
2
debian/install
vendored
Normal file
2
debian/install
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
src/. lib/security/howdy
|
||||
autocomplete/. usr/share/bash-completion/completions
|
||||
204
debian/postinst
vendored
Executable file
204
debian/postinst
vendored
Executable file
|
|
@ -0,0 +1,204 @@
|
|||
#!/usr/bin/env python3
|
||||
# Installation script to install howdy
|
||||
# Executed after primary apt install
|
||||
|
||||
def col(id):
|
||||
"""Add color escape sequences"""
|
||||
if id == 1: return "\033[32m"
|
||||
if id == 2: return "\033[33m"
|
||||
if id == 3: return "\033[31m"
|
||||
return "\033[0m"
|
||||
|
||||
# Import required modules
|
||||
import subprocess
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import fileinput
|
||||
import urllib.parse
|
||||
|
||||
# Don't run unless we need to configure the install
|
||||
# Will also happen on upgrade but we will catch that later on
|
||||
if "configure" not in sys.argv:
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def log(text):
|
||||
"""Print a nicely formatted line to stdout"""
|
||||
print("\n>>> " + col(1) + text + col(0) + "\n")
|
||||
|
||||
def handleStatus(status):
|
||||
"""Abort if a command fails"""
|
||||
if (status != 0):
|
||||
print(col(3) + "Error while running last command" + col(0))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# We're not in fresh configuration mode (probably an upgrade), so exit
|
||||
if not os.path.exists("/tmp/howdy_picked_device"):
|
||||
sys.exit(0)
|
||||
|
||||
# Open the temporary file containing the device ID
|
||||
in_file = open("/tmp/howdy_picked_device", "r")
|
||||
# Load it in, it should be a string
|
||||
picked = in_file.read()
|
||||
in_file.close()
|
||||
|
||||
# Remove the temporary file
|
||||
subprocess.call(["rm /tmp/howdy_picked_device"], shell=True)
|
||||
|
||||
log("Upgrading pip to the latest version")
|
||||
|
||||
# Update pip
|
||||
handleStatus(subprocess.call(["pip3 install --upgrade pip"], shell=True))
|
||||
|
||||
log("Cloning dlib")
|
||||
|
||||
# Clone the dlib git to /tmp, but only the last commit
|
||||
handleStatus(subprocess.call(["git", "clone", "--depth", "1", "https://github.com/davisking/dlib.git", "/tmp/dlib_clone"]))
|
||||
|
||||
log("Building dlib")
|
||||
|
||||
# Start the build without GPU
|
||||
handleStatus(subprocess.call(["cd /tmp/dlib_clone/; python3 setup.py install --yes USE_AVX_INSTRUCTIONS --no DLIB_USE_CUDA"], shell=True))
|
||||
|
||||
log("Cleaning up dlib")
|
||||
|
||||
# Remove the no longer needed git clone
|
||||
handleStatus(subprocess.call(["rm", "-rf", "/tmp/dlib_clone"]))
|
||||
print("Temporary dlib files removed")
|
||||
|
||||
log("Installing python dependencies")
|
||||
|
||||
# Install face_recognition though pip
|
||||
handleStatus(subprocess.call(["pip3", "install", "--cache-dir", "/tmp/pip_howdy", "face_recognition_models==0.3.0", "Click>=6.0", "numpy", "Pillow"]))
|
||||
|
||||
log("Installing face_recognition")
|
||||
|
||||
# Install face_recognition though pip
|
||||
handleStatus(subprocess.call(["pip3", "install", "--cache-dir", "/tmp/pip_howdy", "--no-deps", "face_recognition==1.2.2"]))
|
||||
|
||||
log("Configuring howdy")
|
||||
|
||||
# Manually change the camera id to the one picked
|
||||
for line in fileinput.input(["/lib/security/howdy/config.ini"], inplace = 1):
|
||||
print(line.replace("device_id = 1", "device_id = " + picked), end="")
|
||||
|
||||
# Secure the howdy folder
|
||||
handleStatus(subprocess.call(["chmod 600 -R /lib/security/howdy/"], shell=True))
|
||||
|
||||
# Allow anyone to execute the python CLI
|
||||
handleStatus(subprocess.call(["chmod 755 /lib/security/howdy"], shell=True))
|
||||
handleStatus(subprocess.call(["chmod 744 /lib/security/howdy/cli.py"], shell=True))
|
||||
handleStatus(subprocess.call(["chmod 744 -R /lib/security/howdy/cli"], shell=True))
|
||||
print("Permissions set")
|
||||
|
||||
# Make the CLI executable as howdy
|
||||
handleStatus(subprocess.call(["ln -s /lib/security/howdy/cli.py /usr/local/bin/howdy"], shell=True))
|
||||
handleStatus(subprocess.call(["chmod +x /usr/local/bin/howdy"], shell=True))
|
||||
print("Howdy command installed")
|
||||
|
||||
log("Adding howdy as PAM module")
|
||||
|
||||
# Will be filled with the actual output lines
|
||||
outlines = []
|
||||
# Will be fillled with lines that contain coloring
|
||||
printlines = []
|
||||
# Track if the new lines have been insterted yet
|
||||
inserted = False
|
||||
|
||||
# Open the PAM config file
|
||||
with open("/etc/pam.d/common-auth") as fp:
|
||||
# Read the first line
|
||||
line = fp.readline()
|
||||
|
||||
while line:
|
||||
# Add the line to the output directly, we're not deleting anything
|
||||
outlines.append(line)
|
||||
|
||||
# Print the comments in gray and don't insert into comments
|
||||
if line[:1] == "#":
|
||||
printlines.append("\033[37m" + line + "\033[0m")
|
||||
else:
|
||||
printlines.append(line)
|
||||
|
||||
# If it's not a comment and we haven't inserted yet
|
||||
if not inserted:
|
||||
# Set both the comment and the linking line
|
||||
line_comment = "# Howdy IR face recognition\n"
|
||||
line_link = "auth sufficient pam_python.so /lib/security/howdy/pam.py\n\n"
|
||||
|
||||
# Add them to the output without any markup
|
||||
outlines.append(line_comment)
|
||||
outlines.append(line_link)
|
||||
|
||||
# Make the print orange to make it clear what's being added
|
||||
printlines.append("\033[33m" + line_comment + "\033[0m")
|
||||
printlines.append("\033[33m" + line_link + "\033[0m")
|
||||
|
||||
# Mark as inserted
|
||||
inserted = True
|
||||
|
||||
# Go to the next line
|
||||
line = fp.readline()
|
||||
|
||||
# Print a file Header
|
||||
print("\033[33m" + ">>> START OF /etc/pam.d/common-auth" + "\033[0m")
|
||||
|
||||
# Loop though all printing lines and use the enters from the file
|
||||
for line in printlines:
|
||||
print(line, end="")
|
||||
|
||||
# Print a footer
|
||||
print("\033[33m" + ">>> END OF /etc/pam.d/common-auth" + "\033[0m" + "\n")
|
||||
|
||||
# Do not prompt for a yes if we're in no promt mode
|
||||
if "HOWDY_NO_PROMPT" not in os.environ:
|
||||
# Ask the user if this change is okay
|
||||
print("Lines will be insterted in /etc/pam.d/common-auth as shown above")
|
||||
ans = input("Apply this change? [y/N]: ")
|
||||
|
||||
# Abort the whole thing if it's not
|
||||
if (ans.lower() != "y"):
|
||||
print("Inerpeting as a \"NO\", aborting")
|
||||
sys.exit(1)
|
||||
|
||||
print("Adding lines to PAM\n")
|
||||
|
||||
# Write to PAM
|
||||
common_auth = open("/etc/pam.d/common-auth", "w")
|
||||
common_auth.write("".join(outlines))
|
||||
common_auth.close()
|
||||
|
||||
# From here onwards the installation is complete
|
||||
# We want to gather more information about the types or IR camera's
|
||||
# used though, and the following lines are data gathering
|
||||
# No data is ever uploaded without permission
|
||||
|
||||
if "HOWDY_NO_PROMPT" not in os.environ:
|
||||
# List all video devices
|
||||
diag_out = "Video devices [IR=" + picked + "]\n"
|
||||
diag_out += "```\n"
|
||||
diag_out += subprocess.check_output(['ls /dev/ | grep video'], shell=True).decode("utf-8")
|
||||
diag_out += "```\n"
|
||||
|
||||
# Get some info from the USB kernel listings
|
||||
diag_out += "Lsusb output\n"
|
||||
diag_out += "```\n"
|
||||
diag_out += subprocess.check_output(['lsusb -vvvv | grep -i "Camera\|iFunction"'], shell=True).decode("utf-8")
|
||||
diag_out += "```\n"
|
||||
|
||||
# Get camera information from video4linux
|
||||
diag_out += "Udevadm\n"
|
||||
diag_out += "```\n"
|
||||
diag_out += subprocess.check_output(['udevadm info -r --query=all -n /dev/video' + picked + ' | grep -i "ID_BUS\|ID_MODEL_ID\|ID_VENDOR_ID\|ID_V4L_PRODUCT\|ID_MODEL"'], shell=True).decode("utf-8")
|
||||
diag_out += "```"
|
||||
|
||||
# Print it all as a clickable link to a new github issue
|
||||
print("https://github.com/Boltgolt/howdy-reports/issues/new?title=Post-installation%20camera%20information&body=" + urllib.parse.quote_plus(diag_out) + "\n")
|
||||
|
||||
# Let the user know what to do with the link
|
||||
print("Installation complete.")
|
||||
print(col(2) + "If you want to help the development, please use the link above to post some camera-related information to github!" + col(0))
|
||||
94
debian/preinst
vendored
Executable file
94
debian/preinst
vendored
Executable file
|
|
@ -0,0 +1,94 @@
|
|||
#!/usr/bin/env python3
|
||||
# Used to check cameras before commiting to install
|
||||
# Executed before primary apt install of files
|
||||
|
||||
def col(id):
|
||||
"""Add color escape sequences"""
|
||||
if id == 1: return "\033[32m"
|
||||
if id == 2: return "\033[33m"
|
||||
if id == 3: return "\033[31m"
|
||||
return "\033[0m"
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
|
||||
# Don't run if we're not trying to install fresh
|
||||
if "install" not in sys.argv:
|
||||
sys.exit(0)
|
||||
|
||||
# The default picked video device id
|
||||
picked = -1
|
||||
|
||||
print(col(1) + "Starting IR camera check...\n" + col(0))
|
||||
|
||||
# If prompting has been disabled, skip camera check
|
||||
if "HOWDY_NO_PROMPT" in os.environ:
|
||||
print(col(2) + "AUTOMATED INSTALL, YOU WILL NOT BE ASKED FOR INPUT AND CHECKS WILL BE SKIPPED" + col(0))
|
||||
|
||||
# Write the default device to disk and exit
|
||||
with open("/tmp/howdy_picked_device", "w") as out_file:
|
||||
out_file.write("0")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Get all devices
|
||||
devices = os.listdir("/dev")
|
||||
|
||||
# Loop though all devices
|
||||
for dev in devices:
|
||||
# Only use the video devices
|
||||
if (dev[:5] == "video"):
|
||||
time.sleep(.5)
|
||||
|
||||
# The full path to the device is the default name
|
||||
device_name = "/dev/" + dev
|
||||
# Get the udevadm details to try to get a better name
|
||||
udevadm = subprocess.check_output(["udevadm info -r --query=all -n " + device_name], shell=True).decode("utf-8")
|
||||
|
||||
# Loop though udevadm to search for a better name
|
||||
for line in udevadm.split("\n"):
|
||||
# Match it and encase it in quotes
|
||||
re_name = re.search('product.*=(.*)$', line, re.IGNORECASE)
|
||||
if re_name:
|
||||
device_name = '"' + re_name.group(1) + '"'
|
||||
|
||||
# Show what device we're using
|
||||
print("Trying " + device_name)
|
||||
|
||||
# Let fswebcam keep the camera open in the background
|
||||
sub = subprocess.Popen(["fswebcam -S 9999999999 -d /dev/" + dev + " /dev/null 2>/dev/null"], shell=True, preexec_fn=os.setsid)
|
||||
|
||||
try:
|
||||
# Ask the user if this is the right one
|
||||
print(col(2) + "One of your cameras should now be on." + col(0))
|
||||
ans = input("Did your IR emitters turn on? [y/N]: ")
|
||||
except KeyboardInterrupt:
|
||||
# Kill fswebcam if the user aborts
|
||||
os.killpg(os.getpgid(sub.pid), signal.SIGTERM)
|
||||
raise
|
||||
|
||||
# The user has answered, kill fswebcam
|
||||
os.killpg(os.getpgid(sub.pid), signal.SIGTERM)
|
||||
|
||||
# Set this camera as picked if the answer was yes, go to the next one if no
|
||||
if ans.lower() == "y" or ans.lower() == "yes":
|
||||
picked = dev[5:]
|
||||
break
|
||||
else:
|
||||
print("Interpreting as a " + col(3) + "\"NO\"\n" + col(0))
|
||||
|
||||
# Abort if no camera was picked
|
||||
if picked == -1:
|
||||
print(col(3) + "No suitable IR camera found, aborting install." + col(0))
|
||||
sys.exit(23)
|
||||
|
||||
# Write the result to disk so postinst can have a look at it
|
||||
with open("/tmp/howdy_picked_device", "w") as out_file:
|
||||
out_file.write(str(picked))
|
||||
|
||||
# Add a line break
|
||||
print("")
|
||||
47
debian/prerm
vendored
Executable file
47
debian/prerm
vendored
Executable file
|
|
@ -0,0 +1,47 @@
|
|||
#!/usr/bin/env python3
|
||||
# Executed on deinstallation
|
||||
# Completely remove howdy from the system
|
||||
|
||||
def col(id):
|
||||
"""Add color escape sequences"""
|
||||
if id == 1: return "\033[32m"
|
||||
if id == 2: return "\033[33m"
|
||||
if id == 3: return "\033[31m"
|
||||
return "\033[0m"
|
||||
|
||||
# Import required modules
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Only run when we actually want to remove
|
||||
if "remove" not in sys.argv and "purge" not in sys.argv:
|
||||
sys.exit(0)
|
||||
|
||||
# Don't try running this if it's already gome
|
||||
if not os.path.exists("/lib/security/howdy/cli"):
|
||||
sys.exit(0)
|
||||
|
||||
# Remove files and symlinks
|
||||
try:
|
||||
subprocess.call(["rm /usr/local/bin/howdy"], shell=True)
|
||||
except e:
|
||||
print("Can't remove executable")
|
||||
try:
|
||||
subprocess.call(["rm /usr/share/bash-completion/completions/howdy"], shell=True)
|
||||
except e:
|
||||
print("Can't remove autocompletion script")
|
||||
try:
|
||||
subprocess.call(["rm -rf /lib/security/howdy"], shell=True)
|
||||
except e:
|
||||
# This error is normal
|
||||
pass
|
||||
|
||||
# Remove face_recognition and dlib
|
||||
subprocess.call(["pip3 uninstall face_recognition face_recognition_models dlib -y --no-cache-dir"], shell=True)
|
||||
|
||||
# Print a tearbending message
|
||||
print(col(2) + """
|
||||
There are still lines in /etc/pam.d/common-auth that can't be removed automatically
|
||||
Run "nano /etc/pam.d/common-auth" to remove them by hand\
|
||||
""" + col(0))
|
||||
11
debian/rules
vendored
Executable file
11
debian/rules
vendored
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/make -f
|
||||
DH_VERBOSE = 1
|
||||
|
||||
DPKG_EXPORT_BUILDFLAGS = 1
|
||||
include /usr/share/dpkg/default.mk
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
binary:
|
||||
dh binary
|
||||
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
3.0 (native)
|
||||
6
debian/source/options
vendored
Normal file
6
debian/source/options
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
tar-ignore = ".git"
|
||||
tar-ignore = "models"
|
||||
tar-ignore = ".gitignore"
|
||||
tar-ignore = "README.md"
|
||||
tar-ignore = "LICENSE"
|
||||
tar-ignore = ".travis.yml"
|
||||
252
installer.py
252
installer.py
|
|
@ -1,252 +0,0 @@
|
|||
# Installation script to install howdy
|
||||
# Runs completely independent of the others
|
||||
|
||||
# Import required modules
|
||||
import subprocess
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import fileinput
|
||||
import urllib.parse
|
||||
|
||||
def log(text):
|
||||
"""Print a nicely formatted line to stdout"""
|
||||
print("\n>>> \033[32m" + text + "\033[0m\n")
|
||||
|
||||
def handleStatus(status):
|
||||
"""Abort if a command fails"""
|
||||
if (status != 0):
|
||||
print("\033[31mError while running last command\033[0m")
|
||||
sys.exit()
|
||||
|
||||
# Check if we're running as root
|
||||
user = os.getenv("SUDO_USER")
|
||||
if user is None:
|
||||
print("Please run this script as a sudo user")
|
||||
sys.exit()
|
||||
|
||||
# Print some nice intro text
|
||||
print("\n\033[33m HOWDY INSTALLER FOR UBUNTU\033[0m")
|
||||
print(" Version 1, 2016/02/05\n")
|
||||
|
||||
# Let it sink in
|
||||
time.sleep(.5)
|
||||
log("Installing required apt packages")
|
||||
|
||||
# Install packages though apt
|
||||
handleStatus(subprocess.call(["apt", "install", "-y", "git",
|
||||
"python3-pip",
|
||||
"python3-dev",
|
||||
"python3-setuptools",
|
||||
"build-essential",
|
||||
"libpam-python",
|
||||
"fswebcam",
|
||||
"libopencv-dev",
|
||||
"python-opencv",
|
||||
"cmake"]))
|
||||
|
||||
# Update pip
|
||||
handleStatus(subprocess.call(["pip3 install --upgrade pip"], shell=True))
|
||||
|
||||
log("Starting camera check")
|
||||
|
||||
# Get all devices
|
||||
devices = os.listdir("/dev")
|
||||
# The picked video device id
|
||||
picked = False
|
||||
|
||||
# Loop though all devices
|
||||
for dev in devices:
|
||||
# Only use the video devices
|
||||
if (dev[:5] == "video"):
|
||||
time.sleep(.5)
|
||||
|
||||
# The full path to the device is the default name
|
||||
device_name = "/dev/" + dev
|
||||
# Get the udevadm details to try to get a better name
|
||||
udevadm = subprocess.check_output(["udevadm info -r --query=all -n " + device_name], shell=True).decode("utf-8")
|
||||
|
||||
# Loop though udevadm to search for a better name
|
||||
for line in udevadm.split("\n"):
|
||||
# Match it and encase it in quotes
|
||||
re_name = re.search('product.*=(.*)$', line, re.IGNORECASE)
|
||||
if re_name:
|
||||
device_name = '"' + re_name.group(1) + '"'
|
||||
|
||||
# Show what device we're using
|
||||
print("Trying " + device_name)
|
||||
|
||||
# Let fswebcam keep the camera open in the background
|
||||
sub = subprocess.Popen(["fswebcam -S 9999999999 -d /dev/" + dev + " /dev/null 2>/dev/null"], shell=True, preexec_fn=os.setsid)
|
||||
|
||||
try:
|
||||
# Ask the user if this is the right one
|
||||
print("\033[33mOne of your cameras should now be on.\033[0m")
|
||||
ans = input("Did your IR emitters turn on? [y/N]: ")
|
||||
except KeyboardInterrupt:
|
||||
# Kill fswebcam if the user aborts
|
||||
os.killpg(os.getpgid(sub.pid), signal.SIGTERM)
|
||||
raise
|
||||
|
||||
# The user has answered, kill fswebcam
|
||||
os.killpg(os.getpgid(sub.pid), signal.SIGTERM)
|
||||
|
||||
# Set this camera as picked if the answer was yes, go to the next one if no
|
||||
if (ans.lower() == "y"):
|
||||
picked = dev[5:]
|
||||
break
|
||||
else:
|
||||
print("Inerpeting as a \"NO\"\n")
|
||||
|
||||
# Abort if no camera was picked
|
||||
if (picked == False):
|
||||
print("\033[31mNo suitable IR camera found\033[0m")
|
||||
sys.exit()
|
||||
|
||||
log("Cloning dlib")
|
||||
|
||||
# Clone the git to /tmp
|
||||
handleStatus(subprocess.call(["git", "clone", "https://github.com/davisking/dlib.git", "/tmp/dlib_clone"]))
|
||||
|
||||
log("Building dlib")
|
||||
|
||||
# Start the build without GPU
|
||||
handleStatus(subprocess.call(["cd /tmp/dlib_clone/; python3 setup.py install --yes USE_AVX_INSTRUCTIONS --no DLIB_USE_CUDA"], shell=True))
|
||||
|
||||
log("Cleaning up dlib")
|
||||
|
||||
# Remove the no longer needed git clone
|
||||
handleStatus(subprocess.call(["rm", "-rf", "/tmp/dlib_clone"]))
|
||||
|
||||
log("Installing face_recognition")
|
||||
|
||||
# Install face_recognition though pip
|
||||
handleStatus(subprocess.call(["pip3", "install", "face_recognition"]))
|
||||
|
||||
log("Cloning howdy")
|
||||
|
||||
# Make sure /lib/security exists
|
||||
if not os.path.exists("/lib/security"):
|
||||
os.makedirs("/lib/security")
|
||||
|
||||
# Clone howdy into it
|
||||
handleStatus(subprocess.call(["git", "clone", "https://github.com/Boltgolt/howdy.git", "/lib/security/howdy"]))
|
||||
|
||||
# Manually change the camera id to the one picked
|
||||
for line in fileinput.input(["/lib/security/howdy/config.ini"], inplace = 1):
|
||||
print(line.replace("device_id = 1", "device_id = " + picked), end="")
|
||||
|
||||
# Secure the howdy folder
|
||||
handleStatus(subprocess.call(["chmod 600 -R /lib/security/howdy/"], shell=True))
|
||||
|
||||
# Make the CLI executable as howdy
|
||||
handleStatus(subprocess.call(["ln -s /lib/security/howdy/cli.py /usr/bin/howdy"], shell=True))
|
||||
handleStatus(subprocess.call(["chmod +x /usr/bin/howdy"], shell=True))
|
||||
|
||||
# Install the command autocomplete, don't error on failure
|
||||
subprocess.call(["sudo cp /lib/security/howdy/autocomplete.sh /etc/bash_completion.d/howdy"], shell=True)
|
||||
|
||||
log("Adding howdy as PAM module")
|
||||
|
||||
# Will be filled with the actual output lines
|
||||
outlines = []
|
||||
# Will be fillled with lines that contain coloring
|
||||
printlines = []
|
||||
# Track if the new lines have been insterted yet
|
||||
inserted = False
|
||||
|
||||
# Open the PAM config file
|
||||
with open("/etc/pam.d/common-auth") as fp:
|
||||
# Read the first line
|
||||
line = fp.readline()
|
||||
|
||||
while line:
|
||||
# Add the line to the output directly, we're not deleting anything
|
||||
outlines.append(line)
|
||||
|
||||
# Print the comments in gray and don't insert into comments
|
||||
if line[:1] == "#":
|
||||
printlines.append("\033[37m" + line + "\033[0m")
|
||||
else:
|
||||
printlines.append(line)
|
||||
|
||||
# If it's not a comment and we haven't inserted yet
|
||||
if not inserted:
|
||||
# Set both the comment and the linking line
|
||||
line_comment = "# Howdy IR face recognition\n"
|
||||
line_link = "auth sufficient pam_python.so /lib/security/howdy/pam.py\n\n"
|
||||
|
||||
# Add them to the output without any markup
|
||||
outlines.append(line_comment)
|
||||
outlines.append(line_link)
|
||||
|
||||
# Make the print orange to make it clear what's being added
|
||||
printlines.append("\033[33m" + line_comment + "\033[0m")
|
||||
printlines.append("\033[33m" + line_link + "\033[0m")
|
||||
|
||||
# Mark as inserted
|
||||
inserted = True
|
||||
|
||||
# Go to the next line
|
||||
line = fp.readline()
|
||||
|
||||
# Print a file Header
|
||||
print("\033[33m" + ">>> START OF /etc/pam.d/common-auth" + "\033[0m")
|
||||
|
||||
# Loop though all printing lines and use the enters from the file
|
||||
for line in printlines:
|
||||
print(line, end="")
|
||||
|
||||
# Print a footer
|
||||
print("\033[33m" + ">>> END OF /etc/pam.d/common-auth" + "\033[0m" + "\n")
|
||||
|
||||
# Ask the user if this change is okay
|
||||
print("Lines will be insterted in /etc/pam.d/common-auth as shown above")
|
||||
ans = input("Apply this change? [y/N]: ")
|
||||
|
||||
# Abort the whole thing if it's not
|
||||
if (ans.lower() != "y"):
|
||||
print("Inerpeting as a \"NO\", aborting")
|
||||
sys.exit()
|
||||
|
||||
print("Adding lines to PAM\n")
|
||||
|
||||
# Write to PAM
|
||||
common_auth = open("/etc/pam.d/common-auth", "w")
|
||||
common_auth.write("".join(outlines))
|
||||
common_auth.close()
|
||||
|
||||
# From here onwards the installation is complete
|
||||
# We want to gather more information about the types or IR camera's
|
||||
# used though, and the following lines are data collection
|
||||
|
||||
# List all video devices
|
||||
diag_out = "Video devices [IR=" + picked + "]\n"
|
||||
diag_out += "```\n"
|
||||
diag_out += subprocess.check_output(['ls /dev/ | grep video'], shell=True).decode("utf-8")
|
||||
diag_out += "```\n"
|
||||
|
||||
# Get some info from the USB kernel listings
|
||||
diag_out += "Lsusb output\n"
|
||||
diag_out += "```\n"
|
||||
diag_out += subprocess.check_output(['lsusb -vvvv | grep -i "Camera\|iFunction"'], shell=True).decode("utf-8")
|
||||
diag_out += "```\n"
|
||||
|
||||
# Get camera information from video4linux
|
||||
diag_out += "Udevadm\n"
|
||||
diag_out += "```\n"
|
||||
diag_out += subprocess.check_output(['udevadm info -r --query=all -n /dev/video' + picked + ' | grep -i "ID_BUS\|ID_MODEL_ID\|ID_VENDOR_ID\|ID_V4L_PRODUCT\|ID_MODEL"'], shell=True).decode("utf-8")
|
||||
diag_out += "```"
|
||||
|
||||
# Print it all as a clickable link to a new github issue
|
||||
print("https://github.com/Boltgolt/howdy-reports/issues/new?title=Post-installation%20camera%20information&body=" + urllib.parse.quote_plus(diag_out) + "\n")
|
||||
|
||||
# Let the user know what to do with the link
|
||||
print("Installation complete.")
|
||||
print("If you want to help the development, please use the link above to post some camera-related information to github")
|
||||
|
||||
# Remove the installer if it was downloaded to /tmp
|
||||
if os.path.exists("/tmp/howdy_install.py"):
|
||||
os.remove("/tmp/howdy_install.py")
|
||||
98
src/cli.py
Executable file
98
src/cli.py
Executable file
|
|
@ -0,0 +1,98 @@
|
|||
#!/usr/bin/env python3
|
||||
# CLI directly called by running the howdy command
|
||||
|
||||
# Import required modules
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import getpass
|
||||
import argparse
|
||||
import builtins
|
||||
|
||||
# Try to get the original username (not "root") from shell
|
||||
user = subprocess.check_output("echo $(logname 2>/dev/null || echo $SUDO_USER)", shell=True).decode("ascii").strip()
|
||||
|
||||
# If that fails, try to get the direct user
|
||||
if user == "root" or user == "":
|
||||
env_user = getpass.getuser().strip()
|
||||
|
||||
# If even that fails, error out
|
||||
if env_user == "":
|
||||
print("Could not determine user, please use the --user flag")
|
||||
sys.exit(1)
|
||||
else:
|
||||
user = env_user
|
||||
|
||||
# Basic command setup
|
||||
parser = argparse.ArgumentParser(description="Command line interface for Howdy face authentication.",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
add_help=False,
|
||||
prog="howdy",
|
||||
epilog="For support please visit\nhttps://github.com/Boltgolt/howdy")
|
||||
|
||||
# Add an argument for the command
|
||||
parser.add_argument("command",
|
||||
help="The command option to execute, can be one of the following: add, clear, config, disable, list, remove or test.",
|
||||
metavar="command",
|
||||
choices=["add", "clear", "config", "disable", "list", "remove", "test"])
|
||||
|
||||
# Add an argument for the extra arguments of diable and remove
|
||||
parser.add_argument("argument",
|
||||
help="Either 0 (enable) or 1 (disable) for the disable command, or the model ID for the remove command.",
|
||||
nargs="?")
|
||||
|
||||
# Add the user flag
|
||||
parser.add_argument("-U", "--user",
|
||||
default=user,
|
||||
help="Set the user account to use.")
|
||||
|
||||
# Add the -y flag
|
||||
parser.add_argument("-y",
|
||||
help="Skip all questions.",
|
||||
action="store_true")
|
||||
|
||||
# Overwrite the default help message so we can use a uppercase S
|
||||
parser.add_argument("-h", "--help",
|
||||
action="help",
|
||||
default=argparse.SUPPRESS,
|
||||
help="Show this help message and exit.")
|
||||
|
||||
# If we only have 1 argument we print the help text
|
||||
if len(sys.argv) < 2:
|
||||
print("current active user: " + user + "\n")
|
||||
parser.print_help()
|
||||
sys.exit(0)
|
||||
|
||||
# Parse all arguments above
|
||||
args = parser.parse_args()
|
||||
|
||||
# Save the args and user as builtins which can be accessed by the imports
|
||||
builtins.howdy_args = args
|
||||
builtins.howdy_user = args.user
|
||||
|
||||
# Check if we have rootish rights
|
||||
# This is this far down the file so running the command for help is always possible
|
||||
if os.getenv("SUDO_USER") is None:
|
||||
print("Please run this command with sudo")
|
||||
sys.exit(1)
|
||||
|
||||
# Beond this point the user can't change anymore, if we still have root as user we need to abort
|
||||
if args.user == "root":
|
||||
print("Can't run howdy commands as root, please run this command with the --user flag")
|
||||
sys.exit(1)
|
||||
|
||||
# Execute the right command
|
||||
if args.command == "add":
|
||||
import cli.add
|
||||
elif args.command == "clear":
|
||||
import cli.clear
|
||||
elif args.command == "config":
|
||||
import cli.config
|
||||
elif args.command == "disable":
|
||||
import cli.disable
|
||||
elif args.command == "list":
|
||||
import cli.list
|
||||
elif args.command == "remove":
|
||||
import cli.remove
|
||||
elif args.command == "test":
|
||||
import cli.test
|
||||
|
|
@ -8,6 +8,7 @@ import sys
|
|||
import json
|
||||
import cv2
|
||||
import configparser
|
||||
import builtins
|
||||
|
||||
# Try to import face_recognition and give a nice error if we can't
|
||||
# Add should be the first point where import issues show up
|
||||
|
|
@ -18,7 +19,7 @@ except ImportError as err:
|
|||
|
||||
print("\nCan't import the face_recognition module, check the output of")
|
||||
print("pip3 show face_recognition")
|
||||
sys.exit()
|
||||
sys.exit(1)
|
||||
|
||||
# Get the absolute path to the current file
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
|
@ -27,8 +28,7 @@ path = os.path.dirname(os.path.abspath(__file__))
|
|||
config = configparser.ConfigParser()
|
||||
config.read(path + "/../config.ini")
|
||||
|
||||
# The current user
|
||||
user = sys.argv[1]
|
||||
user = builtins.howdy_user
|
||||
# The permanent file to store the encoded model in
|
||||
enc_file = path + "/../models/" + user + ".dat"
|
||||
# Known encodings
|
||||
|
|
@ -46,11 +46,11 @@ except FileNotFoundError:
|
|||
encodings = []
|
||||
|
||||
# Print a warning if too many encodings are being added
|
||||
if len(encodings) > 2:
|
||||
if len(encodings) > 3:
|
||||
print("WARNING: Every additional model slows down the face recognition engine")
|
||||
print("Press ctrl+C to cancel")
|
||||
print("Press ctrl+C to cancel\n")
|
||||
|
||||
print("Adding face model for the user account " + user)
|
||||
print("Adding face model for the user " + user)
|
||||
|
||||
# Set the default label
|
||||
label = "Initial model"
|
||||
|
|
@ -59,12 +59,16 @@ label = "Initial model"
|
|||
if len(encodings) > 0:
|
||||
label = "Model #" + str(len(encodings) + 1)
|
||||
|
||||
# Ask the user for a custom label
|
||||
label_in = input("Enter a label for this new model [" + label + "]: ")
|
||||
# Keep de default name if we can't ask questions
|
||||
if builtins.howdy_args.y:
|
||||
print("Using default label \"" + label + "\" because of -y flag")
|
||||
else:
|
||||
# Ask the user for a custom label
|
||||
label_in = input("Enter a label for this new model [" + label + "]: ")
|
||||
|
||||
# Set the custom label (if any) and limit it to 24 characters
|
||||
if label_in != "":
|
||||
label = label_in[:24]
|
||||
# Set the custom label (if any) and limit it to 24 characters
|
||||
if label_in != "":
|
||||
label = label_in[:24]
|
||||
|
||||
# Prepare the metadata for insertion
|
||||
insert_model = {
|
||||
|
|
@ -106,12 +110,12 @@ while frames < 60:
|
|||
# If 0 faces are detected we can't continue
|
||||
if len(enc) == 0:
|
||||
print("No face detected, aborting")
|
||||
sys.exit()
|
||||
sys.exit(1)
|
||||
|
||||
# If more than 1 faces are detected we can't know wich one belongs to the user
|
||||
if len(enc) > 1:
|
||||
print("Multiple faces detected, aborting")
|
||||
sys.exit()
|
||||
sys.exit(1)
|
||||
|
||||
# Totally clean array that can be exported as JSON
|
||||
clean_enc = []
|
||||
|
|
@ -130,5 +134,6 @@ with open(enc_file, "w") as datafile:
|
|||
json.dump(encodings, datafile)
|
||||
|
||||
# Give let the user know how it went
|
||||
print("Scan complete")
|
||||
print("\nAdded a new model to " + user)
|
||||
print("""Scan complete
|
||||
|
||||
Added a new model to """ + user)
|
||||
|
|
@ -3,30 +3,33 @@
|
|||
# Import required modules
|
||||
import os
|
||||
import sys
|
||||
import builtins
|
||||
|
||||
# Get the full path to this file
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
# Get the passed user
|
||||
user = sys.argv[1]
|
||||
user = builtins.howdy_user
|
||||
|
||||
# Check if the models folder is there
|
||||
if not os.path.exists(path + "/../models"):
|
||||
print("No models created yet, can't clear them if they don't exist")
|
||||
sys.exit()
|
||||
sys.exit(1)
|
||||
|
||||
# Check if the user has a models file to delete
|
||||
if not os.path.isfile(path + "/../models/" + user + ".dat"):
|
||||
print(user + " has no models or they have been cleared already")
|
||||
sys.exit()
|
||||
sys.exit(1)
|
||||
|
||||
# Double check with the user
|
||||
print("This will clear all models for " + user)
|
||||
ans = input("Do you want to continue [y/N]: ")
|
||||
# Only ask the user if there's no -y flag
|
||||
if not builtins.howdy_args.y:
|
||||
# Double check with the user
|
||||
print("This will clear all models for " + user)
|
||||
ans = input("Do you want to continue [y/N]: ")
|
||||
|
||||
# Abort if they don't answer y or Y
|
||||
if (ans.lower() != "y"):
|
||||
print('\nInerpeting as a "NO"')
|
||||
sys.exit()
|
||||
# Abort if they don't answer y or Y
|
||||
if (ans.lower() != "y"):
|
||||
print('\nInerpeting as a "NO"')
|
||||
sys.exit(1)
|
||||
|
||||
# Delete otherwise
|
||||
os.remove(path + "/../models/" + user + ".dat")
|
||||
15
src/cli/config.py
Normal file
15
src/cli/config.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Open the config file in gedit
|
||||
|
||||
# Import required modules
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
# Let the user know what we're doing
|
||||
print("Opening condig.ini in gedit")
|
||||
|
||||
# Open gedit as a subprocess and fork it
|
||||
subprocess.Popen(["gedit", os.path.dirname(os.path.realpath(__file__)) + "/../config.ini"],
|
||||
cwd="/",
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
41
src/cli/disable.py
Normal file
41
src/cli/disable.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Set the disable flag
|
||||
|
||||
# Import required modules
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import builtins
|
||||
import fileinput
|
||||
import configparser
|
||||
|
||||
# Get the absolute filepath
|
||||
config_path = os.path.dirname(os.path.abspath(__file__)) + "/../config.ini"
|
||||
|
||||
# Read config from disk
|
||||
config = configparser.ConfigParser()
|
||||
config.read(config_path)
|
||||
|
||||
# Check if enough arguments have been passed
|
||||
if builtins.howdy_args.argument == None:
|
||||
print("Please add a 0 (enable) or a 1 (disable) as an argument")
|
||||
sys.exit(1)
|
||||
|
||||
# Translate the argument to the right string
|
||||
if builtins.howdy_args.argument == "1":
|
||||
out_value = "true"
|
||||
elif builtins.howdy_args.argument == "0":
|
||||
out_value = "false"
|
||||
else:
|
||||
# Of it's not a 0 or a 1, it's invalid
|
||||
print("Please only use a 0 (enable) or a 1 (disable) as an argument")
|
||||
sys.exit(1)
|
||||
|
||||
# Loop though the config file and only replace the line containing the disable config
|
||||
for line in fileinput.input([config_path], inplace=1):
|
||||
print(line.replace("disabled = " + config.get("core", "disabled"), "disabled = " + out_value), end="")
|
||||
|
||||
# Print what we just did
|
||||
if builtins.howdy_args.argument == "1":
|
||||
print("Howdy has been disabled")
|
||||
else:
|
||||
print("Howdy has been enabled")
|
||||
|
|
@ -5,10 +5,11 @@ import sys
|
|||
import os
|
||||
import json
|
||||
import time
|
||||
import builtins
|
||||
|
||||
# Get the absolute path and the username
|
||||
path = os.path.dirname(os.path.realpath(__file__)) + "/.."
|
||||
user = sys.argv[1]
|
||||
user = builtins.howdy_user
|
||||
|
||||
# Check if the models file has been created yet
|
||||
if not os.path.exists(path + "/models"):
|
||||
|
|
@ -4,22 +4,23 @@
|
|||
import sys
|
||||
import os
|
||||
import json
|
||||
import builtins
|
||||
|
||||
# Get the absolute path and the username
|
||||
path = os.path.dirname(os.path.realpath(__file__)) + "/.."
|
||||
user = sys.argv[1]
|
||||
user = builtins.howdy_user
|
||||
|
||||
# Check if enough arguments have been passed
|
||||
if len(sys.argv) == 3:
|
||||
print("Please add the ID of the model to remove as an argument")
|
||||
if builtins.howdy_args.argument == None:
|
||||
print("Please add the ID of the model you want to remove as an argument")
|
||||
print("You can find the IDs by running:")
|
||||
print("\n\thowdy " + user + " list\n")
|
||||
print("\n\thowdy list\n")
|
||||
sys.exit(1)
|
||||
|
||||
# Check if the models file has been created yet
|
||||
if not os.path.exists(path + "/models"):
|
||||
print("Face models have not been initialized yet, please run:")
|
||||
print("\n\thowdy " + user + " add\n")
|
||||
print("\n\thowdy add\n")
|
||||
sys.exit(1)
|
||||
|
||||
# Path to the models file
|
||||
|
|
@ -30,7 +31,7 @@ try:
|
|||
encodings = json.load(open(enc_file))
|
||||
except FileNotFoundError:
|
||||
print("No face model known for the user " + user + ", please run:")
|
||||
print("\n\thowdy " + user + " add\n")
|
||||
print("\n\thowdy add\n")
|
||||
sys.exit(1)
|
||||
|
||||
# Tracks if a encoding with that id has been found
|
||||
|
|
@ -38,24 +39,28 @@ found = False
|
|||
|
||||
# Loop though all encodings and check if they match the argument
|
||||
for enc in encodings:
|
||||
if str(enc["id"]) == sys.argv[3]:
|
||||
# Double check with the user
|
||||
print('This will remove the model called "' + enc["label"] + '" for ' + user)
|
||||
ans = input("Do you want to continue [y/N]: ")
|
||||
if str(enc["id"]) == builtins.howdy_args.argument:
|
||||
# Only ask the user if there's no -y flag
|
||||
if not builtins.howdy_args.y:
|
||||
# Double check with the user
|
||||
print('This will remove the model called "' + enc["label"] + '" for ' + user)
|
||||
ans = input("Do you want to continue [y/N]: ")
|
||||
|
||||
# Abort if the answer isn't yes
|
||||
if (ans.lower() != "y"):
|
||||
print('\nInerpeting as a "NO"')
|
||||
sys.exit()
|
||||
# Abort if the answer isn't yes
|
||||
if (ans.lower() != "y"):
|
||||
print('\nInerpeting as a "NO"')
|
||||
sys.exit()
|
||||
|
||||
# Add a padding empty line
|
||||
print()
|
||||
|
||||
# Mark as found and print an enter
|
||||
found = True
|
||||
print()
|
||||
break
|
||||
|
||||
# Abort if no matching id was found
|
||||
if not found:
|
||||
print("No model with ID " + sys.argv[3] + " exists for " + user)
|
||||
print("No model with ID " + builtins.howdy_args.argument + " exists for " + user)
|
||||
sys.exit()
|
||||
|
||||
# Remove the entire file if this encoding is the only one
|
||||
|
|
@ -68,11 +73,11 @@ else:
|
|||
|
||||
# Loop though all encodin and only add thos that don't need to be removed
|
||||
for enc in encodings:
|
||||
if str(enc["id"]) != sys.argv[3]:
|
||||
if str(enc["id"]) != builtins.howdy_args.argument:
|
||||
new_encodings.append(enc)
|
||||
|
||||
# Save this new set to disk
|
||||
# Save this new set to disk
|
||||
with open(enc_file, "w") as datafile:
|
||||
json.dump(new_encodings, datafile)
|
||||
|
||||
print("Removed model " + sys.argv[3])
|
||||
print("Removed model " + builtins.howdy_args.argument)
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
# Howdy config file
|
||||
|
||||
[core]
|
||||
# Do not print anything when a face verification succeeds
|
||||
no_confirmation = false
|
||||
|
|
@ -11,6 +13,10 @@ suppress_unknown = false
|
|||
# Expirimental, can behave incorrectly on some systems
|
||||
dismiss_lockscreen = false
|
||||
|
||||
# Disable howdy in the PAM
|
||||
# The howdy command will still function
|
||||
disabled = false
|
||||
|
||||
[video]
|
||||
# The certainty of the detected face belonging to the user of the account
|
||||
# On a scale from 1 to 10, values above 5 are not recommended
|
||||
|
|
@ -15,6 +15,10 @@ config.read(os.path.dirname(os.path.abspath(__file__)) + "/config.ini")
|
|||
def doAuth(pamh):
|
||||
"""Start authentication in a seperate process"""
|
||||
|
||||
# Abort is Howdy is disabled
|
||||
if config.get("core", "disabled") == "true":
|
||||
sys.exit(0)
|
||||
|
||||
# Run compare as python3 subprocess to circumvent python version and import issues
|
||||
status = subprocess.call(["python3", os.path.dirname(os.path.abspath(__file__)) + "/compare.py", pamh.get_user()])
|
||||
|
||||
36
uninstall.py
36
uninstall.py
|
|
@ -1,36 +0,0 @@
|
|||
# Completely remove howdy from the system
|
||||
|
||||
# Import required modules
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Check if we're running as root
|
||||
user = os.getenv("SUDO_USER")
|
||||
if user is None:
|
||||
print("Please run the uninstaller as a sudo user")
|
||||
sys.exit()
|
||||
|
||||
# Double check with the user for the last time
|
||||
print("This will remove Howdy and all models generated with it")
|
||||
ans = input("Do you want to continue? [y/N]: ")
|
||||
|
||||
# Abort if they don't say yes
|
||||
if (ans.lower() != "y"):
|
||||
sys.exit()
|
||||
|
||||
# Remove files and symlinks
|
||||
subprocess.call(["rm -rf /lib/security/howdy/"], shell=True)
|
||||
subprocess.call(["rm /usr/bin/howdy"], shell=True)
|
||||
subprocess.call(["rm /etc/bash_completion.d/howdy"], shell=True)
|
||||
|
||||
# Remove face_recognition and dlib
|
||||
subprocess.call(["pip3 uninstall face_recognition dlib -y"], shell=True)
|
||||
|
||||
# Print a tearbending message
|
||||
print("""
|
||||
Howdy has been uninstalled :'(
|
||||
|
||||
There are still lines in /etc/pam.d/common-auth that can't be removed automatically
|
||||
Run "nano /etc/pam.d/common-auth" to remove them by hand\
|
||||
""")
|
||||
Loading…
Reference in a new issue