summaryrefslogtreecommitdiff
path: root/usr/src/libpiny
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/libpiny')
-rw-r--r--usr/src/libpiny/Piny.pm11
-rw-r--r--usr/src/libpiny/Piny/Email.pm42
-rw-r--r--usr/src/libpiny/Piny/Environment.pm38
-rw-r--r--usr/src/libpiny/Piny/Group.pm105
-rw-r--r--usr/src/libpiny/Piny/Repo.pm155
-rw-r--r--usr/src/libpiny/Piny/User.pm148
6 files changed, 499 insertions, 0 deletions
diff --git a/usr/src/libpiny/Piny.pm b/usr/src/libpiny/Piny.pm
new file mode 100644
index 0000000..d4e3b27
--- /dev/null
+++ b/usr/src/libpiny/Piny.pm
@@ -0,0 +1,11 @@
+# This very pointedly does not have a package line.
+# That way, you can "use Piny" and import all the Piny packages into your namespace in one fell swoop.
+# Of course, you don't get to pass arguments to their import functions anymore.
+
+use Piny::Email;
+use Piny::Environment;
+use Piny::Group;
+use Piny::Repo;
+use Piny::User;
+
+1;
diff --git a/usr/src/libpiny/Piny/Email.pm b/usr/src/libpiny/Piny/Email.pm
new file mode 100644
index 0000000..c6c3d96
--- /dev/null
+++ b/usr/src/libpiny/Piny/Email.pm
@@ -0,0 +1,42 @@
+package Piny::Email;
+
+use Moose;
+use Moose::Util::TypeConstraints;
+
+use Email::Valid::Loose;
+
+# Types
+
+my $checker = Email::Valid::Loose->new("-fqdn" => 1, "-fudge" => 0, "-local_rules" => 0, "-mxcheck" => 1, "-tldcheck" => 0 );
+
+subtype 'EmailAddress'
+ => as 'Str'
+ => where { $checker->address( $_ ) }
+ => message { 'That does not appear to be a valid email address.' }
+ ;
+
+# Attributes
+
+has 'address' =>
+ ( is => 'rw'
+ , isa => 'EmailAddress'
+ );
+
+# Builder methods
+
+# If constructed with just one argument, then treat it as an address.
+around BUILDARGS => sub {
+ my ( $orig, $class ) = ( shift, shift );
+
+ if ( @_ == 1 && ! ref $_[0] ) {
+ return $class->$orig( address => $_[0] );
+ } else {
+ return $class->$orig( @_ );
+ };
+};
+
+# Moose boilerplate
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/usr/src/libpiny/Piny/Environment.pm b/usr/src/libpiny/Piny/Environment.pm
new file mode 100644
index 0000000..30df62c
--- /dev/null
+++ b/usr/src/libpiny/Piny/Environment.pm
@@ -0,0 +1,38 @@
+package Piny::Environment;
+
+use Moose;
+
+use Piny::User;
+
+# Attributes
+
+has 'user' =>
+ ( is => 'ro'
+ , isa => 'Piny::User'
+ , lazy_build => 1
+ , init_arg => undef
+ );
+
+# Builder methods
+
+sub _build_user {
+ my ( $s ) = @_;
+
+ if ( defined $ENV{"SUDO_UID"} ) {
+ return Piny::User->new( uid => $ENV{"SUDO_UID"} );
+ } elsif ( defined $ENV{"SUDO_USER"} ) {
+ return Piny::User->new( username => $ENV{"SUDO_USER"} );
+ } elsif ( defined $ENV{"UID"} ) {
+ return Piny::User->new( uid => $ENV{"UID"} );
+ } elsif ( defined $ENV{"USER"} ) {
+ return Piny::User->new( username => $ENV{"USERNAME"} );
+ } else {
+ return Piny::User->new( uid => $< );
+ };
+};
+
+# Moose boilerplate
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/usr/src/libpiny/Piny/Group.pm b/usr/src/libpiny/Piny/Group.pm
new file mode 100644
index 0000000..8e0c2aa
--- /dev/null
+++ b/usr/src/libpiny/Piny/Group.pm
@@ -0,0 +1,105 @@
+package Piny::Group;
+
+use Moose;
+
+use Piny::User;
+
+# Attributes
+
+has 'gid' =>
+ ( is => 'ro'
+ , isa => 'Int'
+ , lazy_build => 1
+ );
+
+has 'groupname' =>
+ ( is => 'ro'
+ , isa => 'Str'
+ , lazy_build => 1
+ );
+
+has 'grent' =>
+ ( is => 'ro'
+ , isa => 'ArrayRef'
+ , lazy_build => 1
+ , init_arg => undef
+ );
+
+has 'members' =>
+ ( is => 'ro'
+ , isa => 'ArrayRef[Piny::User]'
+ , lazy_build => 1
+ , init_arg => undef
+ );
+
+# 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( gid => $_[0] );
+ } else {
+ return $class->$orig( groupname => $_[0] );
+ };
+ } else {
+ return $class->$orig( @_ );
+ };
+};
+
+sub BUILD {
+ my ( $s ) = @_;
+
+ if ( not ( $s->has_gid( ) or $s->has_groupname( ) ) ) {
+ die "You must provide either GID or groupname!\n";
+ };
+
+ if ( $s->has_gid( ) and $s->has_groupname( ) ) {
+ die "You must not provide both GID and groupname!\n";
+ };
+};
+
+sub _build_gid {
+ my ( $s ) = @_;
+
+ return $s->grent( )->[2];
+};
+
+sub _build_groupname {
+ my ( $s ) = @_;
+
+ return $s->grent( )->[0];
+};
+
+sub _build_grent {
+ my ( $s ) = @_;
+
+ if ( $s->has_gid( ) ) {
+ my @res = getgrgid( $s->gid( ) );
+ die "getgrgid( " . $s->gid( ) . " ) failed: $!\n" unless @res;
+ return \@res;
+ } elsif ( $s->has_groupname( ) ) {
+ my @res = getgrnam( $s->groupname( ) );
+ die "getgrnam( " . $s->groupname( ) . " ) failed: $!\n" unless @res;
+ return \@res;
+ } else {
+ die "Not enough information provided to lookup group!\n";
+ };
+};
+
+sub _build_members {
+ my ( $s ) = @_;
+
+ return [ ] if ( $s->grent( )->[3] eq "" );
+ return [ map { Piny::User->new( username => $_ ) } split( /:/, $s->grent( )->[3] ) ];
+};
+
+# Moose boilerplate
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/usr/src/libpiny/Piny/Repo.pm b/usr/src/libpiny/Piny/Repo.pm
new file mode 100644
index 0000000..51034dd
--- /dev/null
+++ b/usr/src/libpiny/Piny/Repo.pm
@@ -0,0 +1,155 @@
+package Piny::Repo;
+
+use Moose;
+
+use Piny::User;
+
+# Attributes
+
+has 'name' =>
+ ( is => 'rw'
+ , isa => 'Str'
+ , trigger => \&_rename_repo
+ , required => 1
+ );
+
+has 'path' =>
+ ( is => 'ro'
+ , isa => 'Str'
+ , lazy_build => 1
+ , init_arg => undef
+ );
+
+has 'description' =>
+ ( is => 'rw'
+ , isa => 'Str'
+ , trigger => \&_set_description
+ , lazy_build => 1
+ , init_arg => undef
+ );
+
+has 'repostat' =>
+ ( is => 'ro'
+ , isa => 'ArrayRef'
+ , lazy_build => 1
+ , init_arg => undef
+ );
+
+has 'owner' =>
+ ( is => 'ro'
+ , isa => 'Piny::User'
+ , lazy_build => 1
+ , init_arg => undef
+ );
+
+has 'globally_readable' =>
+ ( is => 'ro'
+ , isa => 'Bool'
+ , lazy_build => 1
+ , init_arg => undef
+ );
+
+has 'globally_writable' =>
+ ( is => 'ro'
+ , isa => 'Bool'
+ , lazy_build => 1
+ , init_arg => undef
+ );
+
+# Triggers
+
+sub _rename_repo {
+ my ( $s, $new_name, $old_name ) = @_;
+
+ return unless defined $old_name;
+
+ my $olddir = "/srv/git/$old_name.git";
+ my $newdir = "/srv/git/$new_name.git";
+
+ rename( $olddir, $newdir ) or die "Couldn't rename $olddir to $newdir: $!\n";
+
+ $s->clear_path( );
+};
+
+sub _set_description {
+ my ( $s, $new_description, $old_description ) = @_;
+
+ return unless defined $old_description;
+
+ open( my $fd, ">", $s->path( ) . "/description" ) or die "Unable to open " . $s->path( ) . "/description for writing: $!\n";
+ print $fd $new_description;
+ close( $fd ) or die "Error when closing " . $s->path( ) . "/description: $!\n";
+};
+
+# Builder methods
+
+# If constructed with just one argument, then treat it as a repo name.
+around BUILDARGS => sub {
+ my ( $orig, $class ) = ( shift, shift );
+
+ if ( @_ == 1 && ! ref $_[0] ) {
+ return $class->$orig( name => $_[0] );
+ } else {
+ return $class->$orig( @_ );
+ };
+};
+
+sub _build_path {
+ my ( $s ) = @_;
+
+ my $dir = "/srv/git/" . $s->name( ) . ".git";
+
+ if ( -d $dir ) {
+ return $dir;
+ } else {
+ die "Expected repo $dir does not exist!\n";
+ };
+};
+
+sub _build_description {
+ my ( $s ) = @_;
+
+ open( my $d, "<", $s->path( ) . "/description" ) or die "Unable to open " . $s->path( ) . "/description: $!\n";
+ my $desc;
+ {
+ local $/ = undef;
+ $desc = <$d>;
+ };
+ close( $d );
+
+ return $desc;
+};
+
+sub _build_repostat {
+ my ( $s ) = @_;
+
+ my @res = stat( $s->path( ) );
+ die "stat( " . $s->path( ) . " ) failed: $!\n" unless @res;
+ return \@res;
+};
+
+sub _build_owner {
+ my ( $s ) = @_;
+
+ my ( $uid ) = $s->repostat( )->[4];
+
+ return Piny::User->new( uid => $uid );
+};
+
+sub _build_globally_readable {
+ my ( $s ) = @_;
+
+ return ( $s->repostat( )->[2] & 0444 ) == 0444;
+};
+
+sub _build_globally_writable {
+ my ( $s ) = @_;
+
+ return ( $s->repostat( )->[2] & 0111 ) == 0111;
+};
+
+# Moose boilerplate
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/usr/src/libpiny/Piny/User.pm b/usr/src/libpiny/Piny/User.pm
new file mode 100644
index 0000000..bba859f
--- /dev/null
+++ b/usr/src/libpiny/Piny/User.pm
@@ -0,0 +1,148 @@
+package Piny::User;
+
+use Moose;
+
+use Piny::Email;
+use Piny::Group;
+
+# Attributes
+
+has 'uid' =>
+ ( is => 'ro'
+ , isa => 'Int'
+ , lazy_build => 1
+ );
+
+has 'username' =>
+ ( is => 'ro'
+ , isa => 'Str'
+ , 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
+ );
+
+# 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!\n";
+ };
+
+ if ( $s->has_uid( ) and $s->has_username( ) ) {
+ die "You must not provide both UID and username!\n";
+ };
+};
+
+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: $!\n" unless @res;
+ return \@res;
+ } elsif ( $s->has_username( ) ) {
+ my @res = getpwnam( $s->username( ) );
+ die "getpwnam( " . $s->username( ) . " ) failed: $!\n" unless @res;
+ return \@res;
+ } else {
+ die "Not enough information provided to lookup user!\n";
+ };
+};
+
+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;