# Copyright © 2010 Julian Blake Kongslie # Licensed under the BSD 3-clause license. package Piny::User; use Moose; use Moose::Util::TypeConstraints; use Piny::Email; use Piny::Group; # Types subtype 'Username' => as 'Str' => where { $_ =~ /^(?!(git|ikiwiki)-)[[a-zA-Z0-9][a-zA-Z0-9_.-]*$/ } => message { 'That username is not in the correct format for a piny user.' } ; # Attributes has 'uid' => ( is => 'ro' , isa => 'Int' , lazy_build => 1 ); has 'username' => ( is => 'ro' , isa => 'Username' , lazy_build => 1 ); has 'pwent' => ( is => 'ro' , isa => 'ArrayRef' , lazy_build => 1 , init_arg => undef ); has 'password_hash' => ( is => 'ro' , isa => 'Str' , lazy_build => 1 , init_arg => undef ); has 'email' => ( is => 'ro' , isa => 'Piny::Email' , lazy_build => 1 , init_arg => undef ); has 'groups' => ( is => 'ro' , isa => 'ArrayRef[Piny::Group]' , lazy_build => 1 , init_arg => undef ); # Public methods sub add_group { my ( $s, @groups ) = @_; foreach my $group ( @groups ) { $group->add_member( $s ); }; }; sub remove_group { my ( $s, @groups ) = @_; foreach my $group ( @groups ) { $group->remove_member( $s ); }; }; # Builder methods # If constructed with just one argument, then # * If that argument is numeric, treat it as a UID. # * Otherwise, treat it as a username. around BUILDARGS => sub { my ( $orig, $class ) = ( shift, shift ); if ( @_ == 1 && ! ref $_[0] ) { if ( $_[0] =~ m/^\d+$/ ) { return $class->$orig( uid => $_[0] ); } else { return $class->$orig( username => $_[0] ); }; } else { return $class->$orig( @_ ); }; }; sub BUILD { my ( $s ) = @_; if ( not ( $s->has_uid( ) or $s->has_username( ) ) ) { die "You must provide either UID or username!"; }; if ( $s->has_uid( ) and $s->has_username( ) ) { die "You must not provide both UID and username!"; }; }; sub _build_uid { my ( $s ) = @_; return $s->pwent( )->[2]; }; sub _build_username { my ( $s ) = @_; return $s->pwent( )->[0]; }; sub _build_pwent { my ( $s ) = @_; if ( $s->has_uid( ) ) { my @res = getpwuid( $s->uid( ) ); die "getpwuid( " . $s->uid( ) . " ) failed: $!" unless @res; return \@res; } elsif ( $s->has_username( ) ) { my @res = getpwnam( $s->username( ) ); die "getpwnam( " . $s->username( ) . " ) failed: $!" unless @res; return \@res; } else { die "Not enough information provided to lookup user!"; }; }; sub _build_password_hash { my ( $s ) = @_; return $s->pwent( )->[1]; }; sub _build_email { my ( $s ) = @_; return Piny::Email->new( address => $s->pwent( )->[6] ); }; sub _build_groups { my ( $s ) = @_; my @res; my @ent; endgrent( ); while ( @ent = getgrent( ) ) { next if ( $ent[3] eq "" ); foreach my $member ( split( /:/, $ent[3] ) ) { if ( $member eq $s->username( ) ) { push @res, Piny::Group->new( gid => $ent[2] ); last; }; }; }; endgrent( ); return \@res; }; # Moose boilerplate __PACKAGE__->meta->make_immutable; 1;