File Coverage

blib/lib/Net/Amazon/IAM.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Net::Amazon::IAM;
2 1     1   12673 use Moose;
  1         292147  
  1         6  
3              
4 1     1   5185 use URI;
  1         2684  
  1         19  
5 1     1   5 use Carp;
  1         4  
  1         47  
6 1     1   496 use JSON;
  1         7154  
  1         3  
7 1     1   430 use URI::Encode;
  1         7312  
  1         38  
8 1     1   148 use XML::Simple;
  0            
  0            
9             use POSIX qw(strftime);
10             use LWP::UserAgent;
11             use LWP::Protocol::https;
12             use Data::Dumper qw(Dumper);
13             use Params::Validate qw(validate SCALAR ARRAYREF HASHREF);
14             use HTTP::Request::Common;
15             use AWS::Signature4;
16              
17             use Net::Amazon::IAM::Error;
18             use Net::Amazon::IAM::Errors;
19             use Net::Amazon::IAM::User;
20             use Net::Amazon::IAM::Policy;
21             use Net::Amazon::IAM::Policies;
22             use Net::Amazon::IAM::UserPolicy;
23             use Net::Amazon::IAM::Group;
24             use Net::Amazon::IAM::AccessKey;
25             use Net::Amazon::IAM::AccessKeyMetadata;
26             use Net::Amazon::IAM::AccessKeysList;
27              
28             our $VERSION = '0.03';
29              
30             =head1 NAME
31              
32             Net::Amazon::IAM - Perl interface to the Amazon Identity and Access Management.
33              
34             =head1 VERSION
35              
36             This is Net::Amazon::IAM version 0.03
37              
38             IAM Query API version: '2010-05-08'
39              
40             =head1 SYNOPSIS
41              
42             use Net::Amazon::IAM;
43              
44             my $iam = Net::Amazon::IAM->new(
45             AWSAccessKeyId => 'PUBLIC_KEY_HERE',
46             SecretAccessKey => 'SECRET_KEY_HERE'
47             );
48              
49             # create new user
50             my $user = $iam->create_user (
51             UserName => 'testuser',
52             Path => '/path/to/test/user/',
53             );
54              
55             # delete user
56             my $delete = $iam->delete_user(UserName => 'testuser');
57             if($delete->isa("Net::Amazon::IAM::Error")) {
58             print Dumper $delete;
59             }else{
60             print "User was successfuly deleted\n";
61             }
62              
63             # add policy to user
64             my $policy_document = {
65             Version => '2012-10-17',
66             Statement => [
67             {
68             Effect => 'Allow',
69             Action => [
70             's3:Get*',
71             's3:List*',
72             ],
73             Resource => [
74             'arn:aws:s3:::sometestbucket',
75             'arn:aws:s3:::sometestbucket/*',
76             ],
77             },
78             ],
79             };
80              
81             my $policy = $iam->put_user_policy (
82             PolicyName => 'somtestpolicy',
83             UserName => 'sometestuser',
84             PolicyDocument => $policy_document,
85             );
86              
87             if($policy->isa("Net::Amazon::IAM::Error")) {
88             print Dumper $policy;
89             }else{
90             print "Policy was added\n";
91             }
92              
93              
94             If an error occurs while communicating with IAM, these methods will
95             throw a L<Net::Amazon::IAM::Error> exception.
96              
97             =head1 DESCRIPTION
98              
99             This module is a Perl interface to Amazon's Identity and Access Management (IAM). It uses the Query API to
100             communicate with Amazon's Web Services framework.
101              
102             =head1 CLASS METHODS
103              
104             =head2 new(%params)
105              
106             This is the constructor, it will return you a Net::Amazon::IAM object to work with. It takes
107             these parameters:
108              
109             =over
110              
111             =item AWSAccessKeyId (required)
112              
113             Your AWS access key.
114              
115             =item SecretAccessKey (required)
116              
117             Your secret key, B<WARNING!> don't give this out or someone will be able to use your account
118             and incur charges on your behalf.
119              
120             =item debug (optional)
121              
122             A flag to turn on debugging. Among other useful things, it will make the failing api calls print
123             a stack trace. It is turned off by default.
124              
125             =back
126              
127             =cut
128              
129             has 'AWSAccessKeyId' => (
130             is => 'ro',
131             isa => 'Str',
132             lazy => 1,
133             default => sub {
134             if (defined($_[0]->temp_creds)) {
135             return $_[0]->temp_creds->{'AccessKeyId'};
136             } else {
137             return undef;
138             }
139             }
140             );
141              
142             has 'SecretAccessKey' => (
143             is => 'ro',
144             isa => 'Str',
145             lazy => 1,
146             default => sub {
147             if (defined($_[0]->temp_creds)) {
148             return $_[0]->temp_creds->{'SecretAccessKey'};
149             } else {
150             return undef;
151             }
152             }
153             );
154              
155             has 'SecurityToken' => (
156             is => 'ro',
157             isa => 'Str',
158             lazy => 1,
159             predicate => 'has_SecurityToken',
160             default => sub {
161             if (defined($_[0]->temp_creds)) {
162             return $_[0]->temp_creds->{'Token'};
163             } else {
164             return undef;
165             }
166             }
167             );
168              
169             has 'base_url' => (
170             is => 'ro',
171             isa => 'Str',
172             lazy => 1,
173             default => sub {
174             return 'http' . ($_[0]->ssl ? 's' : '') . '://iam.amazonaws.com';
175             }
176             );
177              
178             has 'temp_creds' => (
179             is => 'ro',
180             lazy => 1,
181             predicate => 'has_temp_creds',
182             default => sub {
183             my $ret;
184             $ret = $_[0]->_fetch_iam_security_credentials();
185             },
186             );
187              
188             has 'debug' => ( is => 'ro', isa => 'Str', default => 0 );
189             has 'version' => ( is => 'ro', isa => 'Str', default => '2010-05-08' );
190             has 'ssl' => ( is => 'ro', isa => 'Bool', default => 1 );
191             has 'return_errors' => ( is => 'ro', isa => 'Bool', default => 1 );
192              
193             sub _timestamp {
194             return strftime("%Y-%m-%dT%H:%M:%SZ",gmtime);
195             }
196              
197             sub _fetch_iam_security_credentials {
198             my $self = shift;
199             my $retval = {};
200              
201             my $ua = LWP::UserAgent->new();
202             # Fail quickly if this is not running on an EC2 instance
203             $ua->timeout(2);
204              
205             my $url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/';
206              
207             $self->_debug("Attempting to fetch instance credentials");
208              
209             my $res = $ua->get($url);
210             if ($res->code == 200) {
211             # Assumes the first profile is the only profile
212             my $profile = (split /\n/, $res->content())[0];
213              
214             $res = $ua->get($url . $profile);
215              
216             if ($res->code == 200) {
217             $retval->{'Profile'} = $profile;
218             foreach (split /\n/, $res->content()) {
219             return undef if /Code/ && !/Success/;
220             if (m/.*"([^"]+)"\s+:\s+"([^"]+)",/) {
221             $retval->{$1} = $2;
222             }
223             }
224              
225             return $retval if (keys %{$retval});
226             }
227             }
228              
229             return undef;
230             }
231              
232             sub _sign {
233             my $self = shift;
234             my %args = @_;
235             my $action = delete $args{'Action'};
236             my %sign_hash = %args;
237             my $timestamp = $self->_timestamp;
238              
239             $sign_hash{'Action'} = $action;
240             $sign_hash{'Version'} = $self->version;
241              
242             if ($self->has_temp_creds || $self->has_SecurityToken) {
243             $sign_hash{'SecurityToken'} = $self->SecurityToken;
244             }
245              
246             my $signer = AWS::Signature4->new(
247             -access_key => $self->{'AWSAccessKeyId'},
248             -secret_key => $self->{'SecretAccessKey'},
249             );
250              
251             my $ua = LWP::UserAgent->new();
252              
253             my $request = POST(
254             $self->base_url,
255             [
256             %sign_hash,
257             ],
258             );
259              
260             $signer->sign($request);
261              
262             my $res = $ua->request($request);
263              
264             # We should force <item> elements to be in an array
265             my $xs = XML::Simple->new(
266             ForceArray => qr/(?:item|Errors)/i, # Always want item elements unpacked to arrays
267             KeyAttr => '', # Turn off folding for 'id', 'name', 'key' elements
268             SuppressEmpty => undef, # Turn empty values into explicit undefs
269             );
270             my $xml;
271              
272             # Check the result for connectivity problems, if so throw an error
273             if ($res->code >= 500) {
274             my $message = $res->status_line;
275             $xml = <<EOXML;
276             <xml>
277             <RequestID>N/A</RequestID>
278             <Errors>
279             <Error>
280             <Code>HTTP POST FAILURE</Code>
281             <Message>$message</Message>
282             </Error>
283             </Errors>
284             </xml>
285             EOXML
286              
287             } else {
288             $xml = $res->content();
289             }
290              
291             my $ref = $xs->XMLin($xml);
292             warn Dumper($ref) . "\n\n" if $self->debug == 1;
293              
294             return $ref;
295             }
296              
297             sub _parse_errors {
298             my $self = shift;
299             my $errors_xml = shift;
300              
301             my $es;
302             my $request_id = $errors_xml->{'RequestId'};
303              
304             my $error = Net::Amazon::IAM::Error->new(
305             code => $errors_xml->{'Error'}{'Code'},
306             message => $errors_xml->{'Error'}{'Message'},
307             request_id => $request_id,
308             );
309              
310             # Print a stack trace if debugging is enabled
311             if ($self->debug) {
312             confess 'Last error was: ' . $error;
313             }
314              
315             return $error;
316             }
317              
318             sub _debug {
319             my $self = shift;
320             my $message = shift;
321              
322             if ((grep { defined && length} $self->debug) && $self->debug == 1) {
323             print "$message\n\n\n\n";
324             }
325             }
326              
327             sub _build_filters {
328             my ($self, $args) = @_;
329              
330             my $filters = delete $args->{Filter};
331              
332             return unless $filters && ref($filters) eq 'ARRAY';
333              
334             $filters = [ $filters ] unless ref($filters->[0]) eq 'ARRAY';
335             my $count = 1;
336             foreach my $filter (@{$filters}) {
337             my ($name, @args) = @$filter;
338             $args->{"Filter." . $count.".Name"} = $name;
339             $args->{"Filter." . $count.".Value.".$_} = $args[$_-1] for 1..scalar @args;
340             $count++;
341             }
342             }
343              
344             =head2 create_user(%params)
345              
346             Create new IAM user
347              
348             =over
349              
350             =item UserName (required)
351              
352             New user username
353              
354             =item Path (optional)
355              
356             Where to create new user
357              
358             =back
359              
360             Returns a Net::Amazon::IAM::User object on success or Net::Amazon::IAM::Error on fail.
361              
362             =cut
363              
364             sub create_user {
365             my $self = shift;
366              
367             my %args = validate(@_, {
368             UserName => { type => SCALAR },
369             Path => { type => SCALAR, optional => 1 },
370             });
371              
372             my $xml = $self->_sign(Action => 'CreateUser', %args);
373              
374             if ( grep { defined && length } $xml->{'Error'} ) {
375             return $self->_parse_errors($xml);
376             } else {
377             return Net::Amazon::IAM::User->new(
378             $xml->{'CreateUserResult'}{'User'},
379             );
380             }
381             }
382              
383             =head2 delete_user(%params)
384              
385             Delete IAM User
386              
387             =over
388              
389             =item UserName (required)
390              
391             What user should be deleted
392              
393             =back
394              
395             Returns true on success or Net::Amazon::IAM::Error on fail.
396              
397             =cut
398              
399             sub delete_user {
400             my $self = shift;
401              
402             my %args = validate(@_, {
403             UserName => { type => SCALAR },
404             });
405              
406             my $xml = $self->_sign(Action => 'DeleteUser', %args);
407              
408             if ( grep { defined && length } $xml->{'Error'} ) {
409             return $self->_parse_errors($xml);
410             } else {
411             return 1;
412             }
413             }
414              
415             =head2 get_user(%params)
416              
417             Get IAM user details
418              
419             =over
420              
421             =item UserName (required)
422              
423             New user username
424              
425             =back
426              
427             Returns a Net::Amazon::IAM::User object on success or Net::Amazon::IAM::Error on fail.
428              
429             =cut
430              
431             sub get_user {
432             my $self = shift;
433              
434             my %args = validate(@_, {
435             UserName => { type => SCALAR },
436             });
437              
438             my $xml = $self->_sign(Action => 'GetUser', %args);
439              
440             if ( grep { defined && length } $xml->{'Error'} ) {
441             return $self->_parse_errors($xml);
442             } else {
443             return Net::Amazon::IAM::User->new(
444             $xml->{'GetUserResult'}{'User'},
445             );
446             }
447             }
448              
449             =head2 update_user(%params)
450              
451             Updates the name and/or the path of the specified user.
452              
453             =over
454              
455             =item UserName (required)
456              
457             Name of the user to update. If you're changing the name of the user, this is the original user name.
458              
459             =item NewPath (optional)
460              
461             New path for the user. Include this parameter only if you're changing the user's path.
462              
463             =item NewUserName (optional)
464              
465             New name for the user. Include this parameter only if you're changing the user's name.
466              
467             =back
468              
469             Returns true on success or Net::Amazon::IAM::Error on fail.
470              
471             =cut
472              
473             sub update_user {
474             my $self = shift;
475              
476             my %args = validate(@_, {
477             UserName => { type => SCALAR },
478             NewPath => { type => SCALAR, optional => 1 },
479             NewUserName => { type => SCALAR, optional => 1 },
480             });
481              
482             my $xml = $self->_sign(Action => 'UpdateUser', %args);
483              
484             if ( grep { defined && length } $xml->{'Error'} ) {
485             return $self->_parse_errors($xml);
486             } else {
487             return 1;
488             }
489             }
490              
491             =head2 add_user_to_group(%params)
492              
493             Adds the specified user to the specified group.
494              
495             =over
496              
497             =item GroupName (required)
498              
499             The name of the group to update.
500              
501             =item UserName (required)
502              
503             The name of the user to add.
504              
505             =back
506              
507             Returns true on success or Net::Amazon::IAM::Error on fail.
508              
509             =cut
510              
511             sub add_user_to_group {
512             my $self = shift;
513              
514             my %args = validate(@_, {
515             GroupName => { type => SCALAR },
516             UserName => { type => SCALAR },
517             });
518              
519             my $xml = $self->_sign(Action => 'AddUserToGroup', %args);
520              
521             if ( grep { defined && length } $xml->{'Error'} ) {
522             return $self->_parse_errors($xml);
523             } else {
524             return 1;
525             }
526             }
527              
528             =head2 remove_user_from_group(%params)
529              
530             Removes the specified user from the specified group.
531              
532             =over
533              
534             =item GroupName (required)
535              
536             The name of the group to update.
537              
538             =item UserName (required)
539              
540             The name of the user to remove.
541              
542             =back
543              
544             Returns true on success or Net::Amazon::IAM::Error on fail.
545              
546             =cut
547              
548             sub remove_user_from_group {
549             my $self = shift;
550              
551             my %args = validate(@_, {
552             GroupName => { type => SCALAR },
553             UserName => { type => SCALAR },
554             });
555              
556             my $xml = $self->_sign(Action => 'RemoveUserFromGroup', %args);
557              
558             if ( grep { defined && length } $xml->{'Error'} ) {
559             return $self->_parse_errors($xml);
560             } else {
561             return 1;
562             }
563             }
564              
565             =head2 create_group(%params)
566              
567             Creates a new group.
568              
569             =over
570              
571             =item GroupName (required)
572              
573             The name of the group to create.
574              
575             =item Path (optional)
576              
577             The path to the group.
578              
579             =back
580              
581             Returns Net::Amazon::IAM::Group object on success or Net::Amazon::IAM::Error on fail.
582              
583             =cut
584              
585             sub create_group {
586             my $self = shift;
587              
588             my %args = validate(@_, {
589             GroupName => { type => SCALAR },
590             Path => { type => SCALAR, optional => 1 },
591             });
592              
593             my $xml = $self->_sign(Action => 'CreateGroup', %args);
594              
595             if ( grep { defined && length } $xml->{'Error'} ) {
596             return $self->_parse_errors($xml);
597             } else {
598             return Net::Amazon::IAM::Group->new(
599             $xml->{'CreateGroupResult'}{'User'},
600             );
601             }
602             }
603              
604             =head2 get_group(%params)
605              
606             Returns group details and list of users that are in the specified group.
607              
608             =over
609              
610             =item GroupName (required)
611              
612             The name of the group.
613              
614             =back
615              
616             Returns Net::Amazon::IAM::Group object on success or Net::Amazon::IAM::Error on fail.
617             If there one or more users in specified group, Net::Amazon::IAM::Group object
618             will containt Users attribute wich is ArrayRef of Net::Amazon::IAM::User objects
619              
620             =cut
621              
622             sub get_group {
623             my $self = shift;
624              
625             my %args = validate(@_, {
626             GroupName => { type => SCALAR },
627             Marker => { type => SCALAR, optional => 1 },
628             MaxItems => { type => SCALAR, optional => 1 },
629             });
630              
631             my $xml = $self->_sign(Action => 'GetGroup', %args);
632              
633             if ( grep { defined && length } $xml->{'Error'} ) {
634             return $self->_parse_errors($xml);
635             } else {
636             my %result = %{$xml->{'GetGroupResult'}};
637              
638             my $users;
639             for my $user ( @{$result{'Users'}{'member'}} ) {
640             my $u = Net::Amazon::IAM::User->new(
641             $user,
642             );
643             push @$users, $u;
644             }
645              
646             return Net::Amazon::IAM::Group->new(
647             IsTruncated => $result{'IsTruncated'},
648             Users => $users,
649             %{$result{'Group'}},
650             );
651             }
652             }
653              
654             =head2 delete_group(%params)
655              
656             Deletes the specified group. The group must not contain any users or have any attached policies.
657              
658             =over
659              
660             =item GroupName (required)
661              
662             The name of the group to delete.
663              
664             =back
665              
666             Returns true on success or Net::Amazon::IAM::Error on fail.
667              
668             =cut
669              
670             sub delete_group {
671             my $self = shift;
672              
673             my %args = validate(@_, {
674             GroupName => { type => SCALAR },
675             });
676              
677             my $xml = $self->_sign(Action => 'DeleteGroup', %args);
678              
679             if ( grep { defined && length } $xml->{'Error'} ) {
680             return $self->_parse_errors($xml);
681             } else {
682             return 1;
683             }
684             }
685              
686             =head2 create_policy(%params)
687              
688             Creates a new managed policy for your AWS account.
689              
690             =over
691              
692             =item PolicyName (required)
693              
694             The name of the policy document.
695              
696             =item PolicyDocument (required)
697              
698             The policy document.
699              
700             =item Description (optional)
701              
702             A friendly description of the policy.
703              
704             =item Path (optional)
705              
706             The path for the policy.
707              
708             =back
709              
710             Returns Net::Amazon::IAM::Policy object on success or Net::Amazon::IAM::Error on fail.
711              
712             =cut
713              
714             sub create_policy {
715             my $self = shift;
716              
717             my %args = validate(@_, {
718             PolicyName => { type => SCALAR },
719             PolicyDocument => { type => HASHREF },
720             Description => { type => SCALAR, optional => 1 },
721             Path => { type => SCALAR, optional => 1 },
722             });
723              
724             $args{'PolicyDocument'} = encode_json delete $args{'PolicyDocument'};
725              
726             my $xml = $self->_sign(Action => 'CreatePolicy', %args);
727              
728             if ( grep { defined && length } $xml->{'Error'} ) {
729             return $self->_parse_errors($xml);
730             } else {
731             return Net::Amazon::IAM::Policy->new(
732             $xml->{'CreatePolicyResult'}{'Policy'},
733             );
734             }
735             }
736              
737             =head2 get_policy(%params)
738              
739             Retrieves information about the specified managed policy.
740              
741             =over
742              
743             =item PolicyArn (required)
744              
745             The Amazon Resource Name (ARN). ARNs are unique identifiers for AWS resources.
746              
747             =back
748              
749             Returns Net::Amazon::IAM::Policy object on success or Net::Amazon::IAM::Error on fail.
750              
751             =cut
752              
753             sub get_policy {
754             my $self = shift;
755              
756             my %args = validate(@_, {
757             PolicyArn => { type => SCALAR },
758             });
759              
760             my $xml = $self->_sign(Action => 'GetPolicy', %args);
761              
762             if ( grep { defined && length } $xml->{'Error'} ) {
763             return $self->_parse_errors($xml);
764             } else {
765             return Net::Amazon::IAM::Policy->new(
766             $xml->{'GetPolicyResult'}{'Policy'},
767             );
768             }
769             }
770              
771             =head2 delete_policy(%params)
772              
773             Deletes the specified managed policy.
774              
775             =over
776              
777             =item PolicyArn (required)
778              
779             The Amazon Resource Name (ARN). ARNs are unique identifiers for AWS resources.
780              
781             =back
782              
783             Returns true on success or Net::Amazon::IAM::Error on fail.
784              
785             =cut
786              
787             sub delete_policy {
788             my $self = shift;
789              
790             my %args = validate(@_, {
791             PolicyArn => { type => SCALAR },
792             });
793              
794             my $xml = $self->_sign(Action => 'DeletePolicy', %args);
795              
796             if ( grep { defined && length } $xml->{'Error'} ) {
797             return $self->_parse_errors($xml);
798             } else {
799             return 1;
800             }
801             }
802              
803             =head2 list_policies(%params)
804              
805             Lists all the managed policies that are available to your account,
806             including your own customer managed policies and all AWS managed policies.
807              
808             You can filter the list of policies that is returned using the optional
809             OnlyAttached, Scope, and PathPrefix parameters. For example, to list only the
810             customer managed policies in your AWS account, set Scope to Local.
811             To list only AWS managed policies, set Scope to AWS.
812              
813             =over
814              
815             =item OnlyAttached (optional)
816              
817             A flag to filter the results to only the attached policies.
818             When OnlyAttached is true, the returned list contains only the
819             policies that are attached to a user, group, or role.
820             When OnlyAttached is false, or when the parameter is not
821             included, all policies are returned.
822              
823             =item PathPrefix (optional)
824              
825             The path prefix for filtering the results.
826             If it is not included, it defaults to a slash (/), listing all policies.
827              
828             =item Scope (optional)
829              
830             The scope to use for filtering the results.
831              
832             To list only AWS managed policies, set Scope to AWS.
833             To list only the customer managed policies in your AWS account, set Scope to Local.
834             If it is not included, or if it is set to All, all policies are returned.
835              
836             =back
837              
838             Returns Net::Amazon::IAM::Policies on success or Net::Amazon::IAM::Error on fail.
839             When no policies found, the Policies attribute will be just empty array.
840              
841             =cut
842              
843             sub list_policies {
844             my $self = shift;
845              
846             my %args = validate(@_, {
847             Marker => { type => SCALAR, optional => 1 },
848             MaxItems => { type => SCALAR, optional => 1 },
849             PathPrefix => { type => SCALAR, optional => 1, default => '/' },
850             OnlyAttached => { regex => qr/true|false/, optional => 1, default => 'false' },
851             Scope => { regex => qr/AWS|Local|All/, optional => 1, default => 'All' },
852             });
853              
854             my $xml = $self->_sign(Action => 'ListPolicies', %args);
855              
856             if ( grep { defined && length } $xml->{'Error'} ) {
857             return $self->_parse_errors($xml);
858             } else {
859             my %result = %{$xml->{'ListPoliciesResult'}};
860             my $policies;
861              
862             if ( grep { defined && length } $result{'Policies'} ) {
863             if(ref($result{'Policies'}{'member'}) eq 'ARRAY') {
864             for my $policy(@{$result{'Policies'}{'member'}}) {
865             my $p = Net::Amazon::IAM::Policy->new(
866             $policy,
867             );
868              
869             push @$policies, $p;
870             }
871             }else{
872             my $p = Net::Amazon::IAM::Policy->new(
873             $result{'Policies'}{'member'},
874             );
875              
876             push @$policies, $p;
877             }
878             }else{
879             $policies = [];
880             }
881              
882             return Net::Amazon::IAM::Policies->new(
883             Policies => $policies,
884             );
885             }
886             }
887              
888             =head2 put_user_policy(%params)
889              
890             Deletes the specified managed policy.
891              
892             =over
893              
894             =item PolicyDocument (required)
895              
896             The policy document. Must be HashRef.
897              
898             =item PolicyName (required)
899              
900             The name of the policy document.
901              
902             =item UserName (required)
903              
904             The name of the user to associate the policy with.
905              
906             =back
907              
908             Returns true on success or Net::Amazon::IAM::Error on fail.
909              
910             =cut
911              
912             sub put_user_policy {
913             my $self = shift;
914              
915             my %args = validate(@_, {
916             PolicyDocument => { type => HASHREF },
917             PolicyName => { type => SCALAR },
918             UserName => { type => SCALAR },
919             });
920              
921             $args{'PolicyDocument'} = encode_json delete $args{'PolicyDocument'};
922              
923             my $xml = $self->_sign(Action => 'PutUserPolicy', %args);
924              
925             if ( grep { defined && length } $xml->{'Error'} ) {
926             return $self->_parse_errors($xml);
927             } else {
928             return 1;
929             }
930             }
931              
932             =head2 get_user_policy(%params)
933              
934             Retrieves the specified inline policy document that is embedded in the specified user.
935              
936             =over
937              
938             =item PolicyName (required)
939              
940             The name of the policy document to get.
941              
942             =item UserName (required)
943              
944             The name of the user who the policy is associated with.
945              
946             =back
947              
948             Returns Net::Amazon::IAM::UserPolicy object on success or Net::Amazon::IAM::Error on fail.
949              
950             =cut
951              
952             sub get_user_policy {
953             my $self = shift;
954              
955             my %args = validate(@_, {
956             PolicyName => { type => SCALAR },
957             UserName => { type => SCALAR },
958             });
959              
960             my $xml = $self->_sign(Action => 'GetUserPolicy', %args);
961              
962             if ( grep { defined && length } $xml->{'Error'} ) {
963             return $self->_parse_errors($xml);
964             } else {
965             my $user_policy = Net::Amazon::IAM::UserPolicy->new(
966             $xml->{'GetUserPolicyResult'}
967             );
968             $user_policy->{'PolicyDocument'} = decode_json(URI::Encode->new()->decode($user_policy->PolicyDocument));
969             return $user_policy;
970             }
971             }
972              
973             =head2 delete_user_policy(%params)
974              
975             Deletes the specified inline policy that is embedded in the specified user.
976              
977             =over
978              
979             =item PolicyName (required)
980              
981             The name identifying the policy document to delete.
982              
983             =item UserName (required)
984              
985             The name (friendly name, not ARN) identifying the user that the policy is embedded in.
986              
987             =back
988              
989             Returns true on success or Net::Amazon::IAM::Error on fail.
990              
991             =cut
992              
993             sub delete_user_policy {
994             my $self = shift;
995              
996             my %args = validate(@_, {
997             PolicyName => { type => SCALAR },
998             UserName => { type => SCALAR },
999             });
1000              
1001             my $xml = $self->_sign(Action => 'DeleteUserPolicy', %args);
1002              
1003             if ( grep { defined && length } $xml->{'Error'} ) {
1004             return $self->_parse_errors($xml);
1005             } else {
1006             return 1;
1007             }
1008             }
1009              
1010             =head2 list_user_policies(%params)
1011              
1012             Lists the names of the inline policies embedded in the specified user.
1013              
1014             =over
1015              
1016             =item UserName (required)
1017              
1018             The name of the user to list policies for.
1019              
1020             =back
1021              
1022             When found one or more policies, this method will return ArrayRef with policy names.
1023             Once no policies found, will return undef;
1024             Net::Amazon::IAM::Error will be returned on error
1025              
1026             =cut
1027              
1028             sub list_user_policies {
1029             my $self = shift;
1030              
1031             my %args = validate(@_, {
1032             UserName => { type => SCALAR },
1033             Marker => { type => SCALAR, optional => 1 },
1034             MaxItems => { type => SCALAR, optional => 1 },
1035             });
1036              
1037             my $xml = $self->_sign(Action => 'ListUserPolicies', %args);
1038              
1039             if ( grep { defined && length } $xml->{'Error'} ) {
1040             return $self->_parse_errors($xml);
1041             } else {
1042             my $policies;
1043              
1044             my %result = %{$xml->{'ListUserPoliciesResult'}};
1045              
1046             if ( grep { defined && length } $result{'PolicyNames'} ) {
1047             if(ref($result{'PolicyNames'}{'member'}) eq 'ARRAY') {
1048             $policies = $result{'PolicyNames'}{'member'};
1049             }else{
1050             push @$policies, $result{'PolicyNames'}{'member'};
1051             }
1052             } else {
1053             $policies = undef;
1054             }
1055              
1056             return $policies;
1057             }
1058             }
1059              
1060             =head2 create_access_key(%params)
1061              
1062             Creates a new AWS secret access key and corresponding AWS access key ID for the specified user.
1063             The default status for new keys is Active.
1064             If you do not specify a user name, IAM determines the user name implicitly based on the AWS access
1065             key ID signing the request. Because this action works for access keys under the AWS account, you can use
1066             this action to manage root credentials even if the AWS account has no associated users.
1067              
1068             Important:
1069             To ensure the security of your AWS account, the secret access key is accessible only during
1070             key and user creation. You must save the key (for example, in a text file) if you want to be
1071             able to access it again. If a secret key is lost, you can delete the access keys for the associated
1072             user and then create new keys.
1073              
1074             =over
1075              
1076             =item UserName (optional)
1077              
1078             The user name that the new key will belong to.
1079              
1080             =back
1081              
1082             Returns Net::Amazon::IAM::AccessKey object on success or Net::Amazon::IAM::Error on fail.
1083              
1084             =cut
1085              
1086             sub create_access_key {
1087             my $self = shift;
1088              
1089             my %args = validate(@_, {
1090             UserName => { type => SCALAR, optional => 1 },
1091             });
1092              
1093             my $xml = $self->_sign(Action => 'CreateAccessKey', %args);
1094              
1095             if ( grep { defined && length } $xml->{'Error'} ) {
1096             return $self->_parse_errors($xml);
1097             } else {
1098             return Net::Amazon::IAM::AccessKey->new(
1099             $xml->{'CreateAccessKeyResult'}{'AccessKey'},
1100             );
1101             }
1102             }
1103              
1104             =head2 delete_access_key(%params)
1105              
1106             Deletes the access key associated with the specified user.
1107              
1108             If you do not specify a user name, IAM determines the user name implicitly based
1109             on the AWS access key ID signing the request. Because this action works for access
1110             keys under the AWS account, you can use this action to manage root credentials even
1111             if the AWS account has no associated users.
1112              
1113             =over
1114              
1115             =item AccessKeyId (required)
1116              
1117             The access key ID for the access key ID and secret access key you want to delete.
1118              
1119             =item UserName (optional)
1120              
1121             The name of the user whose key you want to delete.
1122              
1123             =back
1124              
1125             Returns true on success or Net::Amazon::IAM::Error on fail.
1126              
1127             =cut
1128              
1129             sub delete_access_key {
1130             my $self = shift;
1131              
1132             my %args = validate(@_, {
1133             AccessKeyId => { type => SCALAR },
1134             UserName => { type => SCALAR, optional => 1 },
1135             });
1136              
1137             my $xml = $self->_sign(Action => 'DeleteAccessKey', %args);
1138              
1139             if ( grep { defined && length } $xml->{'Error'} ) {
1140             return $self->_parse_errors($xml);
1141             } else {
1142             return 1;
1143             }
1144             }
1145              
1146             =head2 list_access_keys(%params)
1147              
1148             Returns information about the access key IDs associated with the specified user.
1149             If the UserName field is not specified, the UserName is determined implicitly based on the AWS access
1150             key ID used to sign the request. Because this action works for access keys under the AWS account,
1151             you can use this action to manage root credentials even if the AWS account has no associated users.
1152              
1153             =over
1154              
1155             =item UserName (optional)
1156              
1157             The name of the user.
1158              
1159             =back
1160              
1161             Returns Net::Amazon::IAM::AccessKeysList on success.
1162             If specified user has no keys, "Keys" attribute of Net::Amazon::IAM::AccessKeysList object
1163             will be just empty array.
1164             Returns Net::Amazon::IAM::Error on fail.
1165              
1166             =cut
1167              
1168             sub list_access_keys {
1169             my $self = shift;
1170              
1171             my %args = validate(@_, {
1172             UserName => { type => SCALAR, optional => 1 },
1173             });
1174              
1175             my $xml = $self->_sign(Action => 'ListAccessKeys', %args);
1176              
1177             if ( grep { defined && length } $xml->{'Error'} ) {
1178             return $self->_parse_errors($xml);
1179             } else {
1180             my %result = %{$xml->{'ListAccessKeysResult'}};
1181             my $keys;
1182              
1183             if ( grep { defined && length } $result{'AccessKeyMetadata'} ) {
1184              
1185             if(ref($result{'AccessKeyMetadata'}{'member'}) eq 'ARRAY') {
1186             for my $key ( @{$result{'AccessKeyMetadata'}{'member'}} ) {
1187             my $k = Net::Amazon::IAM::AccessKeyMetadata->new(
1188             $key,
1189             );
1190             push @$keys, $k;
1191             }
1192             }else{
1193             my $k = Net::Amazon::IAM::AccessKeyMetadata->new(
1194             $result{'AccessKeyMetadata'}{'member'},
1195             );
1196             push @$keys, $k;
1197             }
1198             }else{
1199             $keys = [];
1200             }
1201              
1202             return Net::Amazon::IAM::AccessKeysList->new(
1203             Keys => $keys,
1204             );
1205             }
1206             }
1207              
1208             no Moose;
1209             1;
1210              
1211             =head1 AUTHOR
1212              
1213             Igor Tsigankov <tsiganenok@gmail.com>
1214              
1215             =head1 COPYRIGHT
1216              
1217             Copyright (c) 2015 Igor Tsigankov.
1218              
1219             This program is free software; you can redistribute it and/or modify it
1220             under the same terms as Perl itself.
1221              
1222             =cut
1223              
1224             __END__