[Bps-public-commit] storage-box branch, master, updated. 7a3e76537dda8fc29a6eba3d7251305e56c24608

Dave Goehrig dave at bestpractical.com
Thu Sep 1 13:14:09 EDT 2016


The branch, master has been updated
       via  7a3e76537dda8fc29a6eba3d7251305e56c24608 (commit)
       via  da0519c18509939412b7def70de167786bb1db2c (commit)
      from  eecf8c1b68bf18b3c1b61109178f3fc669f4753f (commit)

Summary of changes:
 Build.PL                   |   2 +-
 META.json                  |  11 ++-
 README.md                  |  36 +++++++-
 cpanfile                   |   4 +-
 lib/Storage/Box.pm         | 201 ++++++++++++++++++++++++++++++++++++++++++++-
 lib/Storage/Box/Auth.pm    | 171 +++++++++++++++++++++-----------------
 lib/Storage/Box/File.pm    | 108 ++++++++++++++++++++++++
 lib/Storage/Box/Folder.pm  | 108 ++++++++++++++++++++++++
 lib/Storage/Box/Request.pm | 136 ++++++++++++++++++++++++++++++
 lib/Storage/Box/User.pm    | 165 +++++++++++++++++++++++++++++++++++++
 scripts/auth_enterprise.pl |  18 ++++
 scripts/auth_user.pl       |  18 ++++
 scripts/create_file.pl     |  28 +++++++
 scripts/create_user.pl     |  30 +++++++
 scripts/delete_file.pl     |  26 ++++++
 scripts/delete_user.pl     |  28 +++++++
 scripts/read_user.pl       |  28 +++++++
 scripts/update_user.pl     |  28 +++++++
 18 files changed, 1062 insertions(+), 84 deletions(-)
 create mode 100644 lib/Storage/Box/File.pm
 create mode 100644 lib/Storage/Box/Folder.pm
 create mode 100644 lib/Storage/Box/Request.pm
 create mode 100644 lib/Storage/Box/User.pm
 create mode 100644 scripts/auth_enterprise.pl
 create mode 100644 scripts/auth_user.pl
 create mode 100644 scripts/create_file.pl
 create mode 100644 scripts/create_user.pl
 create mode 100644 scripts/delete_file.pl
 create mode 100644 scripts/delete_user.pl
 create mode 100644 scripts/read_user.pl
 create mode 100644 scripts/update_user.pl

- Log -----------------------------------------------------------------
commit da0519c18509939412b7def70de167786bb1db2c
Author: Dave Goehrig <dave at bestpractical.com>
Date:   Thu Aug 25 16:18:19 2016 +0000

    manage user objects

diff --git a/META.json b/META.json
index c539095..4acac45 100644
--- a/META.json
+++ b/META.json
@@ -38,7 +38,9 @@
       "runtime" : {
          "requires" : {
             "Crypt::JWT" : "0.017",
+            "Data::UUID" : "1.221",
             "Expect" : "1.15",
+            "HTTP::Request" : "6.11",
             "Modern::Perl" : "1.20150127",
             "perl" : "5.008005"
          }
@@ -50,6 +52,17 @@
       }
    },
    "release_status" : "stable",
