forked from pisilinux-rs/yali-rs
8bb7116194
- form_input_text: Frame wrapper with stroke/fill/rounding, .frame(false) - form_input_password: SVG eye icons instead of emoji, Frame wrapper - Partition disk grid: hardcoded colors, left-aligned headers - Users: root password show/hide toggle, colored form frames - Add c_root_bg() theme function
317 lines
13 KiB
Rust
317 lines
13 KiB
Rust
use eframe::egui;
|
||
use rust_i18n::t;
|
||
use crate::installer::{GlobalState, InstallerStep};
|
||
use crate::funct::{ form_input_text, form_input_password, form_label, form_checkbox };
|
||
|
||
/// Kullanıcı adı, şifre, hostname ve yönetici (root) şifresi ayarları adımı.
|
||
pub struct UsersStep {
|
||
use_same_for_admin: bool,
|
||
show_password: bool,
|
||
show_password_confirm: bool,
|
||
show_root_password: bool, // <-- Eklendi
|
||
show_root_password_confirm: bool, // <-- Eklendi
|
||
}
|
||
|
||
impl Default for UsersStep {
|
||
fn default() -> Self {
|
||
Self {
|
||
use_same_for_admin: true,
|
||
show_password: false,
|
||
show_password_confirm: false,
|
||
show_root_password: false,
|
||
show_root_password_confirm: false,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Gerçek addan geçerli bir Linux kullanıcı adı türetir.
|
||
/// - Küçük harfe çevirir
|
||
/// - Türkçe karakterleri ASCII'ye dönüştürür (ş→s, ğ→g, ı→i, ö→o, ü→u, ç→c)
|
||
/// - Boşlukları `-`'ye çevirir
|
||
/// - Geçersiz karakterleri temizler
|
||
fn derive_username(real_name: &str) -> String {
|
||
let mut name = real_name.to_lowercase();
|
||
name = name
|
||
.replace('ş', "s")
|
||
.replace('ğ', "g")
|
||
.replace('ı', "i")
|
||
.replace('ö', "o")
|
||
.replace('ü', "u")
|
||
.replace('ç', "c")
|
||
.replace(' ', "-")
|
||
.replace('_', "-");
|
||
let valid: String = name.chars().filter(|c| c.is_ascii_alphanumeric() || *c == '-' || *c == '_').collect();
|
||
let trimmed = valid.trim_matches('-').to_string();
|
||
if trimmed.is_empty() {
|
||
"user".to_string()
|
||
} else if trimmed.len() > 32 {
|
||
trimmed[..32].trim_end_matches('-').to_string()
|
||
} else {
|
||
trimmed
|
||
}
|
||
}
|
||
|
||
/// Kullanıcı adının geçerliliğini kontrol eder.
|
||
/// Linux kuralları: küçük harf veya alt çizgi ile başlamalı,
|
||
/// en fazla 32 karakter, yalnızca [a-z0-9_-] içermeli.
|
||
fn is_valid_username(s: &str) -> bool {
|
||
if s.is_empty() || s.len() > 32 {
|
||
return false;
|
||
}
|
||
let mut chars = s.chars();
|
||
// İlk karakter küçük harf veya alt çizgi olmalı
|
||
match chars.next() {
|
||
Some(c) if c.is_ascii_lowercase() || c == '_' => {}
|
||
_ => return false,
|
||
}
|
||
chars.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-')
|
||
}
|
||
|
||
/// Hostname kuralları: 1-63 karakter, yalnızca [a-zA-Z0-9-], tire ile başlayamaz/bitemez.
|
||
fn is_valid_hostname(s: &str) -> bool {
|
||
if s.is_empty() || s.len() > 63 {
|
||
return false;
|
||
}
|
||
if s.starts_with('-') || s.ends_with('-') {
|
||
return false;
|
||
}
|
||
s.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')
|
||
}
|
||
|
||
/// Otomatik hostname üretirken nokta, boşluk ve geçersiz karakterleri kaldırır.
|
||
fn sanitize_hostname(s: &str) -> String {
|
||
let mut hostname: String = s
|
||
.chars()
|
||
.filter(|c| c.is_ascii_alphanumeric())
|
||
.collect();
|
||
|
||
if hostname.len() > 63 {
|
||
hostname.truncate(63);
|
||
}
|
||
|
||
if hostname.is_empty() {
|
||
"host".to_string()
|
||
} else {
|
||
hostname
|
||
}
|
||
}
|
||
|
||
fn password_strength(p: &str) -> (String, egui::Color32) {
|
||
let len = p.len();
|
||
let has_upper = p.chars().any(|c| c.is_uppercase());
|
||
let has_digit = p.chars().any(|c| c.is_ascii_digit());
|
||
let has_special = p.chars().any(|c| "!@#$%^&*()_+-=[]{}|;':\",./<>?".contains(c));
|
||
let score = (len >= 8) as u8
|
||
+ (len >= 12) as u8
|
||
+ has_upper as u8
|
||
+ has_digit as u8
|
||
+ has_special as u8;
|
||
match score {
|
||
0..=1 => (t!("pwd_very_weak").to_string(), egui::Color32::from_rgb(200, 60, 40)),
|
||
2 => (t!("pwd_weak").to_string(), egui::Color32::from_rgb(220, 140, 40)),
|
||
3 => (t!("pwd_medium").to_string(), egui::Color32::from_rgb(200, 180, 40)),
|
||
4 => (t!("pwd_strong").to_string(), egui::Color32::from_rgb(80, 170, 80)),
|
||
_ => (t!("pwd_very_strong").to_string(), egui::Color32::from_rgb(40, 140, 60)),
|
||
}
|
||
}
|
||
|
||
impl InstallerStep for UsersStep {
|
||
fn name(&self) -> String {
|
||
t!("users").to_string()
|
||
}
|
||
|
||
fn on_enter(&mut self, state: &mut GlobalState) {
|
||
// Gerçek ad varsa ama kullanıcı adı yoksa türet
|
||
if !state.real_name.is_empty() && state.username.is_empty() {
|
||
state.username = derive_username(&state.real_name);
|
||
}
|
||
// Hostname boşsa önce /etc/hostname dene
|
||
if state.hostname.is_empty() {
|
||
let sys_hostname = std::fs::read_to_string("/etc/hostname")
|
||
.unwrap_or_default()
|
||
.trim()
|
||
.to_string();
|
||
let sys_hostname = sanitize_hostname(&sys_hostname);
|
||
if !sys_hostname.is_empty() && sys_hostname != "localhost" {
|
||
state.hostname = sys_hostname;
|
||
}
|
||
}
|
||
// Hâlâ boşsa ve username varsa username-pc öner
|
||
if state.hostname.is_empty() && !state.username.is_empty() {
|
||
state.hostname = sanitize_hostname(&format!("{}pc", state.username));
|
||
}
|
||
}
|
||
|
||
fn show(&mut self, ui: &mut egui::Ui, state: &mut GlobalState) {
|
||
use crate::ui::theme;
|
||
|
||
theme::section_heading(ui, &t!("users_title"));
|
||
ui.label(
|
||
egui::RichText::new(t!("users_description"))
|
||
.color(theme::c_text_dim())
|
||
.size(13.0),
|
||
);
|
||
ui.add_space(12.0);
|
||
|
||
// ─── Kullanıcı Bilgileri Formu ──────────────────────────────────
|
||
egui::Frame::group(ui.style())
|
||
.fill(egui::Color32::from_rgb(240, 243, 198))
|
||
.rounding(6.0)
|
||
.inner_margin(egui::Margin::symmetric(10.0, 10.0))
|
||
.show(ui, |ui| {
|
||
ui.set_width(ui.available_width());
|
||
ui.horizontal(|ui| {
|
||
ui.vertical(|ui| {
|
||
ui.add_space(10.0);
|
||
|
||
// Gerçek ad
|
||
form_label(ui, &t!("real_name_label"));
|
||
{
|
||
let prev = state.real_name.clone();
|
||
form_input_text(ui, &mut state.real_name);
|
||
if state.real_name != prev && !state.real_name.is_empty() {
|
||
state.username = derive_username(&state.real_name);
|
||
}
|
||
}
|
||
if !state.real_name.is_empty() {
|
||
ui.colored_label(theme::c_success(), "✅");
|
||
} else {
|
||
ui.colored_label(theme::c_error(), "❌");
|
||
}
|
||
ui.add_space(6.0);
|
||
|
||
// Kullanıcı adı (otomatik türetilir, düzenlenebilir)
|
||
form_label(ui, &t!("username_label"));
|
||
form_input_text(ui, &mut state.username);
|
||
if !state.username.is_empty() {
|
||
if is_valid_username(&state.username) {
|
||
ui.colored_label(theme::c_success(), "✅");
|
||
} else {
|
||
ui.colored_label(theme::c_warning(), t!("username_invalid"));
|
||
}
|
||
}
|
||
ui.add_space(6.0);
|
||
|
||
// Şifre
|
||
form_label(ui, &t!("password_label"));
|
||
// OEM modunda varsayılan şifre uyarısı göster
|
||
if state.oem_mode && !state.oem_password_changed {
|
||
ui.colored_label(
|
||
theme::c_error(),
|
||
t!("oem_change_password_warning"),
|
||
);
|
||
}
|
||
{
|
||
let show_pwd = &mut self.show_password;
|
||
let before = state.password.clone();
|
||
|
||
form_input_password(ui, &mut state.password, show_pwd);
|
||
// Kullanıcı şifreyi değiştirdiyse OEM bayrağını kaldır
|
||
if state.oem_mode && state.password != before {
|
||
state.oem_password_changed = true;
|
||
}
|
||
}
|
||
if !state.password.is_empty() {
|
||
let (label, color) = password_strength(&state.password);
|
||
ui.colored_label(color, label);
|
||
}
|
||
ui.add_space(6.0);
|
||
|
||
// Şifre doğrulama
|
||
form_label(ui, &t!("password_confirm_label"));
|
||
{
|
||
let show_pwd = &mut self.show_password_confirm;
|
||
form_input_password(ui, &mut state.password_confirm, show_pwd);
|
||
}
|
||
if !state.password_confirm.is_empty() {
|
||
if state.password == state.password_confirm {
|
||
ui.colored_label(theme::c_success(), "✅");
|
||
} else {
|
||
ui.colored_label(theme::c_warning(), t!("passwords_no_match"));
|
||
}
|
||
}
|
||
ui.add_space(6.0);
|
||
|
||
// Hostname
|
||
form_label(ui, &t!("hostname_label"));
|
||
form_input_text(ui, &mut state.hostname);
|
||
if !state.hostname.is_empty() {
|
||
if is_valid_hostname(&state.hostname) {
|
||
ui.colored_label(theme::c_success(), "✅");
|
||
} else {
|
||
ui.colored_label(theme::c_warning(), t!("hostname_invalid"));
|
||
}
|
||
}
|
||
|
||
});
|
||
ui.add_space(10.0);
|
||
});
|
||
});
|
||
|
||
ui.add_space(14.0);
|
||
|
||
// ─── Yönetici (root) Ayarları ─────────────────────────────────────
|
||
form_checkbox(ui, &mut self.use_same_for_admin, &t!("use_same_for_admin"));
|
||
|
||
if self.use_same_for_admin {
|
||
state.root_password = state.password.clone();
|
||
state.root_password_confirm = state.password_confirm.clone();
|
||
} else {
|
||
ui.add_space(8.0);
|
||
egui::Frame::group(ui.style())
|
||
.fill(theme::c_root_bg())
|
||
.rounding(6.0)
|
||
.inner_margin(egui::Margin::same(12.0))
|
||
.show(ui, |ui| {
|
||
ui.set_width(ui.available_width());
|
||
ui.label(egui::RichText::new(t!("admin_description")).size(12.0).color(theme::c_text_dim()));
|
||
ui.add_space(8.0);
|
||
|
||
// Root şifresi
|
||
ui.label(egui::RichText::new(t!("admin_password_label")).strong().color(theme::c_text()));
|
||
|
||
// DÜZELTME: 3. parametre olarak show_root_password eklendi
|
||
let show_root_pwd = &mut self.show_root_password;
|
||
form_input_password(ui, &mut state.root_password, show_root_pwd);
|
||
|
||
ui.add_space(8.0);
|
||
|
||
// Şifre doğrulama
|
||
ui.label(egui::RichText::new(t!("admin_password_confirm_label")).strong().color(theme::c_text()));
|
||
|
||
// DÜZELTME: 3. parametre olarak show_root_password_confirm eklendi
|
||
let show_root_pwd_conf = &mut self.show_root_password_confirm;
|
||
form_input_password(ui, &mut state.root_password_confirm, show_root_pwd_conf);
|
||
|
||
if state.root_password == state.root_password_confirm {
|
||
ui.colored_label(theme::c_success(), "✅");
|
||
} else {
|
||
ui.colored_label(theme::c_error(), t!("passwords_no_match"));
|
||
}
|
||
});
|
||
}
|
||
|
||
}
|
||
|
||
fn is_complete(&self, state: &GlobalState) -> bool {
|
||
// OEM modunda varsayılan şifre değiştirilmeden ilerlenmez.
|
||
if state.oem_mode && !state.oem_password_changed {
|
||
return false;
|
||
}
|
||
|
||
let user_ok = is_valid_username(&state.username)
|
||
&& is_valid_hostname(&state.hostname)
|
||
&& state.password.len() >= 8
|
||
&& state.password == state.password_confirm;
|
||
|
||
if self.use_same_for_admin {
|
||
user_ok
|
||
} else {
|
||
user_ok && (
|
||
state.root_password.is_empty()
|
||
|| (state.root_password.len() >= 8 && state.root_password == state.root_password_confirm)
|
||
)
|
||
}
|
||
}
|
||
}
|