Merge branch 'sec/admin-creation' into 'dev'

Force random password on first run. Stop using Crafty as default password

See merge request crafty-controller/crafty-4!672
This commit is contained in:
Iain Powrie 2023-12-07 16:49:46 +00:00
commit bf13d4285b
5 changed files with 78 additions and 17 deletions

View File

@ -4,7 +4,8 @@
- Loading Screen for Crafty during startup ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668))
### Refactor
- Remove deprecated API V1 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/670))
- Tidy up main.py to be more comprehensive ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668))
- Tidy up main.py to be more comprehensive ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/668))
- Force random password on first run. Stop using common default password ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/672) | [Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/673))
### Bug fixes
- Remove webhook `custom` option from webook provider list as it's not currently an option ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/664))
- Bump cryptography for CVE-2023-49083 ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/680))

View File

@ -362,6 +362,42 @@ class Helpers:
return result_of_check == 0
def create_pass(self):
# Maximum length of password needed
max_len = 64
# Declare string of the character that we need in our password
digits = string.digits
locase = string.ascii_lowercase
upcase = string.ascii_uppercase
symbols = "!@#$%^&*" # Reducing to avoid issues with ([]{}<>,'`) etc
# Combine all the character strings above to form one string
combo = digits + upcase + locase + symbols
# Randomly select at least one character from each character set above
rand_digit = secrets.choice(digits)
rand_upper = secrets.choice(upcase)
rand_lower = secrets.choice(locase)
rand_symbol = secrets.choice(symbols)
# Combine the character randomly selected above
temp_pass = rand_digit + rand_upper + rand_lower + rand_symbol
# Fill the rest of the password length by selecting randomly char list
for _ in range(max_len - 4):
temp_pass += secrets.choice(combo)
# Shuffle the temporary password to prevent predictable patterns
temp_pass_list = list(temp_pass)
secrets.SystemRandom().shuffle(temp_pass_list)
# Form the password by concatenating the characters
password = "".join(temp_pass_list)
# Return completed password
return password
@staticmethod
def cmdparse(cmd_in):
# Parse a string into arguments

View File

@ -14,13 +14,17 @@ class DatabaseBuilder:
self.management_helper = management_helper
self.users_helper = users_helper
def default_settings(self):
def default_settings(self, password="crafty"):
logger.info("Fresh Install Detected - Creating Default Settings")
Console.info("Fresh Install Detected - Creating Default Settings")
default_data = self.helper.find_default_password()
if password not in default_data:
Console.help(
"No default password found. Using password created "
"by Crafty. Find it in app/config/default-creds.txt"
)
username = default_data.get("username", "admin")
password = default_data.get("password", "crafty")
password = default_data.get("password", password)
self.users_helper.add_user(
username=username,

View File

@ -105,7 +105,7 @@ class PublicHandler(BaseHandler):
next_page = "/login?" + self.request.query
entered_username = nh3.clean(self.get_argument("username"))
entered_password = nh3.clean(self.get_argument("password"))
entered_password = self.get_argument("password")
# pylint: disable=no-member
try:

44
main.py
View File

@ -51,6 +51,7 @@ if not (sys.version_info.major == 3 and sys.version_info.minor >= 9):
time.sleep(3)
Console.info("Crafty stopped. Exiting...")
sys.exit(0)
# pylint: disable=wrong-import-position
try:
from app.classes.models.base_model import database_proxy
@ -105,8 +106,10 @@ def controller_setup():
else:
helper.servers_dir = master_server_dir
Console.debug(f"Execution Mode: {RUNNING_MODE}")
Console.debug(f"Application path : '{APPLICATION_PATH}'")
logger.info(f"Execution Mode: {RUNNING_MODE}")
logger.info(f"Application path: '{APPLICATION_PATH}'")
Console.info(f"Execution Mode: {RUNNING_MODE}")
Console.info(f"Application path: '{APPLICATION_PATH}'")
controller.clear_support_status()
@ -260,12 +263,14 @@ def setup_logging(debug=True):
one optional (defaulted to True) parameter which
determines whether or not the logging level is "debug" or verbose.
"""
logging_config_file = os.path.join(os.path.curdir, "app", "config", "logging.json")
logging_config_file = os.path.join(
APPLICATION_PATH, "app", "config", "logging.json"
)
if not helper.check_file_exists(
os.path.join(os.path.curdir, "logs", "auth_tracker.log")
os.path.join(APPLICATION_PATH, "logs", "auth_tracker.log")
):
open(
os.path.join(os.path.curdir, "logs", "auth_tracker.log"),
os.path.join(APPLICATION_PATH, "logs", "auth_tracker.log"),
"a",
encoding="utf-8",
).close()
@ -336,12 +341,6 @@ if __name__ == "__main__":
user_helper = HelperUsers(database, helper)
management_helper = HelpersManagement(database, helper)
installer = DatabaseBuilder(database, helper, user_helper, management_helper)
file_helper = FileHelpers(helper)
import_helper = ImportHelpers(helper, file_helper)
controller = Controller(database, helper, file_helper, import_helper)
controller.set_project_root(APPLICATION_PATH)
tasks_manager = TasksManager(helper, controller, file_helper)
import3 = Import3(helper, controller)
FRESH_INSTALL = installer.is_fresh_install()
if FRESH_INSTALL:
@ -352,7 +351,19 @@ if __name__ == "__main__":
f"through your router/firewall if you would like to be able "
f"to access Crafty remotely."
)
installer.default_settings()
PASSWORD = helper.create_pass()
installer.default_settings(PASSWORD)
with open(
os.path.join(APPLICATION_PATH, "app", "config", "default-creds.txt"),
"w",
encoding="utf-8",
) as cred_file:
cred_file.write(
json.dumps({"username": "admin", "password": PASSWORD}, indent=4)
)
os.chmod(
os.path.join(APPLICATION_PATH, "app", "config", "default-creds.txt"), 0o600
)
else:
Console.debug("Existing install detected")
Console.info("Checking for reset secret flag")
@ -364,6 +375,15 @@ if __name__ == "__main__":
else:
Console.info("No flag found. Secrets are staying")
# now we've initialized our database for fresh install we
# can finishing initializing our controllers/modules
file_helper = FileHelpers(helper)
import_helper = ImportHelpers(helper, file_helper)
controller = Controller(database, helper, file_helper, import_helper)
controller.set_project_root(APPLICATION_PATH)
tasks_manager = TasksManager(helper, controller, file_helper)
import3 = Import3(helper, controller)
# Check to see if client config.json version is different than the
# Master config.json in helpers.py
Console.info("Checking for remote changes to config.json")