+   "resources" : {
+      "bugtracker" : {
+         "web" : "https://github.com/cthulhuology/Storage-Box/issues"
+      },
+      "homepage" : "https://github.com/cthulhuology/Storage-Box",
+      "repository" : {
+         "type" : "git",
+         "url" : "https://github.com/cthulhuology/Storage-Box.git",
+         "web" : "https://github.com/cthulhuology/Storage-Box"
+      }
+   },
    "version" : "0.01",
    "x_contributors" : [
       "Dave Goehrig <dave at bestpractical.com>"
diff --git a/README.md b/README.md
index 856bf39..7b26244 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,8 @@ Storage::Box - Blah blah blah
 
 Storage::Box is 
 
+# METHODS
+
 # AUTHOR
 
 Dave Goehrig <dave at dloh.org>
diff --git a/auth_enterprise.pl b/auth_enterprise.pl
new file mode 100644
index 0000000..ed99e77
--- /dev/null
+++ b/auth_enterprise.pl
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+
+use lib 'lib';
+
+use Storage::Box::Auth;
+
+my $enterprise_id = $ARGV[0] || '2064336';
+print "Authorizing $enterprise_id\n";
+
+my $jwt = Storage::Box::Auth::enterprise(
+	"vmqys6db",
+	"keys/private_key.pem",
+	"test",
+	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	"$enterprise_id");
+
+my $res =  Storage::Box::Auth::request(
+	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	"xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3",
+	$jwt);
+
+print $res->{access_token}, "\n";
diff --git a/auth_user.pl b/auth_user.pl
new file mode 100644
index 0000000..cc9882d
--- /dev/null
+++ b/auth_user.pl
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+#
+
+use lib 'lib';
+
+use Storage::Box::Auth;
+use Data::Dumper;
+
+print "Authorizing $ARGV[0]\n";
+
+my $jwt = Storage::Box::Auth::user(
+	"vmqys6db",
+	"keys/private_key.pem",
+	"test",
+	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	"$ARGV[0]");
+
+my $res =  Storage::Box::Auth::request(
+	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	"xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3",
+	$jwt);
+
+print $res->{access_token}, "\n";
diff --git a/cpanfile b/cpanfile
index 909e1f0..19ad9ad 100644
--- a/cpanfile
+++ b/cpanfile
@@ -5,6 +5,8 @@ requires 'Crypt::JWT', '0.017';
 requires 'Expect', '1.15';
 requires 'Data::UUID', '1.221';
 requires 'HTTP::Request', '6.11';
+requires 'Crypt::OpenSSL::RSA', '0.28';
+requires 'Crypt::PK::RSA';
 
 on test => sub {
     requires 'Test::More', '0.96';
diff --git a/create_user.pl b/create_user.pl
new file mode 100644
index 0000000..65e1874
--- /dev/null
+++ b/create_user.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+
+use lib 'lib';
+
+use Storage::Box::Auth;
+use Storage::Box::User;
+use Data::Dumper;
+
+my ($username,$enterprise_id) = @ARGV;
+$enterprise_id ||= 2064336;
+
+my $jwt = Storage::Box::Auth::enterprise(
+	"vmqys6db",
+	"keys/private_key.pem",
+	"test",
+	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	"$enterprise_id");
+
+my $res =  Storage::Box::Auth::request(
+	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	"xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3",
+	$jwt);
+
+my $token = $res->{access_token};
+
+my $user = Storage::Box::User::create($token,$username);
+
+print $user->{id}, "\n";
diff --git a/delete_user.pl b/delete_user.pl
new file mode 100644
index 0000000..ce483e1
--- /dev/null
+++ b/delete_user.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+
+use lib 'lib';
+
+use Storage::Box::Auth;
+use Storage::Box::User;
+use Data::Dumper;
+
+my ($user_id,$enterprise_id) = @ARGV;
+$enterprise_id ||= 2064336;
+
+my $jwt = Storage::Box::Auth::enterprise(
+	"vmqys6db",
+	"keys/private_key.pem",
+	"test",
+	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	"$enterprise_id");
+
+my $res =  Storage::Box::Auth::request(
+	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	"xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3",
+	$jwt);
+
+my $token = $res->{access_token};
+
+my $response =  Storage::Box::User::delete($token,$user_id);
+
+print $response ? "ok\n": "failed\n";
diff --git a/lib/Storage/Box/Auth.pm b/lib/Storage/Box/Auth.pm
index 70b9b99..944971d 100644
--- a/lib/Storage/Box/Auth.pm
+++ b/lib/Storage/Box/Auth.pm
@@ -31,6 +31,8 @@ use Expect;
 use Data::UUID;
 use HTTP::Request;
 use LWP::UserAgent;
+use Crypt::PK::RSA;
+use JSON qw/ decode_json /;
 
 =pod
 
@@ -117,15 +119,14 @@ B<private_key($keyfile)>
 =cut
 
 sub private_key {
-    my ($keyfile) = @_;
-    open my $fh, "< $keyfile" or die "Failed to open $keyfile\n";
-    local $/ = undef;
-    Crypt::OpenSSL::RSA->new_private_key(<$fh>);
+    my ($keyfile,$password) = @_;
+        print "opening $keyfile with $password\n";
+    Crypt::PK::RSA->new($keyfile,$password);
 }
 
 =pod
 
-B<enterprise($password,$kid,$keyfile,$clientid,$entperpriseid)>
+B<enterprise($kid,$keyfile,$password,$clientid,$entperpriseid)>
 
     Creates a JWT assertion for an enterprise account.
 
@@ -138,22 +139,23 @@ B<enterprise($password,$kid,$keyfile,$clientid,$entperpriseid)>
 =cut
 
 sub enterprise {
-    my ($password,$kid,$keyfile,$clientid,$entperpriseid) = @_;
+    my ($kid,$keyfile,$password,$clientid,$enterpriseid) = @_;
     my $ug = Data::UUID->new;
     my $jti = $ug->to_b64string($ug->create);
+    my $time = time;
     my %claims = (
         iss => $clientid,
         sub => $enterpriseid,
         box_sub_type => "enterprise",
         aud => "https://api.box.com/oauth2/token",
+        exp => $time + 60,
+        iat => $time,
         jti => $jti
     );
     Crypt::JWT::encode_jwt( 
         alg => "RS256",
         payload => \%claims,
-        auto_iat => 1,
-        relative_exp =>1,
-        key => private_key($keyfile),
+        key => $keyfile,
         keypass => $password,
         extra_headers =>  { kid => $kid },
     );
@@ -174,22 +176,23 @@ B<user($password,$kid,$keyfile,$clientid,$userid)>
 =cut
 
 sub user {
-    my ($password,$kid,$keyfile,$clientid,$entperpriseid) = @_;
+    my ($kid,$keyfile,$password,$clientid,$userid) = @_;
     my $ug = Data::UUID->new;
     my $jti = $ug->to_b64string($ug->create);
+    my $time = time;
     my %claims = (
         iss => $clientid,
         sub => $userid,
         box_sub_type => "user",
         aud => "https://api.box.com/oauth2/token",
+        exp => $time + 60,
+        iat => $time,
         jti => $jti
     );
     Crypt::JWT::encode_jwt( 
         alg => "RS256",
         payload => \%claims,
-        auto_iat => 1,
-        relative_exp =>1,
-        key => private_key($keyfile),
+        key => $keyfile,
         keypass => $password,
         extra_headers =>  { kid => $kid },
     );
@@ -214,8 +217,8 @@ sub request {
         "client_secret=$secret"
     );
     my $ua = LWP::UserAgent->new;
-    my $resp = $ua->request($req);
-    
+    my $response = $ua->request($req);
+    decode_json $response->content;
 }
 
 
diff --git a/lib/Storage/Box/File.pm b/lib/Storage/Box/File.pm
new file mode 100644
index 0000000..7f13ed0
--- /dev/null
+++ b/lib/Storage/Box/File.pm
@@ -0,0 +1,72 @@
+# vim: ai ts=4 sts=4 et sw=4 ft=perl
+
+package Storage::Box::File;
+
+=pod
+
+=head1 NAME
+
+Storage::Box::File -- manages Box.com File resources
+
+=head1 SYNOPSIS
+
+  my $file = Storage::Box::File::create($user_token,$name,$parent,$data)
+  
+
+=head1 DESCRIPTION
+
+This package allows you to manage Box.com Files.
+
+=cut
+
+use Modern::Perl;
+use HTTP::Request;
+use LWP::UserAgent;
+use JSON qw/ encode_json decode_json /;
+use Data::Dumper;
+
+=pod
+
+=head1 METHODS
+
+B<create($user_token,$name,$parent,$data)>
+
+  Creates a new File with the given name in the specified parent Folder.
+
+=cut 
+
+sub create {
+    my ($token,$name,$parent,$data) = @_;
+    my $request = HTTP::Request->new( POST => "https://api.box.com/2.0/files/content" );
+    $request->header( Authorization => "Bearer $token" );
+    $request->content <<THERE;
+attributes='{"name":"$name","parent":{"id":"$parent"}}'&
+THERE
+
+    my $ua = LWP::UserAgent->new;
+    my $response = $ua->request($request);
+    print Dumper $response;
+    decode_json($response->content); 
+}
+
+=pod
+
+=head1 TO DO
+
+stuff
+
+=head1 BUGS
+
+lots
+
+=head1 COPYRIGHT
+
+Best Practical LLC.
+
+=head1 AUTHORS
+
+Dave Goehrig <dave at dloh.org>
+
+=cut
+
+1;
diff --git a/lib/Storage/Box/User.pm b/lib/Storage/Box/User.pm
new file mode 100644
index 0000000..960a1c7
--- /dev/null
+++ b/lib/Storage/Box/User.pm
@@ -0,0 +1,132 @@
+# vim: ai ts=4 sts=4 et sw=4 ft=perl
+
+package Storage::Box::User;
+
+=pod
+
+=head1 NAME
+
+Storage::Box::User -- manages Box.com App User resources
+
+=head1 SYNOPSIS
+
+  my $user = Storage::Box::User::create($enterprise_token,$name)
+  
+
+=head1 DESCRIPTION
+
+This package allows you to manage Box.com App Users.
+
+=cut
+
+use Modern::Perl;
+use HTTP::Request;
+use LWP::UserAgent;
+use JSON qw/ encode_json decode_json /;
+use Data::Dumper;
+
+=pod
+
+=head1 METHODS
+
+B<create($enterprise_token,$username)>
+
+  Creates a new App User, requires an enterprise OAuth token
+
+=cut 
+
+sub create {
+    my ($token,$username) = @_;
+    my $request = HTTP::Request->new( POST => "https://api.box.com/2.0/users" );
+    $request->header( Authorization => "Bearer $token" );
+    $request->content( encode_json({ name => $username, is_platform_access_only => JSON::true }));
+    my $response = LWP::UserAgent->new->request($request);
+    decode_json($response->content) if $response->code == 201; 
+}
+
+=pod
+
+B<read($enterprise_token,$user_id)>
+
+    Reads the App User metadata for the given user_id
+
+=cut
+
+sub read {
+    my ($token,$id) = @_;
+    my $request = HTTP::Request->new( GET => "https://api.box.com/2.0/users/$id" );
+    $request->header( Authorization => "Bearer $token" );
+    my $response = LWP::UserAgent->new->request($request);
+    decode_json($response->content) if $response->code == 200;
+}
+
+=pod
+
+B<update($enterprise_token,$user_id,$options)>
+
+    Updates the App User resource for the updateable fields only.
+
+    * $enterprise_token = OAuth2 token for the enterprise
+    * $user_id = id of the user resource to update
+    * $options = a hashref containing the fields to update:
+        * name
+        * language
+        * job_title
+        * phone
+        * address
+        * status
+        * timezone
+        * space_ammount
+
+=cut
+
+sub update {
+    my %data = ();
+    my ($token,$id,$options) = @_;
+    my @attributes = qw/ name language job_title phone address status timezone space_amount /;
+    @data{@attributes} = @$options{ @attributes };
+    my $request = HTTP::Request->new( PUT => "https://api.box.com/2.0/users/$id" );
+    $request->header( Authorization => "Bearer $token" );
+    $request->content( encode_json( \%data ));
+    my $response = LWP::UserAgent->new->request($request);
+    decode_json($response->content) if $response->code == 200 || $response->code == 204;
+}
+
+
+=pod
+
+B<delete($enterprise_token,$user_id)>
+
+    Deletes the App User specified by the given user_id
+
+=cut
+
+sub delete {
+    my ($token,$id) = @_;
+    my $request = HTTP::Request->new( DELETE => "https://api.box.com/2.0/users/$id" );
+    $request->header( Authorization => "Bearer $token" );
+    my $response = LWP::UserAgent->new->request($request);
+    $response->code == 204 || $response->code == 404;   # if we don't find it, it has been deleted
+}
+
+=pod
+
+=head1 TO DO
+
+stuff
+
+=head1 BUGS
+
+lots
+
+=head1 COPYRIGHT
+
+Best Practical LLC.
+
+=head1 AUTHORS
+
+Dave Goehrig <dave at dloh.org>
+
+=cut
+
+1;
diff --git a/read_user.pl b/read_user.pl
new file mode 100644
index 0000000..f404237
--- /dev/null
+++ b/read_user.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+
+use lib 'lib';
+
+use Storage::Box::Auth;
+use Storage::Box::User;
+use Data::Dumper;
+
+my ($user_id,$enterprise_id) = @ARGV;
+$enterprise_id ||= 2064336;
+
+my $jwt = Storage::Box::Auth::enterprise(
+	"vmqys6db",
+	"keys/private_key.pem",
+	"test",
+	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	"$enterprise_id");
+
+my $res =  Storage::Box::Auth::request(
+	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	"xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3",
+	$jwt);
+
+my $token = $res->{access_token};
+
+print Dumper Storage::Box::User::read($token,$user_id);
diff --git a/update_user.pl b/update_user.pl
new file mode 100644
index 0000000..e85f0dc
--- /dev/null
+++ b/update_user.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+
+use lib 'lib';
+
+use Storage::Box::Auth;
+use Storage::Box::User;
+use Data::Dumper;
+
+my ($user_id,$enterprise_id,%options) = @ARGV;
+$enterprise_id ||= 2064336;
+
+my $jwt = Storage::Box::Auth::enterprise(
+	"vmqys6db",
+	"keys/private_key.pem",
+	"test",
+	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	"$enterprise_id");
+
+my $res =  Storage::Box::Auth::request(
+	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	"xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3",
+	$jwt);
+
+my $token = $res->{access_token};
+
+print Dumper Storage::Box::User::update($token,$user_id,\%options);

commit 7a3e76537dda8fc29a6eba3d7251305e56c24608
Author: Dave Goehrig <dave at bestpractical.com>
Date:   Thu Sep 1 13:14:02 2016 -0400

    create functional interface for external storage use
    
    Add JSON as a dependency
    nomalize the http interface on curl
    fixing dependencies

diff --git a/Build.PL b/Build.PL
index 0cc48bb..70ec54d 100644
--- a/Build.PL
+++ b/Build.PL
@@ -1,4 +1,4 @@
-# This Build.PL for Storage-Box was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.015.
+# This Build.PL for storage-box was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.015.
 use strict;
 use warnings;
 
diff --git a/META.json b/META.json
index 4acac45..0138c8f 100644
--- a/META.json
+++ b/META.json
@@ -1,5 +1,5 @@
 {
-   "abstract" : "Blah blah blah",
+   "abstract" : "a module for managing storage at Box.com",
    "author" : [
       "Dave Goehrig <dave at dloh.org>"
    ],
@@ -12,7 +12,7 @@
       "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
       "version" : 2
    },
-   "name" : "Storage-Box",
+   "name" : "storage-box",
    "no_index" : {
       "directory" : [
          "eg",
@@ -40,31 +40,23 @@
             "Crypt::JWT" : "0.017",
             "Data::UUID" : "1.221",
             "Expect" : "1.15",
-            "HTTP::Request" : "6.11",
+            "JSON" : "2.90",
             "Modern::Perl" : "1.20150127",
+            "WWW::Curl" : "4.17",
             "perl" : "5.008005"
          }
       },
       "test" : {
          "requires" : {
-            "Test::More" : "0.96"
+            "Test::More" : "0.96",
+            "Test::Pod" : "0"
          }
       }
    },
    "release_status" : "stable",
-   "resources" : {
-      "bugtracker" : {
-         "web" : "https://github.com/cthulhuology/Storage-Box/issues"
-      },
-      "homepage" : "https://github.com/cthulhuology/Storage-Box",
-      "repository" : {
-         "type" : "git",
-         "url" : "https://github.com/cthulhuology/Storage-Box.git",
-         "web" : "https://github.com/cthulhuology/Storage-Box"
-      }
-   },
    "version" : "0.01",
    "x_contributors" : [
+      "Dave Goehrig <=>",
       "Dave Goehrig <dave at bestpractical.com>"
    ],
    "x_serialization_backend" : "Cpanel::JSON::XS version 3.0217"
diff --git a/README.md b/README.md
index 7b26244..100afa4 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,50 @@
 # NAME
 
-Storage::Box - Blah blah blah
+Storage::Box - a module for managing storage at Box.com
 
 # SYNOPSIS
 
     use Storage::Box;
 
+    my $box = Storage::Box->new(
+        key_id => 'lasjfk',
+        enterprise_id => '1231923',
+        private_key => '/etc/box/keys/private_key.pem'
+        password => 'password',
+        client_id => '2lkjlkadsjfoiuawer',
+        client_secret => 'alksdjfoaiusrnre'
+    );
+
+    $box->create_user('bob');
+
 # DESCRIPTION
 
 Storage::Box is 
 
 # METHODS
 
+**create\_user($self,$name)**
+
+    Creates a new Box.com App user with the given username
+
+**read\_user($self,$user\_id)**
+
+    Reads a user object for the given user_id
+
+**update\_user($self,$user\_id,%options)**
+
+    Updates a user object specified by user_id with the given hash 
+    of key => values. Returns the updated user object.
+
+**delete\_user($self,$user\_id)**
+
+    Deletes the user associated with $user_id.  Returns true on success.
+
+**create\_file($self,$filename)**
+
 # AUTHOR
 
-Dave Goehrig <dave at dloh.org>
+Dave Goehrig <dave at dloh.org>
 
 # COPYRIGHT
 
diff --git a/auth_enterprise.pl b/auth_enterprise.pl
deleted file mode 100644
index ed99e77..0000000
--- a/auth_enterprise.pl
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env perl
-#
-
-use lib 'lib';
-
-use Storage::Box::Auth;
-
-my $enterprise_id = $ARGV[0] || '2064336';
-print "Authorizing $enterprise_id\n";
-
-my $jwt = Storage::Box::Auth::enterprise(
-	"vmqys6db",
-	"keys/private_key.pem",
-	"test",
-	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
-	"$enterprise_id");
-
-my $res =  Storage::Box::Auth::request(
-	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
-	"xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3",
-	$jwt);
-
-print $res->{access_token}, "\n";
diff --git a/auth_user.pl b/auth_user.pl
deleted file mode 100644
index cc9882d..0000000
--- a/auth_user.pl
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env perl
-#
-
-use lib 'lib';
-
-use Storage::Box::Auth;
-use Data::Dumper;
-
-print "Authorizing $ARGV[0]\n";
-
-my $jwt = Storage::Box::Auth::user(
-	"vmqys6db",
-	"keys/private_key.pem",
-	"test",
-	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
-	"$ARGV[0]");
-
-my $res =  Storage::Box::Auth::request(
-	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
-	"xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3",
-	$jwt);
-
-print $res->{access_token}, "\n";
diff --git a/cpanfile b/cpanfile
index 19ad9ad..df02af6 100644
--- a/cpanfile
+++ b/cpanfile
@@ -4,10 +4,10 @@ requires 'Modern::Perl', '1.20150127';
 requires 'Crypt::JWT', '0.017';
 requires 'Expect', '1.15';
 requires 'Data::UUID', '1.221';
-requires 'HTTP::Request', '6.11';
-requires 'Crypt::OpenSSL::RSA', '0.28';
-requires 'Crypt::PK::RSA';
+requires 'JSON', '2.90';
+requires 'WWW::Curl', '4.17';
 
 on test => sub {
     requires 'Test::More', '0.96';
+    requires 'Test::Pod';
 };
diff --git a/create_user.pl b/create_user.pl
deleted file mode 100644
index 65e1874..0000000
--- a/create_user.pl
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env perl
-
-use lib 'lib';
-
-use Storage::Box::Auth;
-use Storage::Box::User;
-use Data::Dumper;
-
-my ($username,$enterprise_id) = @ARGV;
-$enterprise_id ||= 2064336;
-
-my $jwt = Storage::Box::Auth::enterprise(
-	"vmqys6db",
-	"keys/private_key.pem",
-	"test",
-	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
-	"$enterprise_id");
-
-my $res =  Storage::Box::Auth::request(
-	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
-	"xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3",
-	$jwt);
-
-my $token = $res->{access_token};
-
-my $user = Storage::Box::User::create($token,$username);
-
-print $user->{id}, "\n";
diff --git a/delete_user.pl b/delete_user.pl
deleted file mode 100644
index ce483e1..0000000
--- a/delete_user.pl
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env perl
-
-use lib 'lib';
-
-use Storage::Box::Auth;
-use Storage::Box::User;
-use Data::Dumper;
-
-my ($user_id,$enterprise_id) = @ARGV;
-$enterprise_id ||= 2064336;
-
-my $jwt = Storage::Box::Auth::enterprise(
-	"vmqys6db",
-	"keys/private_key.pem",
-	"test",
-	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
-	"$enterprise_id");
-
-my $res =  Storage::Box::Auth::request(
-	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
-	"xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3",
-	$jwt);
-
-my $token = $res->{access_token};
-
-my $response =  Storage::Box::User::delete($token,$user_id);
-
-print $response ? "ok\n": "failed\n";
diff --git a/lib/Storage/Box.pm b/lib/Storage/Box.pm
index 89fbaaa..ae30b21 100644
--- a/lib/Storage/Box.pm
+++ b/lib/Storage/Box.pm
@@ -3,18 +3,36 @@
 package Storage::Box;
 
 use Modern::Perl;
+use Object::Simple -base;
+use Storage::Box::Auth;
+use Storage::Box::User;
+use Storage::Box::File;
+use Storage::Box::Folder;
+
 our $VERSION = '0.01';
+
 =pod 
 
 =encoding utf-8
 
 =head1 NAME
 
-Storage::Box - Blah blah blah
+Storage::Box - a module for managing storage at Box.com
 
 =head1 SYNOPSIS
 
-  use Storage::Box;
+    use Storage::Box;
+
+    my $box = Storage::Box->new(
+        key_id => 'lasjfk',
+        enterprise_id => '1231923',
+        private_key => '/etc/box/keys/private_key.pem'
+        password => 'password',
+        client_id => '2lkjlkadsjfoiuawer',
+        client_secret => 'alksdjfoaiusrnre'
+    );
+
+    $box->create_user('bob');
 
 =head1 DESCRIPTION
 
@@ -24,7 +42,186 @@ Storage::Box is
 
 =cut
 
+has key_id => '';
+has enterprise_id => '';
+has public_key => '';
+has password => '';
+has client_id => '';
+has client_secret => '';
+has user_id => '';
+has enterprise_auth => '';
+has user_auth => '';
+has user => '';
+
+sub authenticate_enterprise {
+    my $self = shift;    
+    $self->enterprise_auth('') if ($self->enterprise_auth and 
+        $self->enterprise_auth->expired);
+    unless ($self->enterprise_auth) {
+        $self->enterprise_auth( Storage::Box::Auth->new(
+            key_id => $self->key_id,
+            enterprise_id => $self->enterprise_id,
+            private_key => $self->private_key,
+            password => $self->password,
+            client_id => $self->client_id,
+            client_secret => $self->client_secret
+        ));
+        $self->enterprise_auth->request;
+    }
+    $self;
+}
+
+sub authenticate_user {
+    my $self = shift;    
+    $self->user_auth('') if ($self->user_auth and 
+        $self->user_auth->expired);
+    unless($self->user_auth) {
+        $self->user_auth( Storage::Box::Auth->new(
+            key_id => $self->key_id,
+            user_id => $self->user_id,
+            private_key => $self->private_key,
+            password => $self->password,
+            client_id => $self->client_id,
+            client_secret => $self->client_secret
+        ));
+        $self->user_auth->request;
+    }
+    $self;
+}
+
+=pod
+B<create_user($self,$name)>
+
+    Creates a new Box.com App user with the given username
+
+=cut
+
+sub create_user {
+    my ($self,$name) = @_;
+    $self->authenticate_enterprise;
+    my $user = Storage::Box::User->new(
+        auth => $self->enterprise_auth,
+        name => $name
+    );
+    $user->create;
+}
+
+=pod
+B<read_user($self,$user_id)>
+
+    Reads a user object for the given user_id
+
+=cut
+
+sub read_user {
+    my ($self,$user_id) = @_;
+    $self->authenticate_enterprise;
+    my $user = Storage::Box::User->new(
+        auth => $self->enterprise_auth,
+        id => $user_id
+    );
+    $user->read;
+}
+
+=pod
+B<update_user($self,$user_id,%options)>
+
+    Updates a user object specified by user_id with the given hash 
+    of key => values. Returns the updated user object.
+
+=cut
+
+sub update_user {
+    my ($self,$user_id,%options) = @_;
+    $self->authenticate_enterprise;
+    my $user = Storage::Box::User->new(
+        auth => $self->enterprise_auth,
+        id => $user_id
+    );
+    $user->update(%options);
+}
+
+=pod
+B<delete_user($self,$user_id)>
+
+    Deletes the user associated with $user_id.  Returns true on success.
+=cut
+
+sub delete_user {
+    my ($self,$user_id) = @_;
+    $self->authenticate_enterprise;
+    my $user = Storage::Box::User->new(
+        auth => $self->enterprise_auth,
+        id => $user_id
+    );
+    $user->delete;
+}
+
+=pod
+B<create_file($self,$filename)>
+
+=cut
 
+sub create_file {
+    my ($self,$filename) = @_;
+    $self->authenticate_user;
+    my $file = Storage::Box::File->new(
+        auth => $self->user_auth,
+        name => $filename
+    );
+    $file->create;
+    $file;
+}
+
+sub download_file {
+    my ($self,$file_id) = @_;
+    $self->authenticate_user;
+    my $file = Storage::Box::File->new(
+        auth => $self->user_auth,
+        id => $file_id
+    );
+    $file->download;
+}
+
+sub delete_file {
+    my ($self,$file_id) = @_;
+    $self->authenticate_user;
+    my $file = Storage::Box::File->new(
+        auth => $self->user_auth,
+        id => $file_id
+    );
+    $file->delete;
+}
+
+sub create_folder {
+    my ($self,$name) = @_;
+    $self->authenticate_user;
+    my $folder = Storage::Box::Folder->new(
+        auth => $self->user_auth,
+        name => $name
+    );
+    $folder->create;
+}
+
+sub list_folder {
+    my ($self,$folder_id) = @_;
+    $self->authenticate_user;
+    my $folder = Storage::Box::Folder->new(
+        auth => $self->user_auth,
+        folder_id => $folder_id
+    );
+    $folder->items;
+}
+
+sub delete_folder {
+    my ($self,$folder_id) = @_;
+    $self->authenticate_user;
+    my $folder = Storage::Box::Folder->new(
+        auth => $self->user_auth,
+        folder_id => $folder_id
+    );
+    $folder->delete;
+}
 
 =pod
 
diff --git a/lib/Storage/Box/Auth.pm b/lib/Storage/Box/Auth.pm
index 944971d..bbee131 100644
--- a/lib/Storage/Box/Auth.pm
+++ b/lib/Storage/Box/Auth.pm
@@ -1,6 +1,7 @@
 # vim: ai ts=4 sts=4 et sw=4 ft=perl
 
 package Storage::Box::Auth;
+use Object::Simple -base;
 
 =pod
 
@@ -29,37 +30,53 @@ use Modern::Perl;
 use Crypt::JWT;
 use Expect;
 use Data::UUID;
-use HTTP::Request;
-use LWP::UserAgent;
-use Crypt::PK::RSA;
+use WWW::Curl::Easy;
+use WWW::Curl::Form;
 use JSON qw/ decode_json /;
 
 =pod
 
 =head1 METHODS
 
-B<generate_private_key($password)>
+
+=cut
+
+has password => "";                     # password for private key
+has private_key => 'private_key.pem';
+has public_key => 'public_key.pem';
+has key_id => '';                       # id of public key supplied by box.com
+has client_id => '';                    # id of the application supplied by box.com
+has client_secret => '';                # secret of the application supplied by box.com
+has enterprise_id => '';                # id of the enterprise supplied by box.com
+has user_id => '';                      # id of the user supplied by box.com
+has jwt => '';                          # JSON Web Token generated by user or enterprise
+has token => '';                        # OAuth2 token generated by request
+has expires => 0;
+
+=pod 
+B<generate_private_key()>
 
   Using openssl, this generates a 2048 bit aes256 private key file
 
 =cut 
 
+
 sub generate_private_key {
-my ($password) = @_;
-    my $exp = Expect->spawn("openssl genrsa -aes256 -out private_key.pem 2048") 
-        or die "Failed to generate private_key.pem";
+    my $self = shift;
+    my $exp = Expect->spawn("openssl genrsa -aes256 -out " . $self->private_key . " 2048") 
+        or die "Failed to generate " . $self->private_key;
     $exp->raw_pty(1);
     $exp->expect(1,
         [ qr/private_key\.pem:/ => sub { 
-            $exp->send("$password\r"); exp_continue;
+            $exp->send($self->password . "\r"); exp_continue;
         } ]
     );
     $exp->soft_close();
-}
+};
 
 =pod
 
-B<generate_public_key($password)>
+B<generate_public_key()>
 
   Using openssl, outputs the public key associated with the private_key.pem.
   The password must be the password associated with the private key.
@@ -67,17 +84,17 @@ B<generate_public_key($password)>
 =cut
 
 sub generate_public_key {
-    my ($password) = @_;
-    my $exp = Expect->spawn("openssl rsa -pubout -in private_key.pem -out public_key.pem")
-        or die "Failed to generate public_key.pem";
+    my $self = shift;
+    my $exp = Expect->spawn("openssl rsa -pubout -in " . $self->private_key . " -out " . $self->public_key)
+        or die "Failed to generate " . $self->public_key;
     $exp->raw_pty(1);
     $exp->expect(1,
         [ qr/private_key\.pem:/ => sub { 
-            $exp->send("$password\r"); exp_continue;
+            $exp->send($self->password . "\r"); exp_continue;
         } ]
     );
     $exp->soft_close();
-}
+};
 
 =pod
 
