aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Cleberg <hello@cleberg.net>2024-11-06 23:23:27 -0600
committerChristian Cleberg <hello@cleberg.net>2024-11-06 23:23:27 -0600
commit6dde4dd0bc5e5f91f89587c75a30c9ef7a24494c (patch)
tree6cf4b78ddd63a4606e19fcad423ed2e19ad2a268 /src
parentb5a5fadff88615c8da8a9feb80c86fd8adb238f5 (diff)
downloadyoshi-cli-6dde4dd0bc5e5f91f89587c75a30c9ef7a24494c.tar.gz
yoshi-cli-6dde4dd0bc5e5f91f89587c75a30c9ef7a24494c.tar.bz2
yoshi-cli-6dde4dd0bc5e5f91f89587c75a30c9ef7a24494c.zip
package as a cli app
Diffstat (limited to 'src')
-rw-r--r--src/account.py43
-rw-r--r--src/crypto.py45
-rw-r--r--src/database.py125
-rw-r--r--src/process.py225
-rw-r--r--src/yoshi_cli.egg-info/PKG-INFO198
-rw-r--r--src/yoshi_cli.egg-info/SOURCES.txt11
-rw-r--r--src/yoshi_cli.egg-info/dependency_links.txt1
-rw-r--r--src/yoshi_cli.egg-info/top_level.txt4
8 files changed, 0 insertions, 652 deletions
diff --git a/src/account.py b/src/account.py
deleted file mode 100644
index 79fcc13..0000000
--- a/src/account.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""
-This script imports necessary modules for database interactions.
-
-Modules imported:
- - database: A custom module providing database functionality.
-"""
-
-import database
-
-
-class Account:
- """Represents a login account."""
-
- def __init__(self, uuid: str, application: str, #pylint: disable=R0913,R0917
- username: str, #pylint: disable=R0913,R0917
- password: str, url: str) -> None: #pylint: disable=R0913,R0917
- self.uuid = uuid
- self.application = application
- self.username = username
- self.password = password
- self.url = url
-
- def display_account(self) -> None:
- """Print the account details."""
- print('ID:', self.uuid)
- print('Application:', self.application)
- print('Username:', self.username)
- print('Password:', self.password)
- print('URL:', self.url)
-
- def save_account(self) -> None:
- """Save the account details to the database."""
- database.add_account(
- self.uuid, self.application, self.username, self.password, self.url)
-
- def delete_account(self) -> bool:
- """Delete the account from the database.
-
- Returns:
- bool: True if the deletion was successful.
- """
- database.delete_account(self.uuid)
- return True
diff --git a/src/crypto.py b/src/crypto.py
deleted file mode 100644
index 9b0a423..0000000
--- a/src/crypto.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""
-This module imports the Fernet symmetric encryption algorithm from the cryptography library.
-
-It allows for secure encryption and decryption of data using a secret key.
-"""
-
-from cryptography.fernet import Fernet
-
-VAULT_FILE = 'vault.sqlite'
-
-
-def generate_key() -> bytes:
- """Generates a new encryption key."""
- return Fernet.generate_key()
-
-
-def load_key(key_file: str) -> bytes:
- """
- Loads an existing encryption key from the file.
-
- Args:
- key_file (str): Path to the key file.
- """
- with open(key_file, 'rb') as key:
- return key.read()
-
-
-def encrypt(key: bytes, filename: str = VAULT_FILE) -> None:
- """Encrypts the data in the specified file using the provided key."""
- f = Fernet(key)
- with open(filename, 'rb') as vault:
- data = vault.read()
- encrypted_data = f.encrypt(data)
- with open(filename, 'wb') as vault:
- vault.write(encrypted_data)
-
-
-def decrypt(key: bytes, filename: str = VAULT_FILE) -> None:
- """Decrypts the data in the specified file using the provided key."""
- f = Fernet(key)
- with open(filename, 'rb') as vault:
- encrypted_data = vault.read()
- decrypted_data = f.decrypt(encrypted_data)
- with open(filename, 'wb') as vault:
- vault.write(decrypted_data)
diff --git a/src/database.py b/src/database.py
deleted file mode 100644
index e1e2e78..0000000
--- a/src/database.py
+++ /dev/null
@@ -1,125 +0,0 @@
-"""
-This module provides a basic interface for connecting to and interacting with a SQLite database.
-It includes functions for creating connections, executing queries, and retrieving results.
-"""
-
-import sqlite3
-import sys
-import os
-
-VAULT_DECRYPTED = 'vault.sqlite'
-VAULT_ENCRYPTED = 'vault.sqlite.aes'
-
-
-def create_table() -> None:
- """Create the accounts table within the vault database."""
- db_connection = sqlite3.connect(VAULT_DECRYPTED)
- cursor = db_connection.cursor()
- cursor.execute(
- ''' CREATE TABLE IF NOT EXISTS accounts (uuid text, application text,
- username text, password text, url text) '''
- )
- db_connection.commit()
- db_connection.close()
-
-
-def check_table() -> bool:
- """Check if the 'accounts' table exists within the vault database."""
- check = False
- db_connection = sqlite3.connect(VAULT_DECRYPTED)
- cursor = db_connection.cursor()
- cursor.execute(
- ''' SELECT count(name) FROM sqlite_master WHERE type='table'
- AND name='accounts' '''
- )
- if cursor.fetchone()[0] != 1:
- user_choice = input(
- 'Password vault does not exist. Would you like to create it now? (y/n): ')
- if user_choice.lower() == 'y':
- create_table()
- check = True
- else:
- sys.exit('Program aborted upon user request.')
- else:
- check = True
- db_connection.commit()
- db_connection.close()
- return check
-
-
-def add_account(uuid: str, application: str, username: str, password: str,
- url: str) -> None:
- """Add a new account within the vault database."""
- db_connection = sqlite3.connect(VAULT_DECRYPTED)
- cursor = db_connection.cursor()
- cursor.execute(
- ''' INSERT INTO accounts VALUES (:uuid,:application,:username,
- :password,:url) ''', {
- 'uuid': uuid, 'application': application, 'username': username,
- 'password': password, 'url': url
- }
- )
- db_connection.commit()
- db_connection.close()
-
-
-def delete_account(uuid: str) -> None:
- """Delete an account within the vault database by its unique ID."""
- db_connection = sqlite3.connect(VAULT_DECRYPTED)
- cursor = db_connection.cursor()
- cursor.execute(
- ''' DELETE FROM accounts WHERE uuid = :uuid ''', {'uuid': uuid}
- )
- db_connection.commit()
- db_connection.close()
-
-
-def find_account(uuid: str) -> list:
- """Find an account within the vault database by its unique ID."""
- db_connection = sqlite3.connect(VAULT_DECRYPTED)
- cursor = db_connection.cursor()
- cursor.execute(
- ''' SELECT * FROM accounts WHERE uuid = :uuid ''', {'uuid': uuid}
- )
- account = cursor.fetchall()
- db_connection.close()
- return account
-
-
-def find_accounts() -> list:
- """Return all accounts stored within the vault database."""
- db_connection = sqlite3.connect(VAULT_DECRYPTED)
- cursor = db_connection.cursor()
- cursor.execute(''' SELECT * FROM accounts ''')
- accounts = cursor.fetchall()
- db_connection.close()
- return accounts
-
-
-def update_account(field_name: str, new_value: str, uuid: str) -> None:
- """Update an account within the vault database by its unique ID."""
- queries = {
- 'application': 'UPDATE accounts SET application = :new_value WHERE uuid = :uuid',
- 'username': 'UPDATE accounts SET username = :new_value WHERE uuid = :uuid',
- 'password': 'UPDATE accounts SET password = :new_value WHERE uuid = :uuid',
- 'url': 'UPDATE accounts SET url = :new_value WHERE uuid = :uuid'
- }
- db_connection = sqlite3.connect(VAULT_DECRYPTED)
- cursor = db_connection.cursor()
- cursor.execute(queries[field_name], {'new_value': new_value, 'uuid': uuid})
- db_connection.commit()
- db_connection.close()
-
-
-def purge_table() -> None:
- """Purge the 'accounts' table within the vault database."""
- db_connection = sqlite3.connect(VAULT_DECRYPTED)
- cursor = db_connection.cursor()
- cursor.execute(''' DROP TABLE accounts ''')
- db_connection.commit()
- db_connection.close()
-
-
-def purge_database() -> None:
- """Purge the entire vault database."""
- os.remove(VAULT_DECRYPTED)
diff --git a/src/process.py b/src/process.py
deleted file mode 100644
index 0888a78..0000000
--- a/src/process.py
+++ /dev/null
@@ -1,225 +0,0 @@
-"""
-Password Vault Manager
-
-This script provides various functions for managing password vaults.
-It allows users to create, list, edit and delete accounts.
-
-The `Account` class represents an individual account, with attributes for
-the application name, username, password, and URL. The database module is used
-to interact with the SQLite database file (`vault.sqlite`) that stores the
-accounts data.
-
-Functions:
- generate_characters(n): generates a list of random characters
- shuffle_characters(characters): shuffles the characters to create a password
- generate_passphrase(n, sep): generates an XKCD-style passphrase with n words and separator
- list_accounts(): lists all saved accounts in the database
- delete_account(uuid): deletes an account by its UUID
- purge_accounts(): purges the entire database (irreversible)
- create_account(): creates a new account by prompting user for details
- edit_account(uuid, edit_parameter): edits an existing account's details
-
-Usage:
- Run this script in your terminal to access these functions.
-"""
-
-from string import ascii_letters, punctuation, digits
-import random
-import uuid
-from prettytable import PrettyTable
-from account import Account
-import database
-
-
-def generate_characters(n: int) -> list:
- """
- Generates a list of n random characters from the set of ASCII letters,
- punctuation and digits.
-
- Args:
- n (int): The number of characters to generate
-
- Returns:
- list: A list of n random characters
- """
- characters = []
- password_format = ascii_letters + punctuation + digits
- for _ in range(n):
- characters.append(random.choice(password_format))
- return characters
-
-
-def shuffle_characters(characters: list) -> str:
- """
- Shuffles the characters to create a password.
-
- Args:
- characters (list): The list of characters
-
- Returns:
- str: A string representation of the shuffled characters
- """
- random.shuffle(characters)
- character_string = ''.join(characters)
- return character_string
-
-
-def generate_passphrase(n: int, sep: str) -> str:
- """
- Generates an XKCD-style passphrase with n words and separator.
-
- Args:
- n (int): The number of words to include
- sep (str): The separator symbol
-
- Returns:
- str: A string representation of the passphrase
- """
- phrases = []
- lucky_number = random.choice(range(0, n))
- for _ in range(n):
- with open('wordlist.txt', 'r', encoding='utf-8') as file:
- line = random.choice(file.readlines())
- line = line.replace('\n', '')
- if _ == lucky_number:
- phrases.append(line.strip().capitalize() + str(_))
- else:
- phrases.append(line.strip().capitalize())
- passphrase = sep.join(phrases)
- return passphrase
-
-
-def list_accounts() -> None:
- """
- Lists all saved accounts in the database.
-
- Returns:
- None
- """
- accounts = database.find_accounts()
- t = PrettyTable(['UUID', 'Application', 'Username', 'Password', 'URL'])
- for account in accounts:
- t.add_row([account[0], account[1], account[2], account[3], account[4]])
- print(t)
-
-
-def delete_account(account_uuid: str) -> None:
- """
- Deletes an account by its UUID.
-
- Args:
- account_uuid (str): The UUID of the account to delete
-
- Returns:
- None
- """
- account_record = database.find_account(account_uuid)
- account = Account(account_record[0][0],
- account_record[0][1],
- account_record[0][2],
- account_record[0][3],
- account_record[0][4])
- if account.delete_account():
- print('Account successfully deleted.')
-
-
-def purge_accounts() -> None:
- """
- Purges the entire database (irreversible).
-
- Returns:
- None
- """
- check = input(
- '''Are you absolutely sure you want to delete your password vault?
- This action is irreversible. (y/n): ''')
- if check.lower() == 'y':
- database.purge_table()
- database.purge_database()
- print('The password vault has been purged. You may now exit or create a new one.')
-
-
-def create_account() -> None:
- """
- Creates a new account by prompting user for details.
-
- Returns:
- None
- """
- application_string = input('Please enter a name for this account: ')
- username_string = input('Please enter your username for this account: ')
- url_string = input('(Optional) Please enter a URL for this account: ')
-
- password_type = input(
- '''Do you want a random character password (p), an XKCD-style passphrase
-(x), or a custom password (c)? (p|x|c): '''
- )
- if password_type not in ['p', 'x', 'c']:
- print('Error: Invalid choice. Please choose p, x, or c.')
- return
-
- if password_type == 'x':
- password_length = int(
- input('Please enter number of words to include (min. 2): ')
- )
- if password_length < 3:
- print('Error: Your passphrase length must be at least 3 words.')
- return
- password_separator = input(
- 'Please enter your desired separator symbol (_,-, ~, etc.): '
- )
- password_string = generate_passphrase(password_length, password_separator)
- elif password_type == 'p':
- password_length = int(
- input('Please enter your desired password length (min. 8): ')
- )
- if password_length < 8:
- print('Error: Your password length must be at least 8 characters.')
- return
- password_string = generate_password(password_length) # pylint: disable=undefined-variable
- else:
- password_string = input('Please enter your desired password: ')
-
- account = Account(str(uuid.uuid4()), application_string,
- username_string, password_string, url_string)
- account.save_account()
- print('Account saved to the vault. Use `--list` to see all saved accounts.')
-
-
-
-def edit_account(account_uuid: str, edit_parameter: int) -> None:
- """
- Allow users to edit any account information except the UUID.
-
- Args:
- account_uuid (str): Unique identifier of the account.
- edit_parameter (int): Parameter indicating which field to edit.
- Valid values are 1 for application name, 2 for username,
- 3 for password, and 4 for URL.
- """
- field_name, new_value = ''
- if edit_parameter == 1:
- field_name = 'application'
- new_value = input('Please enter your desired Application name: ')
- elif edit_parameter == 2:
- field_name = 'username'
- new_value = input('Please enter your desired username: ')
- elif edit_parameter == 3:
- field_name = 'password'
- type_check = input(
- 'Do you want a new random password or to enter a custom password? '
- '(random/custom): ').lower()
- if type_check == 'random':
- password_length = int(input('Please enter your desired password length: '))
- if password_length < 8:
- print('Error: Your password length must be at least 8 characters.')
- else:
- password_characters = generate_characters(password_length)
- new_value = shuffle_characters(password_characters)
- else:
- new_value = input('Please enter your desired password: ')
- elif edit_parameter == 4:
- field_name = 'url'
- new_value = input('Please enter your desired URL: ')
- database.update_account(field_name, new_value, account_uuid)
- print('Account successfully updated.')
diff --git a/src/yoshi_cli.egg-info/PKG-INFO b/src/yoshi_cli.egg-info/PKG-INFO
deleted file mode 100644
index e545db5..0000000
--- a/src/yoshi_cli.egg-info/PKG-INFO
+++ /dev/null
@@ -1,198 +0,0 @@
-Metadata-Version: 2.1
-Name: yoshi-cli
-Version: 0.1.0
-Summary: A password manager for the command line.
-Author-email: Christian Cleberg <hello@cleberg.net>
-Project-URL: Homepage, https://github.com/ccleberg/yoshi
-Project-URL: Issues, https://github.com/ccleberg/yoshi/issues
-Classifier: Programming Language :: Python :: 3
-Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
-Classifier: Operating System :: OS Independent
-Requires-Python: >=3.8
-Description-Content-Type: text/markdown
-License-File: LICENSE
-
-# Yoshi: A Password Manager
-
-A simple command-line pass manager, writtin in Python + SQLite3. This tool
-allows you to manage accounts and generate random passwords containing ASCII
-letters, numbers, and punctuation (min. 8 characters) or XKCD-like passphrases
-(min. 3 words).
-
-Please note that the script is written in Python 3 - you may need to run the
-script with the `python3` command instead of `python` if your system uses a
-default of Python 2. See the Installation & Usage sections below for more
-information.
-
-# Table of Contents
-
-- [Installation](#installation)
-- [Usage](#usage)
- - [Arguments](#arguments)
-- [Contributing](#contributing)
-
-# Installation
-
-[(Back to top)](#table-of-contents)
-
-To run the script locally, run the following commands:
-
-```bash
-git clone REPO_URL
-```
-
-```bash
-cd yoshi
-```
-
-```bash
-pip3 install -r requirements.txt
-```
-
-```bash
-python3 main.py --help
-```
-
-# Usage
-
-[(Back to top)](#table-of-contents)
-
-All commands can be passed to the program with the following template:
-`python3 main.py <COMMAND> <FLAG> <PARAMETER>`
-
-![Yoshi CLI Help](./examples/yoshi-help.png)
-
-## Arguments
-
-### Summary
-
-<table>
- <thead>
- <tr>
- <td><b>Argument</b></td>
- <td><b>Shortcut</b></td>
- <td><b>Explanation</b></td>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>help</td>
- <td>h</td>
- <td>Print the welcome message</td>
- </tr>
- <tr>
- <td>new</td>
- <td>n</td>
- <td>Create a new account</td>
- </tr>
- <tr>
- <td>list</td>
- <td>l</td>
- <td>List all saved accounts</td>
- </tr>
- <tr>
- <td>edit</td>
- <td>e</td>
- <td>Edit a saved account (see below for required flags)</td>
- </tr>
- <tr>
- <td>delete</td>
- <td>d</td>
- <td>Delete a saved account (see below for required flags)</td>
- </tr>
- <tr>
- <td>purge</td>
- <td>N/A</td>
- <td>Purge all accounts and delete the vault</td>
- </tr>
- <tr>
- <td>encrypt</td>
- <td>N/A</td>
- <td>Encrypt the vault database (see below for required flags)</td>
- </tr>
- <tr>
- <td>decrypt</td>
- <td>N/A</td>
- <td>Decrypt the vault database (see below for required flags)</td>
- </tr>
- </tbody>
-</table>
-
-#### Flags
-
-Flags for the `edit`, `e` command - both are required:
-
-<table>
- <thead>
- <tr>
- <td><b>Argument</b></td>
- <td><b>Shortcut</b></td>
- <td><b>Explanation</b></td>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>--uuid</td>
- <td>-u</td>
- <td>Provide the account UUID to edit</td>
- </tr>
- <tr>
- <td>--field</td>
- <td>-f</td>
- <td>Provide the account field to edit</td>
- </tr>
- </tbody>
-</table>
-
-Flags for the `delete`, `d` command - this flag is required:
-
-<table>
- <thead>
- <tr>
- <td><b>Argument</b></td>
- <td><b>Shortcut</b></td>
- <td><b>Explanation</b></td>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>--uuid</td>
- <td>-u</td>
- <td>Provide the account UUID to delete</td>
- </tr>
- </tbody>
-</table>
-
-Flags for the `encrypt` or `decrypt` command - you must provide at least one
-when encrypting, none are required when decrypting:
-
-<table>
- <thead>
- <tr>
- <td><b>Argument</b></td>
- <td><b>Shortcut</b></td>
- <td><b>Explanation</b></td>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>--generate</td>
- <td>-g</td>
- <td>When encrypting, generate a new key</td>
- </tr>
- <tr>
- <td>--keyfile</td>
- <td>-k</td>
- <td>When encrypting or decrypting, provide the path to a saved key file</td>
- </tr>
- </tbody>
-</table>
-
-![Yoshi CLI New Account](./examples/yoshi-example.png)
-
-# Contributing
-
-[(Back to top)](#table-of-contents)
-
-Any and all contributions are welcome. Feel free to fork the project, add
-features, and submit a pull request.
diff --git a/src/yoshi_cli.egg-info/SOURCES.txt b/src/yoshi_cli.egg-info/SOURCES.txt
deleted file mode 100644
index c8ca832..0000000
--- a/src/yoshi_cli.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-LICENSE
-README.md
-pyproject.toml
-src/account.py
-src/crypto.py
-src/database.py
-src/process.py
-src/yoshi_cli.egg-info/PKG-INFO
-src/yoshi_cli.egg-info/SOURCES.txt
-src/yoshi_cli.egg-info/dependency_links.txt
-src/yoshi_cli.egg-info/top_level.txt \ No newline at end of file
diff --git a/src/yoshi_cli.egg-info/dependency_links.txt b/src/yoshi_cli.egg-info/dependency_links.txt
deleted file mode 100644
index 8b13789..0000000
--- a/src/yoshi_cli.egg-info/dependency_links.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/yoshi_cli.egg-info/top_level.txt b/src/yoshi_cli.egg-info/top_level.txt
deleted file mode 100644
index e855a8f..0000000
--- a/src/yoshi_cli.egg-info/top_level.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-account
-crypto
-database
-process