Skip to content

Commit b9fecd1

Browse files
committed
feat: 🎸 custom ParamType
1 parent 0522d26 commit b9fecd1

File tree

1 file changed

+71
-22
lines changed

1 file changed

+71
-22
lines changed

pyencrypt/cli.py

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import base64
2+
import ipaddress
23
import os
4+
import re
35
import shutil
46
import sys
57
from pathlib import Path
@@ -8,9 +10,10 @@
810

911
from pyencrypt import __description__, __version__
1012
from pyencrypt.decrypt import decrypt_file
11-
from pyencrypt.encrypt import (can_encrypt, encrypt_file, encrypt_key, generate_so_file)
13+
from pyencrypt.encrypt import (can_encrypt, encrypt_file, encrypt_key,
14+
generate_so_file)
1215
from pyencrypt.generate import generate_aes_key
13-
from pyencrypt.license import generate_license_file, MIN_DATETIME, MAX_DATETIME
16+
from pyencrypt.license import MAX_DATETIME, MIN_DATETIME, generate_license_file
1417

1518
VERSION = f"""\
1619
_
@@ -42,7 +45,7 @@
4245
LICENSE_FILE_NAME = click.style("license.lic", blink=True, fg='blue')
4346

4447
SUCCESS_ANSI = click.style('successfully', fg='green')
45-
INVALID_KEY_MSG = click.style('Your encryption key is invalid.', fg='red')
48+
INVALID_KEY_MSG = click.style('Your encryption 🔑 is invalid.', fg='red')
4649

4750
INVALID_EXPIRED_MSG = 'Expired before date must be less than expired after date.'
4851

@@ -68,8 +71,62 @@
6871
DATETIME_FORMATS = ['%Y-%m-%dT%H:%M:%S %z', '%Y-%m-%d %H:%M:%S', '%Y-%m-%d']
6972

7073

71-
def _check_key(key: str) -> bool:
72-
return not (len(key) % 4 or len(base64.b64decode(key)) % 16)
74+
class KeyParamType(click.ParamType):
75+
name = 'key'
76+
77+
def _check_key(self, key: str) -> bool:
78+
return not (len(key) % 4 or len(base64.b64decode(key)) % 16)
79+
80+
def convert(self, value, param, ctx) -> str:
81+
value = click.STRING.convert(value, param, ctx)
82+
if not self._check_key(value):
83+
self.fail(INVALID_KEY_MSG, param, ctx)
84+
return value
85+
86+
def get_metavar(self, param):
87+
return '🔑'
88+
89+
def __repr__(self) -> str:
90+
return "KEY"
91+
92+
93+
class MacAddressParamType(click.ParamType):
94+
name = 'mac_address'
95+
pattern = re.compile(r'^([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2})$')
96+
97+
def convert(self, value, param, ctx) -> str:
98+
value = click.STRING.convert(value, param, ctx)
99+
if not self.pattern.match(value):
100+
self.fail(f'{value} is not a valid mac address', param, ctx)
101+
return value
102+
103+
def get_metavar(self, param):
104+
return '01:23:45:67:89:AB'
105+
106+
def __repr__(self) -> str:
107+
return "MacAddress"
108+
109+
110+
class IPv4AddressParamType(click.ParamType):
111+
name = 'ipv4_address'
112+
113+
def convert(self, value, param, ctx) -> str:
114+
value = click.STRING.convert(value, param, ctx)
115+
try:
116+
return str(ipaddress.IPv4Address(value))
117+
except ValueError:
118+
self.fail(f'{value} is not a valid IPv4 Address', param, ctx)
119+
120+
def get_metavar(self, param):
121+
return '192.168.0.1'
122+
123+
def __repr__(self) -> str:
124+
return "Ipv4Address"
125+
126+
class CustomParamType:
127+
KEY = KeyParamType()
128+
MAC_ADDR = MacAddressParamType()
129+
IPV4_ADDR = IPv4AddressParamType()
73130

74131

75132
@click.group()
@@ -82,19 +139,17 @@ def cli():
82139
@cli.command(name='encrypt')
83140
@click.argument('pathname', type=click.Path(exists=True, resolve_path=True))
84141
@click.option('-i', '--in-place', 'replace', default=False, help='make changes to files in place', is_flag=True)
85-
@click.option('-k', '--key', default=None, help=KEY_OPTION_HELP, type=click.STRING)
142+
@click.option('-k', '--key', default=None, help=KEY_OPTION_HELP, type=CustomParamType.KEY)
86143
@click.option('--with-license', default=False, help='Add license to encrypted file', is_flag=True)
87-
@click.option('-m', '--bind-mac', 'mac', default=None, help='Bind mac address to encrypted file', type=click.STRING)
88-
@click.option('-4', '--bind-ipv4', 'ipv4', default=None, help='Bind ipv4 address to encrypted file', type=click.STRING)
144+
@click.option('-m', '--bind-mac', 'mac', default=None, help='Bind mac address to encrypted file', type=CustomParamType.MAC_ADDR)
145+
@click.option('-4', '--bind-ipv4', 'ipv4', default=None, help='Bind ipv4 address to encrypted file', type=CustomParamType.IPV4_ADDR)
89146
@click.option('-b', '--before', default=MAX_DATETIME, help='License is invalid before this date.', type=click.DateTime(formats=DATETIME_FORMATS))
90147
@click.option('-a', '--after', default=MIN_DATETIME, help='License is invalid after this date.', type=click.DateTime(formats=DATETIME_FORMATS))
91148
@click.confirmation_option('-y', '--yes', prompt='Are you sure you want to encrypt your python file?', help='Automatically answer yes for confirm questions.')
92149
@click.help_option('-h', '--help')
93150
@click.pass_context
94151
def encrypt_command(ctx, pathname, replace, key, with_license, mac, ipv4, before, after):
95152
"""Encrypt your python code"""
96-
if key is not None and not _check_key(key):
97-
ctx.fail(INVALID_KEY_MSG)
98153
if key is None:
99154
key = generate_aes_key().decode()
100155
click.echo(f'Your randomly encryption 🔑 is {click.style(key,underline=True, fg="yellow")}')
@@ -130,21 +185,19 @@ def encrypt_command(ctx, pathname, replace, key, with_license, mac, ipv4, before
130185
generate_so_file(cipher_key, d, n)
131186
if with_license is True:
132187
generate_license_file(key, Path(os.getcwd()), after, before, mac, ipv4)
133-
ctx.echo(FINISH_GENERATE_LICENSE_MSG)
188+
click.echo(FINISH_GENERATE_LICENSE_MSG)
134189
click.echo(FINISH_ENCRYPT_MSG)
135190

136191

137192
@cli.command(name='decrypt')
138193
@click.argument('pathname', type=click.Path(exists=True, resolve_path=True))
139194
@click.option('-i', '--in-place', 'replace', default=False, help='make changes to files in place', is_flag=True)
140-
@click.option('-k', '--key', required=True, help='Your encryption key.', type=click.STRING)
195+
@click.option('-k', '--key', required=True, help='Your encryption key.', type=CustomParamType.KEY)
141196
@click.help_option('-h', '--help')
142197
@click.pass_context
143198
def decrypt_command(ctx, pathname, replace, key):
144199
"""Decrypt encrypted pye file"""
145200
path = Path(pathname)
146-
if not _check_key(key):
147-
ctx.fail(INVALID_KEY_MSG)
148201

149202
if path.is_file():
150203
if replace:
@@ -173,30 +226,26 @@ def decrypt_command(ctx, pathname, replace, key):
173226

174227

175228
@cli.command(name='generate')
176-
@click.option('-k', '--key', required=True, help='Your encryption key.', type=click.STRING)
229+
@click.option('-k', '--key', required=True, help='Your encryption key.', type=CustomParamType.KEY)
177230
@click.help_option('-h', '--help')
178231
@click.pass_context
179232
def generate_loader(ctx, key):
180233
"""Generate loader file using specified key"""
181-
if not _check_key(key):
182-
ctx.fail(INVALID_KEY_MSG)
183234
cipher_key, d, n = encrypt_key(key.encode())
184235
generate_so_file(cipher_key, d, n, Path(os.getcwd()))
185236
click.echo(FINISH_GENERATE_LOADER_MSG)
186237

187238

188239
@cli.command(name='license')
189240
@click.help_option('-h', '--help')
190-
@click.option('-k', '--key', required=True, help='Your encryption key.', type=click.STRING)
191-
@click.option('-m', '--bind-mac', help='Your mac address.', type=click.STRING)
192-
@click.option('-4', '--bind-ipv4', help='Your ipv4 address.', type=click.STRING)
241+
@click.option('-k', '--key', required=True, help='Your encryption key.', type=CustomParamType.KEY)
242+
@click.option('-m', '--bind-mac', help='Your mac address.', type=CustomParamType.MAC_ADDR)
243+
@click.option('-4', '--bind-ipv4', help='Your ipv4 address.', type=CustomParamType.IPV4_ADDR)
193244
@click.option('-b', '--before', default=MAX_DATETIME, help='License is invalid before this date.', type=click.DateTime(formats=DATETIME_FORMATS))
194245
@click.option('-a', '--after', default=MIN_DATETIME, help='License is invalid after this date.', type=click.DateTime(formats=DATETIME_FORMATS))
195246
@click.pass_context
196247
def generate_license(ctx, key, mac, ipv4, before, after):
197248
"""Generate license file using specified key"""
198-
if not _check_key(key):
199-
ctx.fail(INVALID_KEY_MSG)
200249
if before > after:
201250
ctx.fail(INVALID_EXPIRED_MSG)
202251

0 commit comments

Comments
 (0)