Files
herolib/lib/osal/linux/user_mgmt.v
2025-08-25 06:28:42 +02:00

310 lines
8.0 KiB
V
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module linux
import os
import json
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.ui.console
@[params]
pub struct UserCreateArgs {
pub mut:
name string @[required]
giteakey string
giteaurl string
passwd string
description string
email string
tel string
sshkey string // SSH public key
}
@[params]
pub struct UserDeleteArgs {
pub mut:
name string @[required]
}
@[params]
pub struct SSHKeyCreateArgs {
pub mut:
username string @[required]
sshkey_name string @[required]
sshkey_pub string
sshkey_priv string
}
@[params]
pub struct SSHKeyDeleteArgs {
pub mut:
username string @[required]
sshkey_name string @[required]
}
struct UserConfig {
pub mut:
name string
giteakey string
giteaurl string
email string
description string
tel string
}
// Check if running as root
pub fn (mut lf LinuxFactory) check_root() ! {
if os.getuid() != 0 {
return error(' Must be run as root')
}
}
// Create a new user with all the configuration
pub fn (mut lf LinuxFactory) user_create(args UserCreateArgs) ! {
lf.check_root()!
console.print_header('Creating user: ${args.name}')
// Save config to ~/hero/cfg/myconfig.json
lf.save_user_config(args)!
// Create user using system commands
lf.create_user_system(args)!
}
// Delete a user
pub fn (mut lf LinuxFactory) user_delete(args UserDeleteArgs) ! {
lf.check_root()!
console.print_header('Deleting user: ${args.name}')
// Check if user exists
if !osal.user_exists(args.name) {
return error('User ${args.name} does not exist')
}
// Delete user and home directory
osal.exec(cmd: 'userdel -r ${args.name}')!
console.print_green(' User ${args.name} deleted')
// Remove from config
lf.remove_user_config(args.name)!
}
// Create SSH key for user
pub fn (mut lf LinuxFactory) sshkey_create(args SSHKeyCreateArgs) ! {
lf.check_root()!
console.print_header('Creating SSH key for user: ${args.username}')
user_home := '/home/${args.username}'
ssh_dir := '${user_home}/.ssh'
// Ensure SSH directory exists
osal.dir_ensure(ssh_dir)!
osal.exec(cmd: 'chmod 700 ${ssh_dir}')!
if args.sshkey_priv != '' && args.sshkey_pub != '' {
// Both private and public keys provided
priv_path := '${ssh_dir}/${args.sshkey_name}'
pub_path := '${ssh_dir}/${args.sshkey_name}.pub'
osal.file_write(priv_path, args.sshkey_priv)!
osal.file_write(pub_path, args.sshkey_pub)!
// Set permissions
osal.exec(cmd: 'chmod 600 ${priv_path}')!
osal.exec(cmd: 'chmod 644 ${pub_path}')!
console.print_green(' SSH keys installed for ${args.username}')
} else {
// Generate new SSH key (modern ed25519)
key_path := '${ssh_dir}/${args.sshkey_name}'
osal.exec(
cmd: 'ssh-keygen -t ed25519 -f ${key_path} -N "" -C "${args.username}@$(hostname)"'
)!
console.print_green(' New SSH key generated for ${args.username}')
}
// Set ownership
osal.exec(cmd: 'chown -R ${args.username}:${args.username} ${ssh_dir}')!
}
// Delete SSH key for user
pub fn (mut lf LinuxFactory) sshkey_delete(args SSHKeyDeleteArgs) ! {
lf.check_root()!
console.print_header('Deleting SSH key for user: ${args.username}')
user_home := '/home/${args.username}'
ssh_dir := '${user_home}/.ssh'
priv_path := '${ssh_dir}/${args.sshkey_name}'
pub_path := '${ssh_dir}/${args.sshkey_name}.pub'
// Remove keys if they exist
if os.exists(priv_path) {
os.rm(priv_path)!
console.print_green(' Removed private key: ${priv_path}')
}
if os.exists(pub_path) {
os.rm(pub_path)!
console.print_green(' Removed public key: ${pub_path}')
}
}
// Save user configuration to JSON file
fn (mut lf LinuxFactory) save_user_config(args UserCreateArgs) ! {
config_dir := '${os.home_dir()}/hero/cfg'
osal.dir_ensure(config_dir)!
config_path := '${config_dir}/myconfig.json'
mut configs := []UserConfig{}
// Load existing configs if file exists
if os.exists(config_path) {
content := osal.file_read(config_path)!
configs = json.decode([]UserConfig, content) or { []UserConfig{} }
}
// Check if user already exists in config
mut found_idx := -1
for i, config in configs {
if config.name == args.name {
found_idx = i
break
}
}
new_config := UserConfig{
name: args.name
giteakey: args.giteakey
giteaurl: args.giteaurl
email: args.email
description: args.description
tel: args.tel
}
if found_idx >= 0 {
configs[found_idx] = new_config
} else {
configs << new_config
}
// Save updated configs
content := json.encode_pretty(configs)
osal.file_write(config_path, content)!
console.print_green(' User config saved to ${config_path}')
}
// Remove user from configuration
fn (mut lf LinuxFactory) remove_user_config(username string) ! {
config_dir := '${os.home_dir()}/hero/cfg'
config_path := '${config_dir}/myconfig.json'
if !os.exists(config_path) {
return
}
content := osal.file_read(config_path)!
mut configs := json.decode([]UserConfig, content) or { return }
// Filter out the user
configs = configs.filter(it.name != username)
// Save updated configs
updated_content := json.encode_pretty(configs)
osal.file_write(config_path, updated_content)!
console.print_green(' User config removed for ${username}')
}
// Create user in the system
fn (mut lf LinuxFactory) create_user_system(args UserCreateArgs) ! {
// Check if user exists
if osal.user_exists(args.name) {
console.print_green(' User ${args.name} already exists')
} else {
console.print_item(' Creating user ${args.name}')
osal.exec(cmd: 'useradd -m -s /bin/bash ${args.name}')!
}
user_home := '/home/${args.name}'
// Setup SSH if key provided
if args.sshkey != '' {
ssh_dir := '${user_home}/.ssh'
osal.dir_ensure(ssh_dir)!
osal.exec(cmd: 'chmod 700 ${ssh_dir}')!
authorized_keys := '${ssh_dir}/authorized_keys'
osal.file_write(authorized_keys, args.sshkey)!
osal.exec(cmd: 'chmod 600 ${authorized_keys}')!
osal.exec(cmd: 'chown -R ${args.name}:${args.name} ${ssh_dir}')!
console.print_green(' SSH key installed for ${args.name}')
}
// Ensure ourworld group exists
group_check := osal.exec(cmd: 'getent group ourworld', raise_error: false) or {
osal.Job{
exit_code: 1
}
}
if group_check.exit_code != 0 {
console.print_item(' Creating group ourworld')
osal.exec(cmd: 'groupadd ourworld')!
} else {
console.print_green(' Group ourworld exists')
}
// Add user to group
user_groups := osal.exec(cmd: 'id -nG ${args.name}', stdout: false)!
if !user_groups.output.contains('ourworld') {
osal.exec(cmd: 'usermod -aG ourworld ${args.name}')!
console.print_green(' Added ${args.name} to ourworld group')
} else {
console.print_green(' ${args.name} already in ourworld')
}
// Setup /code directory
osal.dir_ensure('/code')!
osal.exec(cmd: 'chown root:ourworld /code')!
osal.exec(cmd: 'chmod 2775 /code')! // rwx for user+group, SGID bit
console.print_green(' /code prepared (group=ourworld, rwx for group, SGID bit set)')
// Create SSH agent profile script
lf.create_ssh_agent_profile(args.name)!
// Set password if provided
if args.passwd != '' {
osal.exec(cmd: 'echo "${args.name}:${args.passwd}" | chpasswd')!
console.print_green(' Password set for ${args.name}')
}
console.print_header('🎉 Setup complete for user ${args.name}')
}
// Create SSH agent profile script
fn (mut lf LinuxFactory) create_ssh_agent_profile(username string) ! {
user_home := '/home/${username}'
profile_script := '${user_home}/.profile_sshagent'
// script_content := ''
panic('implement')
osal.file_write(profile_script, script_content)!
osal.exec(cmd: 'chown ${username}:${username} ${profile_script}')!
osal.exec(cmd: 'chmod 644 ${profile_script}')!
// Source it on login
bashrc := '${user_home}/.bashrc'
bashrc_content := if os.exists(bashrc) { osal.file_read(bashrc)! } else { '' }
if !bashrc_content.contains('.profile_sshagent') {
source_line := '[ -f ~/.profile_sshagent ] && source ~/.profile_sshagent\n'
osal.file_write(bashrc, bashrc_content + source_line)!
}
console.print_green(' SSH agent profile created for ${username}')
}