0
0
Fork 0
mirror of https://github.com/boltgolt/howdy.git synced 2024-09-19 09:51:19 +02:00

Completed CLI rework

This commit is contained in:
boltgolt 2018-04-13 21:19:28 +02:00
parent 0cdb4b78bd
commit 4f9e240869
11 changed files with 189 additions and 137 deletions

View file

@ -14,7 +14,7 @@ sudo apt update
sudo apt install howdy
```
This will guide you through the installation. When that's done run `sudo howdy add $USER` and replace `$USER` with your username to add a face model.
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.
@ -22,22 +22,22 @@ If nothing went wrong we should be able to run sudo by just showing your face. O
### 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` to list the available options.
Usage:
```
howdy <command> [user] [argument]
howdy [-U user] [-y] command [argument]
```
| Command | Description | User required |
|-----------|-----------------------------------------------|---------------|
| `add` | Add a new face model for the given user | Yes |
| `clear` | Remove all face models for the given user | Yes |
| `config` | Open the config file in nano | No |
| `disable` | Disable or enable howdy | No |
| `list` | List all saved face models for the given user | Yes |
| `remove` | Remove a specific model for the given user | Yes |
| `test` | Test the camera and recognition methods | No |
| Command | Description |
|-----------|-----------------------------------------------|
| `add` | Add a new face model for the given user |
| `clear` | Remove all face models for the given user |
| `config` | Open the config file in nano |
| `disable` | Disable or enable howdy |
| `list` | List all saved face models for the given user |
| `remove` | Remove a specific model for the given user |
| `test` | Test the camera and recognition methods |
### Troubleshooting

View file

@ -7,102 +7,91 @@ 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 env_user == "root" or env_user == "":
# 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
# Check if we have rootish rights
if os.getenv("SUDO_USER") is None:
print("Please run this command with sudo")
sys.exit(1)
# 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")
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"])
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 or 1 for the disable command, or the model ID for the remove command.",
nargs="?")
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,
default=user,
help="Set the user account to use.")
# Add the -y flag
parser.add_argument("-y",
help="Skip all questions.",
action="store_true")
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,
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()
print(args)
sys.exit(0)
# 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 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()
# 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)
# 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":
# 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
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(1)
# 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>")
sys.exit(1)
else:
print('Unknown command "' + cmd + '"')
import cli.help
sys.exit(1)

View file

@ -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)

View file

@ -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
View 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
View 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")

View file

@ -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\
""")

View file

@ -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"):

View file

@ -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
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)

View file

@ -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

View file

@ -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()])