Преглед изворни кода

Introduce --exclude parameter in cli

Allow user to exclude some parameters.
Fixes https://github.com/lesspass/lesspass/issues/528
pull/544/head
Guillaume Vincent пре 4 година
родитељ
комит
9670f95f69
11 измењених фајлова са 75 додато и 15 уклоњено
  1. +3
    -1
      cli/README.md
  2. +5
    -0
      cli/lesspass/cli.py
  3. +9
    -4
      cli/lesspass/core.py
  4. +2
    -0
      cli/lesspass/exceptions.py
  5. +21
    -8
      cli/lesspass/password.py
  6. +1
    -0
      cli/lesspass/profile.py
  7. +1
    -1
      cli/lesspass/version.py
  8. +9
    -0
      cli/tests/test_cli.py
  9. +17
    -1
      cli/tests/test_functional.py
  10. +6
    -0
      cli/tests/test_password_generation.py
  11. +1
    -0
      cli/tests/test_profile.py

+ 3
- 1
cli/README.md Прегледај датотеку

@@ -29,11 +29,13 @@
--no-uppercase remove uppercase from password
--no-digits remove digits from password
--no-symbols remove symbols from password
--exclude remove chars from password
-c, --clipboard copy generated password to clipboard rather than displaying it.
Need pbcopy (OSX), xsel or xclip (Linux) or clip (Windows).
Need pbcopy (OSX), xsel or xclip (Linux) or clip (Windows).
-v, --version lesspass version number

## Examples

### no symbols

lesspass site login masterpassword --no-symbols


+ 5
- 0
cli/lesspass/cli.py Прегледај датотеку

@@ -81,6 +81,11 @@ def parse_args(args):
action="store_true",
help="copy the password to clipboard",
)
parser.add_argument(
"--exclude",
default=None,
help="exclude char from generated password",
)

