a lot of progress
This commit is contained in:
parent
221d7170ce
commit
56bd92d1f1
@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from conn.core import node
|
from .core import node
|
||||||
from conn.configfile import configfile
|
from .configfile import configfile
|
||||||
from conn.connapp import connapp
|
from .connapp import connapp
|
||||||
|
import __main__
|
||||||
|
|
||||||
__version__ = "2.0"
|
__version__ = "2.0"
|
||||||
__all__ = [node, configfile, connapp]
|
__all__ = [node, configfile, connapp]
|
||||||
|
|
||||||
|
10
conn/__main__.py
Normal file
10
conn/__main__.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import conn
|
||||||
|
|
||||||
|
def main():
|
||||||
|
conf = conn.configfile()
|
||||||
|
conn.connapp(conf, conn.node)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
@ -67,12 +67,19 @@ class configfile:
|
|||||||
|
|
||||||
def _explode_unique(self, unique):
|
def _explode_unique(self, unique):
|
||||||
uniques = unique.split("@")
|
uniques = unique.split("@")
|
||||||
|
if not unique.startswith("@"):
|
||||||
result = {"id": uniques[0]}
|
result = {"id": uniques[0]}
|
||||||
|
else:
|
||||||
|
result = {}
|
||||||
if len(uniques) == 2:
|
if len(uniques) == 2:
|
||||||
result["folder"] = uniques[1]
|
result["folder"] = uniques[1]
|
||||||
|
if result["folder"] == "":
|
||||||
|
return False
|
||||||
elif len(uniques) == 3:
|
elif len(uniques) == 3:
|
||||||
result["folder"] = uniques[2]
|
result["folder"] = uniques[2]
|
||||||
result["subfolder"] = uniques[1]
|
result["subfolder"] = uniques[1]
|
||||||
|
if result["folder"] == "" or result["subfolder"] == "":
|
||||||
|
return False
|
||||||
elif len(uniques) > 3:
|
elif len(uniques) > 3:
|
||||||
return False
|
return False
|
||||||
return result
|
return result
|
||||||
|
393
conn/connapp.py
393
conn/connapp.py
@ -6,6 +6,9 @@ from Crypto.PublicKey import RSA
|
|||||||
from Crypto.Cipher import PKCS1_OAEP
|
from Crypto.Cipher import PKCS1_OAEP
|
||||||
import ast
|
import ast
|
||||||
import argparse
|
import argparse
|
||||||
|
import sys
|
||||||
|
import inquirer
|
||||||
|
import yaml
|
||||||
|
|
||||||
#functions and classes
|
#functions and classes
|
||||||
|
|
||||||
@ -15,49 +18,349 @@ class connapp:
|
|||||||
self.node = node
|
self.node = node
|
||||||
self.config = config
|
self.config = config
|
||||||
self.nodes = self._getallnodes()
|
self.nodes = self._getallnodes()
|
||||||
parser = argparse.ArgumentParser(prog = "conn", description = "SSH and Telnet connection manager", formatter_class=argparse.RawTextHelpFormatter)
|
self.folders = self._getallfolders()
|
||||||
crud = parser.add_mutually_exclusive_group()
|
self.profiles = list(self.config.profiles.keys())
|
||||||
parser.add_argument("node", metavar="node|folder", nargs='?', default=None, action=self.store_type, type=self._type, help="node[@subfolder][@folder]\nRecursively search in folders and subfolders if not specified\n[@subfolder][@folder]\nShow all available connections globaly or in specified path")
|
#DEFAULTPARSER
|
||||||
crud.add_argument("--add", dest="action", action='append_const', help="Add new node[@subfolder][@folder]", const="add")
|
defaultparser = argparse.ArgumentParser(prog = "conn", description = "SSH and Telnet connection manager", formatter_class=argparse.RawTextHelpFormatter)
|
||||||
crud.add_argument("--del", "--rm", dest="action", action='append_const', help="Delete node[@subfolder][@folder]", const="del")
|
subparsers = defaultparser.add_subparsers(title="Commands")
|
||||||
crud.add_argument("--mod", "--edit", dest="action", action='append_const', help="Modify node[@subfolder][@folder]", const="mod")
|
#NODEPARSER
|
||||||
crud.add_argument("--show", dest="action", action='append_const', help="Show node[@subfolder][@folder]", const="show")
|
nodeparser = subparsers.add_parser("node", help=self._help("node"),formatter_class=argparse.RawTextHelpFormatter)
|
||||||
crud.add_argument("--mv", dest="action", nargs=2, action=self.store_type, help="Move node[@subfolder][@folder] dest_node[@subfolder][@folder]", default="mv", type=self._type)
|
nodecrud = nodeparser.add_mutually_exclusive_group()
|
||||||
crud.add_argument("--cp", dest="action", nargs=2, action=self.store_type, help="Copy node[@subfolder][@folder] new_node[@subfolder][@folder]", default="cp", type=self._type)
|
nodeparser.add_argument("node", metavar="node|folder", nargs='?', default=None, action=self.store_type, type=self._type_node, help=self._help("node"))
|
||||||
crud.add_argument("--ls", dest="action", action='store', choices=["nodes","folders"], help="List nodes or folders", default=False)
|
nodecrud.add_argument("--add", dest="action", action="store_const", help="Add new node[@subfolder][@folder]", const="add", default="connect")
|
||||||
crud.add_argument("--bulk", const="bulk", dest="action", action='append_const', help="Add nodes in bulk")
|
nodecrud.add_argument("--del", "--rm", dest="action", action="store_const", help="Delete node[@subfolder][@folder]", const="del", default="connect")
|
||||||
self.parser = parser.parse_args()
|
nodecrud.add_argument("--mod", "--edit", dest="action", action="store_const", help="Modify node[@subfolder][@folder]", const="mod", default="connect")
|
||||||
print(vars(self.parser))
|
nodecrud.add_argument("--show", dest="action", action="store_const", help="Show node[@subfolder][@folder]", const="show", default="connect")
|
||||||
# if self.parser.node == False:
|
nodeparser.set_defaults(func=self._func_node)
|
||||||
# print("todos")
|
#PROFILEPARSER
|
||||||
# else:
|
profileparser = subparsers.add_parser("profile", help="Manage profiles")
|
||||||
# print(self.parser.node)
|
profileparser.add_argument("profile", nargs='?', action=self.store_type, type=self._type_profile, help="Name of profile to manage")
|
||||||
|
profilecrud = profileparser.add_mutually_exclusive_group(required=True)
|
||||||
|
profilecrud.add_argument("--add", dest="action", action="store_const", help="Add new profile", const="add", default="connect")
|
||||||
|
profilecrud.add_argument("--del", "--rm", dest="action", action="store_const", help="Delete profile", const="del", default="connect")
|
||||||
|
profilecrud.add_argument("--mod", "--edit", dest="action", action="store_const", help="Modify profile", const="mod", default="connect")
|
||||||
|
profilecrud.add_argument("--show", dest="action", action="store_const", help="Show profile", const="show", default="connect")
|
||||||
|
profileparser.set_defaults(func=self._func_profile)
|
||||||
|
#MOVEPARSER
|
||||||
|
moveparser = subparsers.add_parser("move", aliases=["mv"], help="Move node")
|
||||||
|
moveparser.add_argument("move", nargs=2, action=self.store_type, help="Move node[@subfolder][@folder] dest_node[@subfolder][@folder]", default="move", type=self._type_node)
|
||||||
|
moveparser.set_defaults(func=self._func_others)
|
||||||
|
#COPYPARSER
|
||||||
|
copyparser = subparsers.add_parser("copy", aliases=["cp"], help="Copy node")
|
||||||
|
copyparser.add_argument("cp", nargs=2, action=self.store_type, help="Copy node[@subfolder][@folder] new_node[@subfolder][@folder]", default="cp", type=self._type_node)
|
||||||
|
copyparser.set_defaults(func=self._func_others)
|
||||||
|
#LISTPARSER
|
||||||
|
lsparser = subparsers.add_parser("list", aliases=["ls"], help="List profiles, nodes or folders")
|
||||||
|
lsparser.add_argument("ls", action=self.store_type, choices=["profiles","nodes","folders"], help="List profiles, nodes or folders", default=False)
|
||||||
|
lsparser.set_defaults(func=self._func_others)
|
||||||
|
#BULKPARSER
|
||||||
|
bulkparser = subparsers.add_parser("bulk", help="Add nodes in bulk")
|
||||||
|
bulkparser.add_argument("bulk", const="bulk", nargs=0, action=self.store_type, help="Add nodes in bulk")
|
||||||
|
bulkparser.set_defaults(func=self._func_others)
|
||||||
|
#Set default subparser and tune arguments
|
||||||
|
commands = ["node", "-h", "--help", "profile", "mv", "move","copy", "cp", "bulk", "ls", "list"]
|
||||||
|
profilecmds = ["--add", "--del", "--rm", "--mod", "--edit", "--show"]
|
||||||
|
if len(sys.argv) >= 3 and sys.argv[2] == "profile" and sys.argv[1] in profilecmds:
|
||||||
|
sys.argv[2] = sys.argv[1]
|
||||||
|
sys.argv[1] = "profile"
|
||||||
|
if len(sys.argv) < 2 or sys.argv[1] not in commands:
|
||||||
|
sys.argv.insert(1,"node")
|
||||||
|
args = defaultparser.parse_args()
|
||||||
|
args.func(args)
|
||||||
|
|
||||||
def _node_type(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$@#-]+$")):
|
def _func_node(self, args):
|
||||||
if not pat.match(arg_value):
|
if args.action == "connect":
|
||||||
raise argparse.ArgumentTypeError
|
if args.data == None:
|
||||||
uniques = self.config._explode_unique(arg_value)
|
matches = self.nodes
|
||||||
if uniques == False:
|
|
||||||
raise argparse.ArgumentTypeError
|
|
||||||
return uniques
|
|
||||||
|
|
||||||
def _type(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$@#-]+$"), arg = "node"):
|
|
||||||
if not pat.match(arg_value):
|
|
||||||
raise argparse.ArgumentTypeError
|
|
||||||
uniques = self.config._explode_unique(arg_value)
|
|
||||||
if uniques == False:
|
|
||||||
raise argparse.ArgumentTypeError
|
|
||||||
if arg == "node":
|
|
||||||
return uniques
|
|
||||||
else:
|
else:
|
||||||
return arg
|
if args.data.startswith("@"):
|
||||||
|
matches = list(filter(lambda k: args.data in k, self.nodes))
|
||||||
|
else:
|
||||||
|
matches = list(filter(lambda k: k.startswith(args.data), self.nodes))
|
||||||
|
if len(matches) == 0:
|
||||||
|
print("ERROR NO MACHEA NI FOLDER NI NODE")
|
||||||
|
return
|
||||||
|
elif len(matches) > 1:
|
||||||
|
matches[0] = self._choose(matches,"node", "connect")
|
||||||
|
if matches[0] == None:
|
||||||
|
return
|
||||||
|
node = self._get_item(matches[0])
|
||||||
|
node = self.node(matches[0],**node, config = self.config)
|
||||||
|
node.interact()
|
||||||
|
elif args.action == "del":
|
||||||
|
if args.data == None:
|
||||||
|
print("MISSING ARGUMENT NODE")
|
||||||
|
return
|
||||||
|
elif args.data.startswith("@"):
|
||||||
|
matches = list(filter(lambda k: k == args.data, self.folders))
|
||||||
|
else:
|
||||||
|
matches = list(filter(lambda k: k == args.data, self.nodes))
|
||||||
|
if len(matches) == 0:
|
||||||
|
print("ERROR NO MACHEO NI FOLDER NI NODE")
|
||||||
|
return
|
||||||
|
question = [inquirer.Confirm("delete", message="Are you sure you want to delete {}?".format(matches[0]))]
|
||||||
|
confirm = inquirer.prompt(question)
|
||||||
|
if confirm["delete"]:
|
||||||
|
uniques = self.config._explode_unique(matches[0])
|
||||||
|
if args.data.startswith("@"):
|
||||||
|
self.config._folder_del(**uniques)
|
||||||
|
else:
|
||||||
|
self.config._connections_del(**uniques)
|
||||||
|
self.config.saveconfig(self.config.file)
|
||||||
|
print("{} deleted succesfully".format(matches[0]))
|
||||||
|
elif args.action == "add":
|
||||||
|
if args.data == None:
|
||||||
|
print("MISSING ARGUMENT NODE")
|
||||||
|
return
|
||||||
|
elif args.data.startswith("@"):
|
||||||
|
type = "folder"
|
||||||
|
matches = list(filter(lambda k: k == args.data, self.folders))
|
||||||
|
else:
|
||||||
|
type = "node"
|
||||||
|
matches = list(filter(lambda k: k == args.data, self.nodes))
|
||||||
|
if len(matches) > 0:
|
||||||
|
print(matches[0] + " ALLREADY EXIST")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
if type == "folder":
|
||||||
|
uniques = self.config._explode_unique(args.data)
|
||||||
|
if uniques == False:
|
||||||
|
print("Invalid folder {}".format(args.data))
|
||||||
|
return
|
||||||
|
if "subfolder" in uniques.keys():
|
||||||
|
parent = "@" + uniques["folder"]
|
||||||
|
if parent not in self.folders:
|
||||||
|
print("FOLDER {} DONT EXIST".format(uniques["folder"]))
|
||||||
|
return
|
||||||
|
self.config._folder_add(**uniques)
|
||||||
|
self.config.saveconfig(self.config.file)
|
||||||
|
print("{} added succesfully".format(args.data))
|
||||||
|
|
||||||
|
if type == "node":
|
||||||
|
nodefolder = args.data.partition("@")
|
||||||
|
nodefolder = "@" + nodefolder[2]
|
||||||
|
if nodefolder not in self.folders and nodefolder != "@":
|
||||||
|
print(nodefolder + " DONT EXIST")
|
||||||
|
return
|
||||||
|
uniques = self.config._explode_unique(args.data)
|
||||||
|
if uniques == False:
|
||||||
|
print("Invalid node {}".format(args.data))
|
||||||
|
return False
|
||||||
|
print("You can use the configured setting in a profile using @profilename.")
|
||||||
|
print("You can also leave empty any value except hostname/IP.")
|
||||||
|
print("You can pass 1 or more passwords using comma separated @profiles")
|
||||||
|
print("You can use this variables on logging file name: ${id} ${unique} ${host} ${port} ${user} ${protocol}")
|
||||||
|
newnode = self._questions_nodes(args.data, args.action, uniques)
|
||||||
|
if newnode == False:
|
||||||
|
return
|
||||||
|
self.config._connections_add(**newnode)
|
||||||
|
self.config.saveconfig(self.config.file)
|
||||||
|
print("{} added succesfully".format(args.data))
|
||||||
|
elif args.action == "show":
|
||||||
|
if args.data == None:
|
||||||
|
print("MISSING ARGUMENT NODE")
|
||||||
|
return
|
||||||
|
matches = list(filter(lambda k: k == args.data, self.nodes))
|
||||||
|
if len(matches) == 0:
|
||||||
|
print("ERROR NO MACHEO NODE")
|
||||||
|
return
|
||||||
|
node = self._get_item(matches[0])
|
||||||
|
print(yaml.dump(node, Dumper=yaml.CDumper))
|
||||||
|
elif args.action == "mod":
|
||||||
|
if args.data == None:
|
||||||
|
print("MISSING ARGUMENT NODE")
|
||||||
|
return
|
||||||
|
matches = list(filter(lambda k: k == args.data, self.nodes))
|
||||||
|
if len(matches) == 0:
|
||||||
|
print("ERROR NO MACHEO NODE")
|
||||||
|
return
|
||||||
|
node = self._get_item(matches[0])
|
||||||
|
edits = self._questions_edit()
|
||||||
|
if edits == None:
|
||||||
|
return
|
||||||
|
uniques = self.config._explode_unique(args.data)
|
||||||
|
updatenode = self._questions_nodes(args.data, args.action, uniques, edit=edits)
|
||||||
|
if not updatenode:
|
||||||
|
return
|
||||||
|
node.pop("type")
|
||||||
|
uniques.update(node)
|
||||||
|
if sorted(updatenode.items()) == sorted(uniques.items()):
|
||||||
|
print("Nothing to do here")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.config._connections_add(**updatenode)
|
||||||
|
self.config.saveconfig(self.config.file)
|
||||||
|
print("{} edited succesfully".format(args.data))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(matches)
|
||||||
|
|
||||||
|
def _func_profile(self, args):
|
||||||
|
print(args.command)
|
||||||
|
print(vars(args))
|
||||||
|
|
||||||
|
def _func_others(self, args):
|
||||||
|
print(args.command)
|
||||||
|
print(vars(args))
|
||||||
|
|
||||||
|
def _choose(self, list, name, action):
|
||||||
|
questions = [inquirer.List(name, message="Pick {} to {}:".format(name,action), choices=list)]
|
||||||
|
answer = inquirer.prompt(questions)
|
||||||
|
if answer == None:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return answer[name]
|
||||||
|
|
||||||
|
def _host_validation(self, answers, current, regex = "^.+$"):
|
||||||
|
if not re.match(regex, current):
|
||||||
|
raise inquirer.errors.ValidationError("", reason="Host cannot be empty")
|
||||||
|
if current.startswith("@"):
|
||||||
|
if current[1:] not in self.profiles:
|
||||||
|
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _protocol_validation(self, answers, current, regex = "(^ssh$|^telnet$|^$|^@.+$)"):
|
||||||
|
if not re.match(regex, current):
|
||||||
|
raise inquirer.errors.ValidationError("", reason="Pick between ssh, telnet, leave empty or @profile")
|
||||||
|
if current.startswith("@"):
|
||||||
|
if current[1:] not in self.profiles:
|
||||||
|
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _port_validation(self, answers, current, regex = "(^[0-9]*$|^@.+$)"):
|
||||||
|
if not re.match(regex, current):
|
||||||
|
raise inquirer.errors.ValidationError("", reason="Pick a port between 1-65535, @profile o leave empty")
|
||||||
|
try:
|
||||||
|
port = int(current)
|
||||||
|
except:
|
||||||
|
port = 0
|
||||||
|
if current.startswith("@"):
|
||||||
|
if current[1:] not in self.profiles:
|
||||||
|
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||||
|
elif current != "" and not 1 <= int(port) <= 65535:
|
||||||
|
raise inquirer.errors.ValidationError("", reason="Pick a port between 1-65535, @profile o leave empty")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _pass_validation(self, answers, current, regex = "(^@.+$)"):
|
||||||
|
profiles = current.split(",")
|
||||||
|
for i in profiles:
|
||||||
|
if not re.match(regex, i) or i[1:] not in self.profiles:
|
||||||
|
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(i))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _default_validation(self, answers, current):
|
||||||
|
if current.startswith("@"):
|
||||||
|
if current[1:] not in self.profiles:
|
||||||
|
raise inquirer.errors.ValidationError("", reason="Profile {} don't exist".format(current))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _questions_edit(self):
|
||||||
|
questions = []
|
||||||
|
questions.append(inquirer.Confirm("host", message="Edit Hostname/IP?"))
|
||||||
|
questions.append(inquirer.Confirm("protocol", message="Edit Protocol?"))
|
||||||
|
questions.append(inquirer.Confirm("port", message="Edit Port?"))
|
||||||
|
questions.append(inquirer.Confirm("options", message="Edit Options?"))
|
||||||
|
questions.append(inquirer.Confirm("logs", message="Edit logging path/file?"))
|
||||||
|
questions.append(inquirer.Confirm("user", message="Edit User?"))
|
||||||
|
questions.append(inquirer.Confirm("password", message="Edit password?"))
|
||||||
|
answers = inquirer.prompt(questions)
|
||||||
|
return answers
|
||||||
|
|
||||||
|
def _questions_nodes(self, unique, action,uniques = None, edit = None):
|
||||||
|
try:
|
||||||
|
defaults = self._get_item(unique)
|
||||||
|
except:
|
||||||
|
defaults = { "host":"", "protocol":"", "port":"", "user":"", "options":"", "logs":"" }
|
||||||
|
node = {}
|
||||||
|
|
||||||
|
if edit == None:
|
||||||
|
edit = { "host":True, "protocol":True, "port":True, "user":True, "password": True,"options":True, "logs":True }
|
||||||
|
questions = []
|
||||||
|
if edit["host"]:
|
||||||
|
questions.append(inquirer.Text("host", message="Add Hostname or IP", validate=self._host_validation, default=defaults["host"]))
|
||||||
|
else:
|
||||||
|
node["host"] = defaults["host"]
|
||||||
|
if edit["protocol"]:
|
||||||
|
questions.append(inquirer.Text("protocol", message="Select Protocol", validate=self._protocol_validation, default=defaults["protocol"]))
|
||||||
|
else:
|
||||||
|
node["protocol"] = defaults["protocol"]
|
||||||
|
if edit["port"]:
|
||||||
|
questions.append(inquirer.Text("port", message="Select Port Number", validate=self._port_validation, default=defaults["port"]))
|
||||||
|
else:
|
||||||
|
node["port"] = defaults["port"]
|
||||||
|
if edit["options"]:
|
||||||
|
questions.append(inquirer.Text("options", message="Pass extra options to protocol", validate=self._default_validation, default=defaults["options"]))
|
||||||
|
else:
|
||||||
|
node["options"] = defaults["options"]
|
||||||
|
if edit["logs"]:
|
||||||
|
questions.append(inquirer.Text("logs", message="Pick logging path/file ", validate=self._default_validation, default=defaults["logs"]))
|
||||||
|
else:
|
||||||
|
node["logs"] = defaults["logs"]
|
||||||
|
if edit["user"]:
|
||||||
|
questions.append(inquirer.Text("user", message="Pick username", validate=self._default_validation, default=defaults["user"]))
|
||||||
|
else:
|
||||||
|
node["user"] = defaults["user"]
|
||||||
|
if edit["password"]:
|
||||||
|
questions.append(inquirer.List("password", message="Password: Use a local password, no password or a list of profiles to reference?", choices=["Local Password", "Profiles", "No Password"]))
|
||||||
|
else:
|
||||||
|
node["password"] = defaults["password"]
|
||||||
|
answer = inquirer.prompt(questions)
|
||||||
|
if answer == None:
|
||||||
|
return False
|
||||||
|
if "password" in answer.keys():
|
||||||
|
if answer["password"] == "Local Password":
|
||||||
|
passq = [inquirer.Password("password", message="Set Password")]
|
||||||
|
passa = inquirer.prompt(passq)
|
||||||
|
answer["password"] = self.encrypt(passa["password"])
|
||||||
|
elif answer["password"] == "Profiles":
|
||||||
|
passq = [(inquirer.Text("password", message="Set a @profile or a comma separated list of @profiles", validate=self._pass_validation))]
|
||||||
|
passa = inquirer.prompt(passq)
|
||||||
|
answer["password"] = passa["password"].split(",")
|
||||||
|
elif answer["password"] == "No Password":
|
||||||
|
answer["password"] = ""
|
||||||
|
result = {**uniques, **answer, **node}
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _get_item(self, unique):
|
||||||
|
uniques = self.config._explode_unique(unique)
|
||||||
|
if unique.startswith("@"):
|
||||||
|
if uniques.keys() >= {"folder", "subfolder"}:
|
||||||
|
folder = self.config.connections[uniques["folder"]][uniques["subfolder"]]
|
||||||
|
else:
|
||||||
|
folder = self.config.connections[uniques["folder"]]
|
||||||
|
return folder
|
||||||
|
else:
|
||||||
|
if uniques.keys() >= {"folder", "subfolder"}:
|
||||||
|
node = self.config.connections[uniques["folder"]][uniques["subfolder"]][uniques["id"]]
|
||||||
|
elif "folder" in uniques.keys():
|
||||||
|
node = self.config.connections[uniques["folder"]][uniques["id"]]
|
||||||
|
else:
|
||||||
|
node = self.config.connections[uniques["id"]]
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _type_node(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$@#-]+$")):
|
||||||
|
if not pat.match(arg_value):
|
||||||
|
raise argparse.ArgumentTypeError
|
||||||
|
return arg_value
|
||||||
|
|
||||||
|
def _type_profile(self, arg_value, pat=re.compile(r"^[0-9a-zA-Z_.$#-]+$")):
|
||||||
|
if not pat.match(arg_value):
|
||||||
|
raise argparse.ArgumentTypeError
|
||||||
|
return arg_value
|
||||||
|
|
||||||
class store_type(argparse.Action):
|
class store_type(argparse.Action):
|
||||||
def __call__(self, parser, args, values, option_string=None):
|
def __call__(self, parser, args, values, option_string=None):
|
||||||
result = [self.default]
|
setattr(args, "data", values)
|
||||||
if values is not None:
|
delattr(args,self.dest)
|
||||||
result.extend(values)
|
setattr(args, "command", self.dest)
|
||||||
setattr(args, self.dest, result)
|
|
||||||
|
def _help(self, type):
|
||||||
|
if type == "node":
|
||||||
|
return "node[@subfolder][@folder]\nConnect to specific node or show all matching nodes\n[@subfolder][@folder]\nShow all available connections globaly or in specified path"
|
||||||
|
|
||||||
def _getallnodes(self):
|
def _getallnodes(self):
|
||||||
nodes = []
|
nodes = []
|
||||||
@ -73,10 +376,18 @@ class connapp:
|
|||||||
nodes.extend(layer3)
|
nodes.extend(layer3)
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
def encrypt(password, keyfile=None):
|
def _getallfolders(self):
|
||||||
|
folders = ["@" + k for k,v in self.config.connections.items() if isinstance(v, dict) and v["type"] == "folder"]
|
||||||
|
subfolders = []
|
||||||
|
for f in folders:
|
||||||
|
s = ["@" + k + f for k,v in self.config.connections[f[1:]].items() if isinstance(v, dict) and v["type"] == "subfolder"]
|
||||||
|
subfolders.extend(s)
|
||||||
|
folders.extend(subfolders)
|
||||||
|
return folders
|
||||||
|
|
||||||
|
def encrypt(self, password, keyfile=None):
|
||||||
if keyfile is None:
|
if keyfile is None:
|
||||||
home = os.path.expanduser("~")
|
keyfile = self.config.key
|
||||||
keyfile = home + '/.config/conn/.osk'
|
|
||||||
key = RSA.import_key(open(keyfile).read())
|
key = RSA.import_key(open(keyfile).read())
|
||||||
publickey = key.publickey()
|
publickey = key.publickey()
|
||||||
encryptor = PKCS1_OAEP.new(publickey)
|
encryptor = PKCS1_OAEP.new(publickey)
|
||||||
|
21
conn/core.py
21
conn/core.py
@ -104,17 +104,18 @@ class node:
|
|||||||
else:
|
else:
|
||||||
return t
|
return t
|
||||||
|
|
||||||
def interact(self, missingtext = False):
|
def interact(self, debug = False):
|
||||||
connect = self._connect()
|
connect = self._connect(debug = debug)
|
||||||
if connect == True:
|
if connect == True:
|
||||||
print("Connected to " + self.unique + " at " + self.host + (":" if self.port != '' else '') + self.port + " via: " + self.protocol)
|
print("Connected to " + self.unique + " at " + self.host + (":" if self.port != '' else '') + self.port + " via: " + self.protocol)
|
||||||
if 'logfile' in dir(self):
|
if debug:
|
||||||
|
self.child.logfile_read = None
|
||||||
|
elif 'logfile' in dir(self):
|
||||||
self.child.logfile_read = open(self.logfile, "wb")
|
self.child.logfile_read = open(self.logfile, "wb")
|
||||||
# self.child.logfile = sys.stdout.buffer
|
|
||||||
if 'missingtext' in dir(self):
|
if 'missingtext' in dir(self):
|
||||||
print(self.child.after.decode(), end='')
|
print(self.child.after.decode(), end='')
|
||||||
self.child.interact()
|
self.child.interact()
|
||||||
if "logfile" in dir(self):
|
if "logfile" in dir(self) and not debug:
|
||||||
self._logclean(self.logfile)
|
self._logclean(self.logfile)
|
||||||
|
|
||||||
def run(self, commands,*, folder = '', prompt = '>$|#$|\$.$', stdout = False):
|
def run(self, commands,*, folder = '', prompt = '>$|#$|\$.$', stdout = False):
|
||||||
@ -147,7 +148,7 @@ class node:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _connect(self):
|
def _connect(self, debug = False):
|
||||||
if self.protocol == "ssh":
|
if self.protocol == "ssh":
|
||||||
cmd = "ssh"
|
cmd = "ssh"
|
||||||
if self.idletime > 0:
|
if self.idletime > 0:
|
||||||
@ -166,7 +167,7 @@ class node:
|
|||||||
passwords = self.__passtx(self.password)
|
passwords = self.__passtx(self.password)
|
||||||
else:
|
else:
|
||||||
passwords = []
|
passwords = []
|
||||||
expects = ['yes/no', 'refused', 'supported', 'cipher', 'sage', 'timeout', 'unavailable', 'closed', '[p|P]assword:|[u|U]sername:', '>$|#$|\$.$', 'suspend', pexpect.EOF]
|
expects = ['yes/no', 'refused', 'supported', 'cipher', 'sage', 'timeout', 'unavailable', 'closed', '[p|P]assword:|[u|U]sername:', '>$|#$|\$.$', 'suspend', pexpect.EOF, "No route to host"]
|
||||||
elif self.protocol == "telnet":
|
elif self.protocol == "telnet":
|
||||||
cmd = "telnet " + self.host
|
cmd = "telnet " + self.host
|
||||||
if self.port != '':
|
if self.port != '':
|
||||||
@ -179,11 +180,13 @@ class node:
|
|||||||
passwords = self.__passtx(self.password)
|
passwords = self.__passtx(self.password)
|
||||||
else:
|
else:
|
||||||
passwords = []
|
passwords = []
|
||||||
expects = ['[u|U]sername:', 'refused', 'supported', 'cipher', 'sage', 'timeout', 'unavailable', 'closed', '[p|P]assword:', '>$|#$|\$.$', 'suspend', pexpect.EOF]
|
expects = ['[u|U]sername:', 'refused', 'supported', 'cipher', 'sage', 'timeout', 'unavailable', 'closed', '[p|P]assword:', '>$|#$|\$.$', 'suspend', pexpect.EOF, "No route to host"]
|
||||||
else:
|
else:
|
||||||
print("Invalid protocol: " + self.protocol)
|
print("Invalid protocol: " + self.protocol)
|
||||||
return
|
return
|
||||||
child = pexpect.spawn(cmd)
|
child = pexpect.spawn(cmd)
|
||||||
|
if debug:
|
||||||
|
child.logfile_read = sys.stdout.buffer
|
||||||
if len(passwords) > 0:
|
if len(passwords) > 0:
|
||||||
loops = len(passwords)
|
loops = len(passwords)
|
||||||
else:
|
else:
|
||||||
@ -202,7 +205,7 @@ class node:
|
|||||||
else:
|
else:
|
||||||
self.missingtext = True
|
self.missingtext = True
|
||||||
break
|
break
|
||||||
case 1 | 2 | 3 | 4 | 5 | 6 |7:
|
case 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12:
|
||||||
print("Connection failed code:" + str(results))
|
print("Connection failed code:" + str(results))
|
||||||
child.close()
|
child.close()
|
||||||
return
|
return
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
inquirer~=2.9.1
|
||||||
pexpect~=4.8.0
|
pexpect~=4.8.0
|
||||||
pycryptodome~=3.14.1
|
pycryptodome~=3.14.1
|
||||||
PyYAML~=6.0
|
PyYAML~=6.0
|
Loading…
Reference in New Issue
Block a user