From e8885a144d9b6d0b14269e6c24b02b16b838bfa5 Mon Sep 17 00:00:00 2001 From: Mateus Melchiades Date: Mon, 6 Mar 2023 09:30:55 -0300 Subject: [PATCH] User creation --- recipe.json | 3 + vanilla_first_setup/defaults/applications.py | 12 +- vanilla_first_setup/defaults/meson.build | 1 + vanilla_first_setup/defaults/user.py | 146 ++++++++++++++++++ vanilla_first_setup/gtk/default-user.ui | 70 +++++++++ vanilla_first_setup/gtk/window.ui | 6 +- vanilla_first_setup/meson.build | 1 + vanilla_first_setup/utils/builder.py | 6 +- vanilla_first_setup/utils/parser.py | 2 +- vanilla_first_setup/utils/processor.py | 2 +- .../vanilla-first-setup.gresource.xml | 1 + vanilla_first_setup/window.py | 4 +- 12 files changed, 239 insertions(+), 15 deletions(-) create mode 100644 vanilla_first_setup/defaults/user.py create mode 100644 vanilla_first_setup/gtk/default-user.ui diff --git a/recipe.json b/recipe.json index 3ab7941..53df8c0 100644 --- a/recipe.json +++ b/recipe.json @@ -50,6 +50,9 @@ "theme": { "template": "theme" }, + "user": { + "template": "user" + }, "packages": { "template": "preferences", "is-advanced": true, diff --git a/vanilla_first_setup/defaults/applications.py b/vanilla_first_setup/defaults/applications.py index 3572ebd..ff71f98 100644 --- a/vanilla_first_setup/defaults/applications.py +++ b/vanilla_first_setup/defaults/applications.py @@ -39,7 +39,7 @@ class VanillaLayoutApplications(Adw.Bin): # signals self.btn_next.connect("clicked", self.__next_step) self.__window.connect("page-changed", self.__on_page_changed) - + @property def step_id(self): return self.__key @@ -92,7 +92,7 @@ class VanillaLayoutApplications(Adw.Bin): def close_customize(widget, dialog): dialog.hide() - + def apply_preferences(widget, dialog, apps_list, item): for app in item["applications"]: app["active"] = app["switch"].get_active() @@ -148,12 +148,12 @@ class VanillaLayoutApplications(Adw.Bin): _customize.connect("clicked", present_customize, selection_dialogs[-1], _apps_list, item) _cancel_button.connect("clicked", close_customize, selection_dialogs[-1]) _apply_button.connect("clicked", apply_preferences, selection_dialogs[-1], _apps_list, item) - + self.bundles_list.add(_action_row) self.__register_widgets.append((item["id"], _switcher, _index)) _index += 1 - + def __on_page_changed(self, widget, page): if page == self.__key: if True not in [ @@ -163,7 +163,7 @@ class VanillaLayoutApplications(Adw.Bin): self.bundles_list.set_sensitive(False) else: self.bundles_list.set_sensitive(True) - + def __next_step(self, *args): self.__window.next() @@ -192,4 +192,4 @@ class VanillaLayoutApplications(Adw.Bin): for app in self.__step["bundles"][index]["applications"]: finals["vars"][app["name"]] = False - return finals \ No newline at end of file + return finals diff --git a/vanilla_first_setup/defaults/meson.build b/vanilla_first_setup/defaults/meson.build index 32cf3c9..01a406f 100644 --- a/vanilla_first_setup/defaults/meson.build +++ b/vanilla_first_setup/defaults/meson.build @@ -5,6 +5,7 @@ sources = [ '__init__.py', 'welcome.py', 'theme.py', + 'user.py', 'applications.py', 'conn_check.py' ] diff --git a/vanilla_first_setup/defaults/user.py b/vanilla_first_setup/defaults/user.py new file mode 100644 index 0000000..4a6cd45 --- /dev/null +++ b/vanilla_first_setup/defaults/user.py @@ -0,0 +1,146 @@ +# user.py +# +# Copyright 2022 mirkobrombin +# Copyright 2022 muqtadir +# +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundationat version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import sys +import time +import re, subprocess, shutil +import crypt +from gi.repository import Gtk, Gio, GLib, Adw + + +@Gtk.Template(resource_path='/io/github/vanilla-os/FirstSetup/gtk/default-user.ui') +class VanillaDefaultUser(Adw.Bin): + __gtype_name__ = 'VanillaDefaultUser' + + btn_next = Gtk.Template.Child() + fullname_entry = Gtk.Template.Child() + username_entry = Gtk.Template.Child() + password_entry = Gtk.Template.Child() + password_confirmation = Gtk.Template.Child() + + fullname = "" + fullname_filled = False + username = "" + username_filled = False + password_filled = False + + def __init__(self, window, distro_info, key, step, **kwargs): + super().__init__(**kwargs) + self.__window = window + self.__distro_info = distro_info + self.__key = key + self.__step = step + + # signals + self.btn_next.connect("clicked", self.__window.next) + self.fullname_entry.connect('changed', self.__on_fullname_entry_changed) + self.username_entry.connect('changed', self.__on_username_entry_changed) + self.password_entry.connect('changed', self.__on_password_changed) + self.password_confirmation.connect('changed', self.__on_password_changed) + + @property + def step_id(self): + return self.__key + + def get_finals(self): + return { + "vars": { + "create": True + }, + "funcs": [ + { + "if": "create", + "type": "command", + "commands": [ + f"useradd -m {self.username} -c \"{self.fullname}\" -G sudo,adm,lpadmin -p {crypt.crypt(self.password_entry.get_text())}", + ] + } + ] + } + + def __on_fullname_entry_changed(self, *args): + _fullname = self.fullname_entry.get_text() + + if len(_fullname) > 32: + self.fullname_entry.set_text(_fullname[:32]) + self.fullname_entry.set_position(-1) + _fullname = self.fullname_entry.get_text() + + self.fullname_filled = True + self.__verify_continue() + self.fullname = _fullname + + def __on_username_entry_changed(self, *args): + _input = self.username_entry.get_text() + _status = True + + # cannot be longer than 32 characters + if len(_input) > 32: + self.username_entry.set_text(_input[:32]) + self.username_entry.set_position(-1) + _input = self.username_entry.get_text() + + # cannot contain special characters + if re.search(r'[^a-z0-9]', _input): + _status = False + self.__window.toast("Username cannot contain special characters or uppercase letters. Please choose another username.") + + # cannot be empty + elif not _input: + _status = False + self.__window.toast("Username cannot be empty. Please type a username.") + + # cannot be root + elif _input == "root": + _status = False + self.__window.toast("root user is reserved. Please choose another username.") + + if not _status: + self.username_entry.add_css_class('error') + self.username_filled = False + self.__verify_continue() + else: + self.username_entry.remove_css_class('error') + self.username_filled = True + self.__verify_continue() + self.username = _input + + + def __on_password_changed(self, *args): + password = self.password_entry.get_text() + if password == self.password_confirmation.get_text() \ + and password.strip(): + self.password_filled = True; + self.password_confirmation.remove_css_class('error') + self.password = self.__encrypt_password(password) + else: + self.password_filled = False; + self.password_confirmation.add_css_class('error') + + self.__verify_continue(); + + def __verify_continue(self): + self.btn_next.set_sensitive(self.fullname_filled and self.password_filled and self.username_filled) + + def __encrypt_password(self, password): + command = subprocess.run( + [shutil.which("openssl"), "passwd", "-crypt", password], + capture_output=True + ) + password_encrypted = command.stdout.decode('utf-8').strip('\n') + return password_encrypted diff --git a/vanilla_first_setup/gtk/default-user.ui b/vanilla_first_setup/gtk/default-user.ui new file mode 100644 index 0000000..6315266 --- /dev/null +++ b/vanilla_first_setup/gtk/default-user.ui @@ -0,0 +1,70 @@ + + + + + diff --git a/vanilla_first_setup/gtk/window.ui b/vanilla_first_setup/gtk/window.ui index 9fb70ff..18124b3 100644 --- a/vanilla_first_setup/gtk/window.ui +++ b/vanilla_first_setup/gtk/window.ui @@ -3,8 +3,8 @@ - \ No newline at end of file + diff --git a/vanilla_first_setup/meson.build b/vanilla_first_setup/meson.build index 59b0ee4..bd02177 100644 --- a/vanilla_first_setup/meson.build +++ b/vanilla_first_setup/meson.build @@ -20,6 +20,7 @@ conf.set('pkgdatadir', pkgdatadir) configure_file( input: 'vanilla-first-setup.in', output: 'vanilla-first-setup', + install_mode: 'rwxr-xr-x', configuration: conf, install: true, install_dir: get_option('bindir') diff --git a/vanilla_first_setup/utils/builder.py b/vanilla_first_setup/utils/builder.py index 894e673..e02d932 100644 --- a/vanilla_first_setup/utils/builder.py +++ b/vanilla_first_setup/utils/builder.py @@ -24,6 +24,7 @@ from vanilla_first_setup.utils.recipe import RecipeLoader from vanilla_first_setup.defaults.conn_check import VanillaDefaultConnCheck from vanilla_first_setup.defaults.welcome import VanillaDefaultWelcome from vanilla_first_setup.defaults.theme import VanillaDefaultTheme +from vanilla_first_setup.defaults.user import VanillaDefaultUser from vanilla_first_setup.layouts.preferences import VanillaLayoutPreferences from vanilla_first_setup.layouts.yes_no import VanillaLayoutYesNo @@ -37,6 +38,7 @@ templates = { "conn-check": VanillaDefaultConnCheck, "welcome": VanillaDefaultWelcome, "theme": VanillaDefaultTheme, + "user": VanillaDefaultUser, "preferences": VanillaLayoutPreferences, "yes-no": VanillaLayoutYesNo, "applications": VanillaLayoutApplications @@ -102,7 +104,7 @@ class Builder: if step["template"] in templates: _widget = templates[step["template"]](self.__window, self.distro_info, key, step) self.__register_widgets.append((_widget, _status, _protected)) - + def get_temp_finals(self, step_id: str): for widget, _, _ in self.__register_widgets: if widget.step_id == step_id: @@ -125,7 +127,7 @@ class Builder: @property def recipe(self): return self.__recipe.raw - + @property def distro_info(self): return { diff --git a/vanilla_first_setup/utils/parser.py b/vanilla_first_setup/utils/parser.py index 7855b32..b01473d 100644 --- a/vanilla_first_setup/utils/parser.py +++ b/vanilla_first_setup/utils/parser.py @@ -74,7 +74,7 @@ class Parser: # check if the condition is met if _condition == _vars[_func["if"]]: commands += _func["commands"] - + # set-up warps if any for warp in warps: _vars = warp["vars"] diff --git a/vanilla_first_setup/utils/processor.py b/vanilla_first_setup/utils/processor.py index ce7b134..c02b5e2 100644 --- a/vanilla_first_setup/utils/processor.py +++ b/vanilla_first_setup/utils/processor.py @@ -113,7 +113,7 @@ class Processor: f.write("if [ $? -eq 0 ]; then") f.write(f"{out_run}\n") f.write("fi") - + # create the done file f.write("if [ $? -eq 0 ]; then\n") f.write(f"touch {done_file}\n") diff --git a/vanilla_first_setup/vanilla-first-setup.gresource.xml b/vanilla_first_setup/vanilla-first-setup.gresource.xml index ff7b364..7cbad4b 100644 --- a/vanilla_first_setup/vanilla-first-setup.gresource.xml +++ b/vanilla_first_setup/vanilla-first-setup.gresource.xml @@ -11,6 +11,7 @@ gtk/default-conn-check.ui gtk/default-theme.ui gtk/default-welcome.ui + gtk/default-user.ui gtk/layout-preferences.ui gtk/layout-yes-no.ui diff --git a/vanilla_first_setup/window.py b/vanilla_first_setup/window.py index d69f15c..a7de457 100644 --- a/vanilla_first_setup/window.py +++ b/vanilla_first_setup/window.py @@ -172,8 +172,8 @@ class VanillaWindow(Adw.ApplicationWindow): commands ) - self.__view_progress.start(res, Processor.hide_first_setup, self.__user) - + self.__view_progress.start(res, Processor.hide_first_setup) + def set_installation_result(self, result, terminal): self.__view_done.set_result(result, terminal) self.next()