lowercase_group = parser.add_mutually_exclusive_group()
lowercase_group.add_argument(


+ 9
- 4
cli/lesspass/core.py Прегледај датотеку

@@ -4,6 +4,7 @@ import sys
import traceback
import signal

from lesspass import exceptions
from lesspass.version import __version__
from lesspass.cli import parse_args
from lesspass.profile import create_profile
@@ -17,7 +18,7 @@ def main(args=sys.argv[1:]):
args = parse_args(args)
if args.clipboard and not get_system_copy_command():
print(
"ERROR To use the option -c (--copy) you need pbcopy on OSX, "
"error: To use the option -c (--copy) you need pbcopy on OSX, "
+ "xsel, xclip, or wl-clipboard on Linux, and clip on Windows"
)
sys.exit(3)
@@ -32,15 +33,19 @@ def main(args=sys.argv[1:]):
args.master_password = getpass.getpass("Master Password: ")

if not args.site:
print("ERROR argument SITE is required but was not provided.")
print("error: argument SITE is required but was not provided.")
sys.exit(4)

if not args.master_password:
print("ERROR argument MASTER_PASSWORD is required but was not provided")
print("error: argument MASTER_PASSWORD is required but was not provided")
sys.exit(5)

profile, master_password = create_profile(args)
generated_password = generate_password(profile, master_password)
try:
generated_password = generate_password(profile, master_password)
except exceptions.ExcludeAllCharsAvailable:
print("error: you can't exclude all chars available")
sys.exit(6)

if args.clipboard:
try:


+ 2
- 0
cli/lesspass/exceptions.py Прегледај датотеку

@@ -0,0 +1,2 @@
class ExcludeAllCharsAvailable(Exception):
pass

+ 21
- 8
cli/lesspass/password.py Прегледај датотеку

@@ -20,6 +20,8 @@

import hashlib

from lesspass import exceptions

CHARACTER_SUBSETS = {
"lowercase": "abcdefghijklmnopqrstuvwxyz",
"uppercase": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
@@ -40,7 +42,14 @@ def _calc_entropy(password_profile, master_password):
return int(hex_entropy, 16)


def _get_set_of_characters(rules=None):
def _remove_excluded_chars(string, exclude):
new_string = "".join(c for c in string if c not in exclude)
if len(new_string) == 0:
raise exceptions.ExcludeAllCharsAvailable
return new_string


def _get_set_of_characters(rules=None, exclude=""):
if rules is None:
return (
CHARACTER_SUBSETS["lowercase"]
@@ -48,10 +57,10 @@ def _get_set_of_characters(rules=None):
+ CHARACTER_SUBSETS["digits"]
+ CHARACTER_SUBSETS["symbols"]
)
set_of_chars = ""
pool_of_chars = ""
for rule in rules:
set_of_chars += CHARACTER_SUBSETS[rule]
return set_of_chars
pool_of_chars += CHARACTER_SUBSETS[rule]
return _remove_excluded_chars(pool_of_chars, exclude)


def _consume_entropy(generated_password, quotient, set_of_characters, max_length):
@@ -72,10 +81,11 @@ def _insert_string_pseudo_randomly(generated_password, entropy, string):
return generated_password


def _get_one_char_per_rule(entropy, rules):
def _get_one_char_per_rule(entropy, rules, exclude=""):
one_char_per_rules = ""
for rule in rules:
value, entropy = _consume_entropy("", entropy, CHARACTER_SUBSETS[rule], 1)
available_chars = _remove_excluded_chars(CHARACTER_SUBSETS[rule], exclude)
value, entropy = _consume_entropy("", entropy, available_chars, 1)
one_char_per_rules += value
return [one_char_per_rules, entropy]

@@ -89,12 +99,15 @@ def _get_configured_rules(password_profile):

def _render_password(entropy, password_profile):
rules = _get_configured_rules(password_profile)
set_of_characters = _get_set_of_characters(rules)
excluded_chars = (
password_profile["exclude"] if "exclude" in password_profile else ""
)
set_of_characters = _get_set_of_characters(rules, excluded_chars)
password, password_entropy = _consume_entropy(
"", entropy, set_of_characters, password_profile["length"] - len(rules)
)
characters_to_add, character_entropy = _get_one_char_per_rule(
password_entropy, rules
password_entropy, rules, excluded_chars
)
return _insert_string_pseudo_randomly(
password, character_entropy, characters_to_add


+ 1
- 0
cli/lesspass/profile.py Прегледај датотеку

@@ -8,6 +8,7 @@ def create_profile(args):
"counter": args.counter,
"site": args.site,
"login": args.login or "",
"exclude": args.exclude or "",
}
if args.l or args.u or args.d or args.s:
profile["lowercase"] = args.l


+ 1
- 1
cli/lesspass/version.py Прегледај датотеку

@@ -1 +1 @@
__version__ = "9.1.9"
__version__ = "9.2.0"

+ 9
- 0
cli/tests/test_cli.py Прегледај датотеку

@@ -105,3 +105,12 @@ class TestParseArgs(unittest.TestCase):

def test_parse_args_prompt_short(self):
self.assertTrue(parse_args(["-p"]).prompt)

def test_parse_args_exclude_default(self):
self.assertEqual(parse_args(["site"]).exclude, None)

def test_parse_args_exclude(self):
self.assertEqual(parse_args(["site", "--exclude", "!@$*+-"]).exclude, "!@$*+-")

def test_parse_args_exclude_single_and_double_quote(self):
self.assertEqual(parse_args(["site", "--exclude", "\"'"]).exclude, "\"'")

+ 17
- 1
cli/tests/test_functional.py Прегледај датотеку

@@ -21,4 +21,20 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(range_type('5'), 5)
self.assertEqual(range_type('35'), 35)
with self.assertRaises(argparse.ArgumentTypeError):
range_type('2')
range_type('2')

def test_exclude(self):
p = pexpect.spawn(
'python3 lesspass/core.py site login masterpassword --exclude "!@$*+-8"'
)
output = p.read().decode()
for c in "!@$*+-8":
self.assertTrue(c not in output)

def test_exclude(self):
p = pexpect.spawn(
'python3 lesspass/core.py site login masterpassword -d -L6 --exclude "0123456789"'
)
output = p.read().decode()

self.assertTrue("error: you can't exclude all chars available" in output)

+ 6
- 0
cli/tests/test_password_generation.py Прегледај датотеку

@@ -154,6 +154,12 @@ class TestPassword(unittest.TestCase):
"abcdefghijklmnopqrstuvwxyz0123456789",
)

def test_get_set_of_characters_with_several_rules_and_exclude(self):
self.assertEqual(
password._get_set_of_characters(["lowercase", "digits"], 'iy4!'),
"abcdefghjklmnopqrstuvwxz012356789",
)

def test_consume_entropy(self):
entropy = b"dc33d431bce2b01182c613382483ccdb0e2f66482cbba5e9d07dab34acc7eb1e"



+ 1
- 0
cli/tests/test_profile.py Прегледај датотеку

@@ -15,6 +15,7 @@ class TestProfile(unittest.TestCase):
self.assertEqual(profile["counter"], 1)
self.assertEqual(profile["site"], "site")
self.assertEqual(profile["login"], "login")
self.assertEqual(profile["exclude"], "")
self.assertIsNone(master_password)

def test_create_profile_login(self):


Loading…
Откажи
Сачувај