NAME

Sys::Export::Unix::UserDB - Abstractions for Unix passwd/group/shadow files

SYNOPSIS

use Sys::Export::Unix::UserDB;

# Load from filesystem
my $source_db = Sys::Export::Unix::UserDB->new;
$source_db->load('/path/to/source/etc');

# Create destination database and import some users/groups
my $dest_db = Sys::Export::Unix::UserDB->new(
  auto_import => $source_db,
  users => [qw( root daemon postgres nobody )],
  groups => [qw( root wheel daemon video audio )],
);

# Add new users and groups
$dest_db->add_group('newgroup', gid => 1001);
$dest_db->add_user('newuser', uid => 1001, groups => [qw( audio video newgroup )]);

# Save to files
$dest_db->save('/path/to/dest/etc', format => 'Linux');

DESCRIPTION

This module provides abstractions for working with Unix 'passwd' databases, consisting of the /etc/passwd and /etc/group files, as well as platform-specific extensions like /etc/shadow. (currently only Linux is supported, but BSD /etc/master.passwd wouldn't be hard to add support for)

The goal of this object is to extract user/group information from one system image and merge it into another, with proper conflict detection and UID/GID management. It can also import users and groups from the host via getpwnam/getgrnam.

CONSTRUCTORS

new

$udb= Sys::Export::Unix::UserDB->new(%options);
auto_import

The "auto_import" attribute, which can be another UserDB, or any "true" value meaning to import from the host.

valid_name_regex

Affects the result of "is_valid_name", unless overridden in a subclass

users

Hashref of { username => \%user_attrs } of users to be added. An arrayref of usernames can be used if you set auto_import, in which case each name will be imported (but must exist in the auto_import source). THis can also be an arrayref of User objects.

groups

Hashref of { groupname => \%group_attrs } of groups to be added. An arrayref of group names can be used if you set auto_import. It can also be an arrayref of Group objects.

ATTRIBUTES

users

A hashref of username => $user_obj.

uids

A convenience hashref uid => $first_user_obj_having_uid.

groups

A hashref of groupname => $group_obj.

gids

A convenience hashref gid => $first_group_obj_having_gid.

auto_import

This setting causes "user" (or "group") with an unknown name or ID to attempt to import the user/group rather than returning false. If the import fails, the "user" / "group" methods return false as normal.

If the value of this attribute is an instance of Sys::Export::Unix::UserDB, it imports from that other user database. If the value is a simple true scalar, it imports from the host via getpwnam etc. See "import_user" for a description of how imports work.

METHODS

clone

my $cloned_db = $userdb->clone;

Creates a deep clone of the entire UserDB object.

is_valid_name

Return true if a name is valid for users/groups of a UserDB. By default this uses a fairly permissive regular expression, and future versions may become more permissive. You can override it with something more specific to your system by passing valid_name_regex => qr/.../ to the constructor. You could also override this method in a subclass.

load

$userdb->load($path);

Given a path like /example/etc, reads passwd, group, and (if readable) shadow files from that directory. Future versions may also support master.passwd for BSD support.

save

$userdb->save($path_or_hashref);

If given a path, saves passwd, group, and shadow files to that directory. If given a hashref, saves the file contents into scalars named 'passwd', 'group', 'shadow'.

import_user

$user= $userdb->import_user($name);         # attrs from getpwnam
$user= $userdb->import_user($user_obj);     # from another userdb
$user= $userdb->import_user($name, %attrs); # like add_user, but "DWIM"

Imports a user into this UserDB. This differs from "add_user" in that it will attempt to re-number foreign UID/GIDs that conflcit with UID/GIDs that already exist in this UserDB. UID/GID under 1000 are considered "service accounts" and remapping will choose a new number on the same side of that divider as the old number. You should specify a name rather than GID for the user's primary group. If the group name is the same as the user name, this will create a group with GID equal to the UID.

Note: the behavior of this function is subject to change if I can find better ways to Do What I Mean for an import. If you want perfect backward compatibility, you should add the users and groups directly with the add_* functions.

Returns the newly created user object, or dies.

import_group

$group= $userdb->import_group($name);         # attrs from getgrnam
$group= $userdb->import_group($group_obj);    # from another userdb
$group= $userdb->import_group($name, %attrs); # like add_group, but "DWIM"

Imports a group into this UserDB. This differs from "add_group" in that it will attempt to re-number foreign GIDs that conflcit with GIDs that already exist in this UserDB. GIDs under 1000 are considered "service accounts" and remapping will choose a new number on the same side of that divider as the old number.

Returns the newly created group object, or dies.

add_user

$user= $userdb->add_user($name_or_user_obj, %attrs);

Creates a new user. If the first parameter is a User object, clones it. Otherwise creates a new user with the given name. Duplicate names throw an exception, but duplicate UIDs only warn.

Returns the newly created user object, or dies.

add_group

$group= $userdb->add_group($name_or_group_obj, %attrs);

Creates a new group. If the first parameter is a Group object, clones it. Otherwise creates a new group with the given name. Duplicate names throw an exception, but duplicate UIDs only warn.

Returns the newly created group object, or dies.

user

$user = $userdb->user($name_or_uid);

Returns a user by name or UID if it exists, undef otherwise. If "auto_import" is enabled, this may first attempt to import the requested UID/name.

has_user

Like "user" but returns a boolean and doesn't attempt to auto_import.

group

$group = $userdb->group($name_or_uid);

Returns a group by name or GID if it exists, undef otherwise. If "auto_import" is enabled, this may first attempt to import the requested GID/name.

has_group

Like "group" but returns a boolean and doesn't attempt to auto_import.

USER OBJECTS

The user entries in the UserDB are represented as mostly-writeable objects. These deviate from the normal fields of /etc/passwd by having a group attribute instead of gid. The gid is resolved during export using the UserDB's group list. Also, the supplemental groups are stored on the user object instead of as a list of members on the group object. There are also attributes for the fields of the shadow file.

You may declare arbitrary attributes, but you get a warning if they aren't known. This allows future compatibility with formats other than Linux.

User Attributes

This object supports accessors for arbitrary attributes via AUTOLOAD, but the following are pre-defined. Using an unknown attribute accessor generates a warning, which you can suppress by adding keys to the set of %Sys::Export::Unix::UserDB::User::known_attrs.

name

Required

uid

Required

group

Required, and should be a name rather than a GID.

groups

A set (hashref) of supplemental group names, but you may assign using an arrayref for convenience.

comment

General user information, usually full name

gecos

More specific type of comment which should be composed of Full Name, Ofice Location, Work Tel. and Home Tel. User-editable, so structure is not enforced.

dir

Home directory path

shell

Login shell, or program to run on login

passwd

The hashed password. This field is written to the shadow file. If a shadow file entry is required (for this or other shadow fields) then the passwd file field is written as x, else *.

expire

Unix time when account will expire (converted to epoch seconds if the field was stored as days)

pw_last_change

Unix time of last password change (converted to epoch seconds if the field was stored as days)

pw_min_age

Min days before password can be changed

pw_max_age

Max days before password must be changed

pw_warn_days

Days before max when warning is given to user

pw_inactive_days

Days after max when user can still log in and immediately change password

User Methods

new
clone
add_group
remove_group

GROUP OBJECTS

While the /etc/group file normally stores a list of users belonging to a group, this UserDB implementation stores a set of groups on the user object, so the group object is rather empty.

Group Attributes:

This object supports accessors for arbitrary attributes via AUTOLOAD, but the following are pre-defined. Using an unknown attribute accessor generates a warning, which you can suppress by adding keys to the set of %Sys::Export::Unix::UserDB::Group::known_attrs.

name

Group name

gid

Group ID

passwd

Group password, which should never be used anyway. Leave this undef or '*'.

Group Methods:

new
clone

VERSION

version 0.003

AUTHOR

Michael Conrad <mike@nrdvana.net>

COPYRIGHT AND LICENSE

This software is copyright (c) 2025 by Michael Conrad.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.