276 lines
8.7 KiB
Nix
276 lines
8.7 KiB
Nix
{
|
|
pkgs,
|
|
config,
|
|
lib,
|
|
inputs,
|
|
...
|
|
}:
|
|
|
|
# ============================================================================
|
|
# User Configuration Module
|
|
# ============================================================================
|
|
# This module defines the schema for user accounts and handles their creation.
|
|
# It bridges the gap between the data in 'users.nix' and the actual NixOS
|
|
# and Home Manager configuration.
|
|
|
|
let
|
|
# Load users.nix to get account definitions
|
|
accounts = config.athenix.users or { };
|
|
|
|
# Helper: Resolve external module path from fetchGit/fetchTarball/path
|
|
resolveExternalPath =
|
|
external:
|
|
if external == null then
|
|
null
|
|
else if builtins.isAttrs external && external ? outPath then
|
|
external.outPath
|
|
else
|
|
external;
|
|
|
|
# Helper: Check if path exists and is valid
|
|
isValidPath =
|
|
path:
|
|
path != null
|
|
&& (builtins.isPath path || (builtins.isString path && lib.hasPrefix "/" path))
|
|
&& builtins.pathExists path;
|
|
|
|
# Extract athenix.users options from external user.nix modules
|
|
# First, build a cache of options per user from their external user.nix (if any).
|
|
externalUserModuleOptions = lib.genAttrs (lib.attrNames accounts) (
|
|
name:
|
|
let
|
|
user = accounts.${name};
|
|
externalPath = resolveExternalPath (user.external or null);
|
|
userNixPath = if externalPath != null then externalPath + "/user.nix" else null;
|
|
in
|
|
if isValidPath userNixPath then
|
|
let
|
|
# Import and evaluate the module with minimal args
|
|
outerModule = import userNixPath { inherit inputs; };
|
|
evaluatedModule = outerModule {
|
|
config = { };
|
|
inherit lib pkgs;
|
|
osConfig = null;
|
|
};
|
|
# Extract just the athenix.users.<name> options
|
|
athenixUsers = evaluatedModule.athenix.users or { };
|
|
in
|
|
athenixUsers.${name} or { }
|
|
else
|
|
{ }
|
|
);
|
|
|
|
# externalUserOptions only contains users that actually have options defined
|
|
externalUserOptions = lib.filterAttrs (
|
|
_: moduleOptions: moduleOptions != { }
|
|
) externalUserModuleOptions;
|
|
|
|
# Submodule defining the structure of a user account
|
|
userSubmodule = lib.types.submodule {
|
|
options = {
|
|
isNormalUser = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
};
|
|
description = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
};
|
|
extraGroups = lib.mkOption {
|
|
type = lib.types.listOf lib.types.str;
|
|
default = [ ];
|
|
};
|
|
hashedPassword = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "!";
|
|
};
|
|
extraPackages = lib.mkOption {
|
|
type = lib.types.listOf lib.types.package;
|
|
default = [ ];
|
|
};
|
|
excludePackages = lib.mkOption {
|
|
type = lib.types.listOf lib.types.package;
|
|
default = [ ];
|
|
};
|
|
homePackages = lib.mkOption {
|
|
type = lib.types.listOf lib.types.package;
|
|
default = [ ];
|
|
};
|
|
extraImports = lib.mkOption {
|
|
type = lib.types.listOf lib.types.path;
|
|
default = [ ];
|
|
};
|
|
external = lib.mkOption {
|
|
type = lib.types.nullOr (
|
|
lib.types.oneOf [
|
|
lib.types.path
|
|
lib.types.package
|
|
lib.types.attrs
|
|
]
|
|
);
|
|
default = null;
|
|
description = ''
|
|
External user configuration module. Can be:
|
|
- A path to a local module directory
|
|
- A fetchGit/fetchTarball result pointing to a repository
|
|
|
|
The external module can contain:
|
|
- user.nix (optional): Sets athenix.users.<name> options AND home-manager config
|
|
- nixos.nix (optional): System-level NixOS configuration
|
|
|
|
Example: builtins.fetchGit { url = "https://github.com/user/dotfiles"; rev = "..."; }
|
|
'';
|
|
};
|
|
opensshKeys = lib.mkOption {
|
|
type = lib.types.listOf lib.types.str;
|
|
default = [ ];
|
|
description = "List of SSH public keys for the user.";
|
|
};
|
|
shell = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.package;
|
|
default = null;
|
|
description = "The shell for this user.";
|
|
};
|
|
editor = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.package;
|
|
default = null;
|
|
description = "The default editor for this user.";
|
|
};
|
|
useZshTheme = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Whether to apply the system Zsh theme.";
|
|
};
|
|
useNvimPlugins = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Whether to apply the system Neovim configuration.";
|
|
};
|
|
enable = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = "Whether this user account is enabled on this system.";
|
|
};
|
|
};
|
|
};
|
|
in
|
|
{
|
|
|
|
options.athenix.users = lib.mkOption {
|
|
type = lib.types.attrsOf userSubmodule;
|
|
default = { };
|
|
description = "User accounts configuration. Set enable=true for users that should exist on this system.";
|
|
};
|
|
|
|
config = {
|
|
# Merge user definitions from users.nix with options from external user.nix modules
|
|
# External options take precedence over users.nix (which uses lib.mkDefault)
|
|
athenix.users = lib.mapAttrs (
|
|
name: user:
|
|
user
|
|
// {
|
|
description = lib.mkDefault (user.description or null);
|
|
shell = lib.mkDefault (user.shell or null);
|
|
extraGroups = lib.mkDefault (user.extraGroups or [ ]);
|
|
}
|
|
// (externalUserOptions.${name} or { })
|
|
) accounts;
|
|
|
|
# Generate NixOS users
|
|
users.users =
|
|
let
|
|
enabledAccounts = lib.filterAttrs (_: user: user.enable) config.athenix.users;
|
|
in
|
|
lib.mapAttrs (
|
|
name: user:
|
|
let
|
|
isPlasma6 = config.services.desktopManager.plasma6.enable;
|
|
defaultPackages = lib.optionals (isPlasma6 && name != "root") [ pkgs.kdePackages.kate ];
|
|
finalPackages = lib.subtractLists user.excludePackages (defaultPackages ++ user.extraPackages);
|
|
in
|
|
{
|
|
inherit (user) isNormalUser extraGroups hashedPassword;
|
|
description = if user.description != null then user.description else lib.mkDefault "";
|
|
openssh.authorizedKeys.keys = user.opensshKeys;
|
|
packages = finalPackages;
|
|
shell = if user.shell != null then user.shell else pkgs.bash;
|
|
}
|
|
) enabledAccounts;
|
|
|
|
# Home Manager configs per user
|
|
home-manager = {
|
|
useGlobalPkgs = true;
|
|
useUserPackages = true;
|
|
extraSpecialArgs = {
|
|
osConfig = config;
|
|
inherit inputs;
|
|
};
|
|
|
|
users =
|
|
let
|
|
enabledAccounts = lib.filterAttrs (_: user: user.enable) config.athenix.users;
|
|
in
|
|
lib.mapAttrs (
|
|
name: user:
|
|
let
|
|
# Resolve external module paths
|
|
hasExternal = user.external != null;
|
|
externalPath = resolveExternalPath user.external;
|
|
userNixPath = if externalPath != null then externalPath + "/user.nix" else null;
|
|
hasExternalUser = isValidPath userNixPath;
|
|
|
|
# Import external user.nix for home-manager (filter out athenix.* options)
|
|
externalUserModule =
|
|
if hasExternalUser then
|
|
let
|
|
fullModule = import userNixPath { inherit inputs; };
|
|
in
|
|
# Only pass through non-athenix options to home-manager
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
osConfig,
|
|
...
|
|
}:
|
|
let
|
|
evaluated = fullModule {
|
|
inherit
|
|
config
|
|
lib
|
|
pkgs
|
|
osConfig
|
|
;
|
|
};
|
|
in
|
|
lib.filterAttrs (attrName: _: attrName != "athenix") evaluated
|
|
else
|
|
{ };
|
|
|
|
# Common imports based on user flags
|
|
commonImports = lib.optional user.useZshTheme ../sw/theme.nix ++ [
|
|
(import ../sw/nvim.nix { inherit user; })
|
|
];
|
|
|
|
# Build imports list
|
|
allImports = user.extraImports ++ commonImports ++ lib.optional hasExternalUser externalUserModule;
|
|
in
|
|
lib.mkMerge [
|
|
{
|
|
imports = allImports;
|
|
|
|
# Always set these required options
|
|
home.username = name;
|
|
home.homeDirectory = if name == "root" then "/root" else "/home/${name}";
|
|
home.stateVersion = "25.11";
|
|
}
|
|
(lib.mkIf (!hasExternal) {
|
|
# For local users only, add their packages
|
|
home.packages = user.homePackages;
|
|
})
|
|
]
|
|
) enabledAccounts;
|
|
};
|
|
};
|
|
}
|