@@ -89,9 +106,9 @@ B<generate_keys($password)>
 =cut
 
 sub generate_keys {
-    my ($password) = @_;
-    generate_private_key $password;
-    generate_public_key $password;
+    my ($self) = @_;
+    $self->segenerate_private_key;
+    $self->generate_public_key;
     print <<THERE;
 
 To install this key in box.com:
@@ -113,89 +130,77 @@ THERE
 
 =pod
 
-B<private_key($keyfile)>
-
-    Loads a private keyfile
-=cut
-
-sub private_key {
-    my ($keyfile,$password) = @_;
-        print "opening $keyfile with $password\n";
-    Crypt::PK::RSA->new($keyfile,$password);
-}
-
-=pod
-
-B<enterprise($kid,$keyfile,$password,$clientid,$entperpriseid)>
+B<enterprise($self)>
 
     Creates a JWT assertion for an enterprise account.
+    requires the following attributes be set:
 
-    * $password = password for the keyfile
-    * $kid = key id generated by Box.com
-    * $keyfile = path to the private keyfile
-    * $clientid = client id of the application creating the assertion
-    * $enterpriseid = token specific to an enterprise when creating and managing app users
+    * password = password for the keyfile
+    * key_id = key id generated by Box.com
+    * private_key = path to the private keyfile
+    * client_id = client id of the application creating the assertion
+    * enterprise_id = token specific to an enterprise when creating and managing app users
 
 =cut
 
 sub enterprise {
-    my ($kid,$keyfile,$password,$clientid,$enterpriseid) = @_;
+    my $self = shift;
     my $ug = Data::UUID->new;
     my $jti = $ug->to_b64string($ug->create);
     my $time = time;
     my %claims = (
-        iss => $clientid,
-        sub => $enterpriseid,
+        iss => $self->client_id,
+        sub => $self->enterprise_id,
         box_sub_type => "enterprise",
         aud => "https://api.box.com/oauth2/token",
         exp => $time + 60,
         iat => $time,
         jti => $jti
     );
-    Crypt::JWT::encode_jwt( 
+    $self->jwt(Crypt::JWT::encode_jwt( 
         alg => "RS256",
         payload => \%claims,
-        key => $keyfile,
-        keypass => $password,
-        extra_headers =>  { kid => $kid },
-    );
+        key => $self->private_key,
+        keypass => $self->password,
+        extra_headers =>  { kid => $self->key_id },
+    ));
 }
 
 =pod
 
-B<user($password,$kid,$keyfile,$clientid,$userid)>
+B<user($self)>
 
     Creates a JWT assertion for a user account.
 
-    * $password = password for the keyfile
-    * $kid = key id generated by Box.com
-    * $keyfile = path to the private keyfile
-    * $clientid = client id of the application creating the assertion
-    * $userid = app user_id for a token specific to an individual app user.
+    * password = password for the keyfile
+    * key_id = key id generated by Box.com
+    * private_key = path to the private keyfile
+    * client_id = client id of the application creating the assertion
+    * user_id = app user_id for a token specific to an individual app user.
 
 =cut
 
 sub user {
-    my ($kid,$keyfile,$password,$clientid,$userid) = @_;
+    my $self = shift;
     my $ug = Data::UUID->new;
     my $jti = $ug->to_b64string($ug->create);
     my $time = time;
     my %claims = (
-        iss => $clientid,
-        sub => $userid,
+        iss => $self->client_id,
+        sub => $self->user_id,
         box_sub_type => "user",
         aud => "https://api.box.com/oauth2/token",
         exp => $time + 60,
         iat => $time,
         jti => $jti
     );
-    Crypt::JWT::encode_jwt( 
+    $self->jwt(Crypt::JWT::encode_jwt( 
         alg => "RS256",
         payload => \%claims,
-        key => $keyfile,
-        keypass => $password,
-        extra_headers =>  { kid => $kid },
-    );
+        key => $self->private_key,
+        keypass => $self->password,
+        extra_headers =>  { kid => $self->key_id },
+    ));
 }
 
 =pod
@@ -207,21 +212,34 @@ B<request($client_id,$client_secret,$jwt)>
 =cut
 
 sub request {
-    my ($client_id,$secret,$jwt) = @_;
-    my $req = HTTP::Request->new(POST => "https://api.box.com/oauth2/token");
-    $req->header("Content-Type" => "application/x-www-form-urlencoded");
-    $req->content(
-        "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&" .
-        "assertion=$jwt&" .
-        "client_id=$client_id&" .
-        "client_secret=$secret"
-    );
-    my $ua = LWP::UserAgent->new;
-    my $response = $ua->request($req);
-    decode_json $response->content;
+    my $self = shift;
+    my $body;
+
+    my $form = "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&" .
+        "assertion=" . $self->jwt . "&" .
+        "client_id=" . $self->client_id . "&" .
+        "client_secret=" . $self->client_secret;
+
+    my $curl = WWW::Curl::Easy->new;
+    $curl->setopt(CURLOPT_URL,"https://api.box.com/oauth2/token");
+    $curl->setopt(CURLOPT_POST,1);
+    $curl->setopt(CURLOPT_HTTPHEADER, [
+        "Content-Type: application/x-www-form-urlencoded"
+    ]);
+    $curl->setopt(CURLOPT_FILE, \$body );
+    $curl->setopt(CURLOPT_POSTFIELDS,$form);
+    
+    return $self if ($curl->perform || $curl->getinfo(CURLINFO_RESPONSE_CODE) != 200 );
+    my $res = decode_json $body;
+    $self->expires(time + $res->{expires_in});
+    $self->token($res->{access_token});
+    $self;
 }
 
-
+sub expired {
+    my $self = time;
+    time >= $self->expires;
+}
 
 =pod
 
diff --git a/lib/Storage/Box/File.pm b/lib/Storage/Box/File.pm
index 7f13ed0..321ebf2 100644
--- a/lib/Storage/Box/File.pm
+++ b/lib/Storage/Box/File.pm
@@ -1,6 +1,7 @@
 # vim: ai ts=4 sts=4 et sw=4 ft=perl
 
 package Storage::Box::File;
+use Object::Simple -base;
 
 =pod
 
@@ -20,33 +21,68 @@ This package allows you to manage Box.com Files.
 =cut
 
 use Modern::Perl;
-use HTTP::Request;
-use LWP::UserAgent;
 use JSON qw/ encode_json decode_json /;
-use Data::Dumper;
+use Storage::Box::Request;
 
 =pod
 
 =head1 METHODS
 
-B<create($user_token,$name,$parent,$data)>
+=cut
+
+has id => '0';          # id of the file, supplied by box.com
+has name => '';         # name of the file
+has parent => '0';      # id of the parent folder, 0 is top level default
+has auth => '';         # Storage::Box::Auth object
+
+=pod
+B<create($user_token,$name,$parent,$filename)>
 
   Creates a new File with the given name in the specified parent Folder.
 
 =cut 
 
 sub create {
-    my ($token,$name,$parent,$data) = @_;
-    my $request = HTTP::Request->new( POST => "https://api.box.com/2.0/files/content" );
-    $request->header( Authorization => "Bearer $token" );
-    $request->content <<THERE;
-attributes='{"name":"$name","parent":{"id":"$parent"}}'&
-THERE
-
-    my $ua = LWP::UserAgent->new;
-    my $response = $ua->request($request);
-    print Dumper $response;
-    decode_json($response->content); 
+    my $self = shift;
+    my $req = Storage::Box::Request->new(
+        url => "https://upload.box.com/api/2.0/files/content",
+        auth => $self->auth
+    );
+    $req->field('attributes',"{\"name\":\"" . $self->name . "\",\"parent\":{\"id\":\"" . $self->parent . "\"}}");
+    $req->file($self->name);
+    $req->post->request;
+    return $self unless ($req->code == 201);
+    my $json = decode_json($req->body);
+    $self->id($json->{entries}[0]->{id});   # update our id
+    $self;
+}
+
+=pod
+
+B<download($token,$fileid)> 
+
+    Downloads a file
+
+=cut
+
+sub download {
+    my $self = shift;
+    my $req = Storage::Box::Request->new(
+        url => "https://api.box.com/2.0/files/" . $self->id . "/content",
+        auth => $self->auth
+    );
+    $req->get->perform;
+    $req->body;
+}
+ 
+sub delete {
+    my $self = shift;
+    my $req = Storage::Box::Request->new(
+        url => "https://api.box.com/2.0/files/" . $self->id,
+        auth => $self->auth
+    );
+    $req->delete->perform;
+    $req->code == 200 || $req->code == 404;
 }
 
 =pod
diff --git a/lib/Storage/Box/Folder.pm b/lib/Storage/Box/Folder.pm
new file mode 100644
index 0000000..4bb6b31
--- /dev/null
+++ b/lib/Storage/Box/Folder.pm
@@ -0,0 +1,108 @@
+# vim: ai ts=4 sts=4 et sw=4 ft=perl
+
+package Storage::Box::Folder;
+
+=pod
+
+=head1 NAME
+
+Storage::Box::Folder -- manages Box.com Folder resources
+
+=head1 SYNOPSIS
+
+	my $file = Storage::Box::Folder->new(
+
+	)
+  
+
+=head1 DESCRIPTION
+
+This package allows you to manage Box.com Files.
+
+=cut
+
+use Object::Simple -base;
+use Modern::Perl;
+use Storage::Box::Request;
+use JSON qw/ encode_json decode_json /;
+use Data::Dumper;
+
+=pod
+
+=head1 METHODS
+
+=cut
+
+has id => '0';          # id of the file, supplied by box.com
+has name => '';         # name of the file
+has parent => '0';      # id of the parent folder, 0 is top level default
+has auth => '';         # Storage::Box::Auth object
+
+=pod
+B<create($user_token,$name,$parent,$filename)>
+
+  Creates a new File with the given name in the specified parent Folder.
+
+=cut 
+
+sub create {
+    my $self = shift;
+    my $req = Storage::Box::Request->new(
+        url => "https://api.box.com/2.0/folders",
+        auth => $self->auth
+    );
+    $req->content("{\"name\":\"" . $self->name . "\",\"parent\":{\"id\":\"" . $self->parent . "\"}}");
+    $req->post->request;
+    my $json = decode_json  $req->body;
+    $self->id($json->{id});
+}
+
+
+sub items {
+    my $self = shift;
+    my $req = Storage::Box::Request->new(
+        url => "https://api.box.com/2.0/folders/" . $self->id . "/items",
+        auth => $self->auth
+    );
+    $req->get->request;
+    decode_json $req->body;
+}
+
+=pod
+
+B<delete()> 
+
+    Delete the folder
+
+=cut
+sub delete {
+    my $self = shift;
+    my $req = Storage::Box::Request->new(
+        url => "https://api.box.com/2.0/folders/" . $self->id,
+        auth => $self->auth
+    );
+    $req->delete->request;
+    $self;
+}
+
+=pod
+
+=head1 TO DO
+
+stuff
+
+=head1 BUGS
+
+lots
+
+=head1 COPYRIGHT
+
+Best Practical LLC.
+
+=head1 AUTHORS
+
+Dave Goehrig <dave at dloh.org>
+
+=cut
+
+1;
diff --git a/lib/Storage/Box/Request.pm b/lib/Storage/Box/Request.pm
new file mode 100644
index 0000000..7e8a711
--- /dev/null
+++ b/lib/Storage/Box/Request.pm
@@ -0,0 +1,136 @@
+# vim: ai ts=4 sts=4 et sw=4 ft=perl
+#
+package Storage::Box::Request;
+
+=pod
+
+=head1 NAME
+
+Storage::Box::Request
+
+=head1 SYNOPSIS
+
+	my $request = Storage::Box::Request->new(
+        url => "https://api.box.com",
+        auth => $auth
+    );
+
+=head1 DESCRIPTION
+
+=cut
+
+use Object::Simple -base;
+use Modern::Perl;
+use JSON qw/ encode_json decode_json /;
+use WWW::Curl::Easy;
+use WWW::Curl::Form;
+
+has url => '';
+has auth => '';
+has curl => '';
+has body => '';
+has form => '';
+has max_retries => 5;
+has retries => 0;
+has code => 200;
+
+sub get {
+    my $self = shift;
+    $self->curl( WWW::Curl::Easy->new() ) unless $self->curl;
+    $self;
+}
+
+sub post {
+    my $self = shift;
+    $self->curl( WWW::Curl::Easy->new() ) unless $self->curl;
+    $self->curl->setopt(CURLOPT_POST, 1);
+    $self;
+}
+
+sub put {
+    my $self = shift;
+    $self->curl( WWW::Curl::Easy->new() ) unless $self->curl;
+    $self->curl->setopt(CURLOPT_CUSTOMREQUEST,"PUT");
+    $self;
+}
+
+sub delete {
+    my $self = shift;
+    $self->curl( WWW::Curl::Easy->new() ) unless $self->curl;
+    $self->curl->setopt(CURLOPT_CUSTOMREQUEST,"DELETE");
+    $self;
+}
+
+sub request {
+    my $self = shift;
+    $self->curl( WWW::Curl::Easy->new() ) unless $self->curl;
+    my $headers = [
+        "Accept: */*",
+        "Authorization: Bearer " . $self->auth->token()
+    ];
+    my $body;
+    $self->curl->setopt(CURLOPT_FOLLOWLOCATION,1);
+    $self->curl->setopt(CURLOPT_HTTPHEADER,$headers);
+    $self->curl->setopt(CURLOPT_FILE, \$body );
+    $self->curl->setopt(CURLOPT_URL,$self->url);
+    $self->curl->setopt(CURLOPT_HTTPPOST,$self->form) if ($self->form ne ''); 
+    if ($self->curl->perform) {  # CURLE_OK is 0
+        if ( ++$self->retries < $self->max_retries) {
+            sleep 1;
+            return $self->request;
+        }
+        $self->retries(0);
+        $self->body('');    # fail with empty body
+        return $self;
+    }
+    $self->code($self->curl->getinfo(CURLINFO_RESPONSE_CODE));
+    $self->body($body);
+    $self;
+}
+
+sub field {
+    my $self = shift;
+    my ($key,$value) = @_;
+    $self->form( WWW::Curl::Form->new() ) unless $self->form;
+    $self->form->formadd($key,$value);
+    $self;
+}
+
+sub file {
+    my $self = shift;
+    my $filename = shift;
+    $self->form( WWW::Curl::Form->new() ) unless $self->form;
+    $self->form->formadd('attributes',"{\"name\":\"" . $self->name . "\",\"parent\":{\"id\":\"" . $self->parent . "\"}}");
+    $self->form->formaddfile($filename, 'attachment', "multipart/form-data");
+    $self;
+}
+
+sub content {
+    my $self = shift;
+    my $data = shift;
+    $self->curl( WWW::Curl::Easy->new() ) unless $self->curl;
+    $self->curl->setopt(CURLOPT_POSTFIELDS, $data );
+    $self;
+}
+
+=pod
+
+=head1 TO DO
+
+stuff
+
+=head1 BUGS
+
+lots
+
+=head1 COPYRIGHT
+
+Best Practical LLC.
+
+=head1 AUTHORS
+
+Dave Goehrig <dave at dloh.org>
+
+=cut
+
+1;
diff --git a/lib/Storage/Box/User.pm b/lib/Storage/Box/User.pm
index 960a1c7..6df16f3 100644
--- a/lib/Storage/Box/User.pm
+++ b/lib/Storage/Box/User.pm
@@ -1,6 +1,7 @@
 # vim: ai ts=4 sts=4 et sw=4 ft=perl
 
 package Storage::Box::User;
+use Object::Simple -base;
 
 =pod
 
@@ -10,7 +11,10 @@ Storage::Box::User -- manages Box.com App User resources
 
 =head1 SYNOPSIS
 
-  my $user = Storage::Box::User::create($enterprise_token,$name)
+    my $user = Storage::Box::User->new(
+        auth => $auth,
+        name => 'Dave'
+    )->create;
   
 
 =head1 DESCRIPTION
@@ -20,49 +24,73 @@ This package allows you to manage Box.com App Users.
 =cut
 
 use Modern::Perl;
-use HTTP::Request;
-use LWP::UserAgent;
 use JSON qw/ encode_json decode_json /;
-use Data::Dumper;
+use Storage::Box::Request;
 
 =pod
 
 =head1 METHODS
 
-B<create($enterprise_token,$username)>
 
-  Creates a new App User, requires an enterprise OAuth token
+=cut
+
+has id => '';
+has name => '';
+has auth => '';
+
+=pod
+
+B<create()>
+
+    Creates a new App User, requires:
+
+    * name = name of the user
+    * auth = an enterprise Storage::Box::Auth object
 
 =cut 
 
 sub create {
-    my ($token,$username) = @_;
-    my $request = HTTP::Request->new( POST => "https://api.box.com/2.0/users" );
-    $request->header( Authorization => "Bearer $token" );
-    $request->content( encode_json({ name => $username, is_platform_access_only => JSON::true }));
-    my $response = LWP::UserAgent->new->request($request);
-    decode_json($response->content) if $response->code == 201; 
+    my $self = shift;
+    my $req = Storage::Box::Request->new(
+        url => "https://api.box.com/2.0/users",
+        auth => $self->auth
+    );
+    $req->content( encode_json({ 
+        name => $self->name, 
+        is_platform_access_only => JSON::true 
+    }));
+    $req->post->request;
+    if ($req->code == 201) {
+        my $json = decode_json($req->body);
+        $self->id($json->{id});
+    }
+    $self;
 }
 
 =pod
 
 B<read($enterprise_token,$user_id)>
 
-    Reads the App User metadata for the given user_id
+    Reads the App User metadata for the user
+
+    * id = id of the user
+    * auth = an enterprise Storage::Box::Auth object
 
 =cut
 
 sub read {
-    my ($token,$id) = @_;
-    my $request = HTTP::Request->new( GET => "https://api.box.com/2.0/users/$id" );
-    $request->header( Authorization => "Bearer $token" );
-    my $response = LWP::UserAgent->new->request($request);
-    decode_json($response->content) if $response->code == 200;
+    my $self = shift;
+    my $req = Storage::Box::Request->new(
+        url => "https://api.box.com/2.0/users/" . $self->id,
+        auth => $self->auth 
+    );
+    $req->get->request;
+    $req->code == 200 ? decode_json($req->body) : {};
 }
 
 =pod
 
-B<update($enterprise_token,$user_id,$options)>
+B<update(%options)>
 
     Updates the App User resource for the updateable fields only.
 
@@ -82,31 +110,36 @@ B<update($enterprise_token,$user_id,$options)>
 
 sub update {
     my %data = ();
-    my ($token,$id,$options) = @_;
+    my $self = shift;
+    my (%options) = @_;
     my @attributes = qw/ name language job_title phone address status timezone space_amount /;
-    @data{@attributes} = @$options{ @attributes };
-    my $request = HTTP::Request->new( PUT => "https://api.box.com/2.0/users/$id" );
-    $request->header( Authorization => "Bearer $token" );
-    $request->content( encode_json( \%data ));
-    my $response = LWP::UserAgent->new->request($request);
-    decode_json($response->content) if $response->code == 200 || $response->code == 204;
+    @data{@attributes} = @options{ @attributes };
+    my $req = Storage::Box::Request->new(
+        url => "https://api.box.com/2.0/users/" . $self->id,
+        auth => $self->auth
+    );
+    $req->content( encode_json( \%data ));
+    $req->put->request;
+    $req->code == 200 ? decode_json($req->body) : {};
 }
 
 
 =pod
 
-B<delete($enterprise_token,$user_id)>
+B<delete()>
 
     Deletes the App User specified by the given user_id
 
 =cut
 
 sub delete {
-    my ($token,$id) = @_;
-    my $request = HTTP::Request->new( DELETE => "https://api.box.com/2.0/users/$id" );
-    $request->header( Authorization => "Bearer $token" );
-    my $response = LWP::UserAgent->new->request($request);
-    $response->code == 204 || $response->code == 404;   # if we don't find it, it has been deleted
+    my $self = shift;
+    my $req = Storage::Box::Request->new(
+        url => "https://api.box.com/2.0/users/" . $self->id,
+        auth => $self->auth
+    );
+    $req->delete->request;
+    $req->code == 204 || $req->code == 404;   # if we don't find it, it has been deleted
 }
 
 =pod
diff --git a/read_user.pl b/read_user.pl
deleted file mode 100644
index f404237..0000000
--- a/read_user.pl
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env perl
-
-use lib 'lib';
-
-use Storage::Box::Auth;
-use Storage::Box::User;
-use Data::Dumper;
-
-my ($user_id,$enterprise_id) = @ARGV;
-$enterprise_id ||= 2064336;
-
-my $jwt = Storage::Box::Auth::enterprise(
-	"vmqys6db",
-	"keys/private_key.pem",
-	"test",
-	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
-	"$enterprise_id");
-
-my $res =  Storage::Box::Auth::request(
-	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
-	"xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3",
-	$jwt);
-
-my $token = $res->{access_token};
-
-print Dumper Storage::Box::User::read($token,$user_id);
diff --git a/scripts/auth_enterprise.pl b/scripts/auth_enterprise.pl
new file mode 100644
index 0000000..a137882
--- /dev/null
+++ b/scripts/auth_enterprise.pl
@@ -0,0 +1,18 @@
+#!/usr/bin/env perl
+#
+
+use lib 'lib';
+
+use Storage::Box::Auth;
+
+my $auth = Storage::Box::Auth->new(
+	key_id => "vmqys6db",
+	enterprise_id => ($ARGV[0] || '2064336'),
+	public_key => "keys/public_key.pem",
+	private_key => "keys/private_key.pem",
+	password => "test",
+	client_id => "96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	client_secret => "xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3");
+
+$auth->enterprise->request;
+print $auth->token, "\n";
diff --git a/scripts/auth_user.pl b/scripts/auth_user.pl
new file mode 100644
index 0000000..caa42c0
--- /dev/null
+++ b/scripts/auth_user.pl
@@ -0,0 +1,18 @@
+#!/usr/bin/env perl
+#
+
+use lib 'lib';
+use Storage::Box::Auth;
+
+my $auth = Storage::Box::Auth->new(
+	key_id => "vmqys6db",
+	user_id => ($ARGV[0]),
+	public_key => "keys/public_key.pem",
+	private_key => "keys/private_key.pem",
+	password => "test",
+	client_id => "96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	client_secret => "xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3");
+
+$auth->user->request;
+
+print $auth->token, "\n";
diff --git a/scripts/create_file.pl b/scripts/create_file.pl
new file mode 100644
index 0000000..5a980c2
--- /dev/null
+++ b/scripts/create_file.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+#
+use lib 'lib';
+use Data::Dumper;
+use Storage::Box::File;
+use Storage::Box::Auth;
+
+my ($user_id,$filename) = @ARGV;
+
+my $auth = Storage::Box::Auth->new(
+	key_id => "vmqys6db",
+	user_id => $user_id,
+	public_key => "keys/public_key.pem",
+	private_key => "keys/private_key.pem",
+	password => "test",
+	client_id => "96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	client_secret => "xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3");
+
+$auth->user->request;
+
+my $file = Storage::Box::File->new(
+	auth => $auth,
+	name => $filename
+);
+
+$file->create;
+
+print $file->id, "\n";
diff --git a/scripts/create_user.pl b/scripts/create_user.pl
new file mode 100644
index 0000000..4c93276
--- /dev/null
+++ b/scripts/create_user.pl
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+
+use lib 'lib';
+
+use Storage::Box::Auth;
+use Storage::Box::User;
+
+my ($username,$enterprise_id) = @ARGV;
+
+$enterprise_id ||= '2064336';
+
+my $auth = Storage::Box::Auth->new(
+	key_id => "vmqys6db",
+	enterprise_id => $enterprise_id,
+	public_key => "keys/public_key.pem",
+	private_key => "keys/private_key.pem",
+	password => "test",
+	client_id => "96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	client_secret => "xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3");
+
+$auth->enterprise->request;
+
+my $user = Storage::Box::User->new(
+	name => $username,
+	auth => $auth
+);
+
+$user->create;
+
+print $user->id, "\n";
diff --git a/scripts/delete_file.pl b/scripts/delete_file.pl
new file mode 100644
index 0000000..c69c2c8
--- /dev/null
+++ b/scripts/delete_file.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+#
+use lib 'lib';
+use Data::Dumper;
+use Storage::Box::Auth;
+use Storage::Box::File;
+
+my ($user_id,$file_id) = @ARGV;
+
+my $auth = Storage::Box::Auth->new(
+	key_id => "vmqys6db",
+	user_id => $user_id,
+	public_key => "keys/public_key.pem",
+	private_key => "keys/private_key.pem",
+	password => "test",
+	client_id => "96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	client_secret => "xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3");
+
+$auth->user->request;
+
+my $file = Storage::Box::File->new(
+	auth => $auth,
+	id => $file_id
+);
+
+$file->delete;
diff --git a/scripts/delete_user.pl b/scripts/delete_user.pl
new file mode 100644
index 0000000..827fcd3
--- /dev/null
+++ b/scripts/delete_user.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+
+use lib 'lib';
+
+use Storage::Box::Auth;
+use Storage::Box::User;
+use Data::Dumper;
+
+my ($user_id,$enterprise_id) = @ARGV;
+$enterprise_id ||= '2064336';
+
+my $auth = Storage::Box::Auth->new(
+	key_id => "vmqys6db",
+	enterprise_id => $enterprise_id,
+	public_key => "keys/public_key.pem",
+	private_key => "keys/private_key.pem",
+	password => "test",
+	client_id => "96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	client_secret => "xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3");
+
+$auth->enterprise->request;
+
+my $user = Storage::Box::User->new(
+	auth => $auth,
+	id => $user_id
+);
+
+print $user->delete ? "ok\n": "failed\n";
diff --git a/scripts/read_user.pl b/scripts/read_user.pl
new file mode 100644
index 0000000..8868f6e
--- /dev/null
+++ b/scripts/read_user.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+
+use lib 'lib';
+
+use Storage::Box::Auth;
+use Storage::Box::User;
+use Data::Dumper;
+
+my ($user_id,$enterprise_id) = @ARGV;
+$enterprise_id ||= '2064336';
+
+my $auth = Storage::Box::Auth->new(
+	key_id => "vmqys6db",
+	enterprise_id => $enterprise_id,
+	public_key => "keys/public_key.pem",
+	private_key => "keys/private_key.pem",
+	password => "test",
+	client_id => "96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	client_secret => "xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3");
+
+$auth->enterprise->request;
+
+my $user = Storage::Box::User->new(
+	auth => $auth,
+	id => $user_id
+);
+
+print Dumper $user->read;
diff --git a/scripts/update_user.pl b/scripts/update_user.pl
new file mode 100644
index 0000000..5bd3186
--- /dev/null
+++ b/scripts/update_user.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+
+use lib 'lib';
+
+use Storage::Box::Auth;
+use Storage::Box::User;
+use Data::Dumper;
+
+my ($user_id,$enterprise_id,%options) = @ARGV;
+$enterprise_id ||= '2064336';
+
+my $auth = Storage::Box::Auth->new(
+	key_id => "vmqys6db",
+	enterprise_id => $enterprise_id,
+	public_key => "keys/public_key.pem",
+	private_key => "keys/private_key.pem",
+	password => "test",
+	client_id => "96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
+	client_secret => "xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3");
+
+$auth->enterprise->request;
+
+my $user = Storage::Box::User->new(
+	auth => $auth,
+	id => $user_id
+);
+
+print Dumper $user->update(%options);
diff --git a/update_user.pl b/update_user.pl
deleted file mode 100644
index e85f0dc..0000000
--- a/update_user.pl
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env perl
-
-use lib 'lib';
-
-use Storage::Box::Auth;
-use Storage::Box::User;
-use Data::Dumper;
-
-my ($user_id,$enterprise_id,%options) = @ARGV;
-$enterprise_id ||= 2064336;
-
-my $jwt = Storage::Box::Auth::enterprise(
-	"vmqys6db",
-	"keys/private_key.pem",
-	"test",
-	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
-	"$enterprise_id");
-
-my $res =  Storage::Box::Auth::request(
-	"96o6g1e6mot3j1ord2qq6ptvxcsbn4oh",
-	"xhp3us3rX3kNLvMt9y3DcGSasqP6Orl3",
-	$jwt);
-
-my $token = $res->{access_token};
-
-print Dumper Storage::Box::User::update($token,$user_id,\%options);

-----------------------------------------------------------------------


More information about the Bps-public-commit mailing list