diff --git a/api/handlers_sgs.go b/api/handlers_sgs.go index b861e59..60d8fc4 100644 --- a/api/handlers_sgs.go +++ b/api/handlers_sgs.go @@ -2,11 +2,12 @@ package api import ( "fmt" + "net/http" + "strconv" + "github.com/YaleSpinup/apierror" "github.com/YaleSpinup/ec2-api/ec2" "github.com/gorilla/mux" - "net/http" - "strconv" ) func (s *server) SecurityGroupListHandler(w http.ResponseWriter, r *http.Request) { @@ -89,3 +90,41 @@ func (s *server) SecurityGroupGetHandler(w http.ResponseWriter, r *http.Request) handleResponseOk(w, toEc2SecurityGroupResponse(out[0])) } + +func (s *server) SecurityGroupDeleteHandler(w http.ResponseWriter, r *http.Request) { + w = LogWriter{w} + vars := mux.Vars(r) + account := s.mapAccountNumber(vars["account"]) + id := vars["id"] + + role := fmt.Sprintf("arn:aws:iam::%s:role/%s", account, s.session.RoleName) + policy, err := sgDeletePolicy(id) + if err != nil { + handleError(w, apierror.New(apierror.ErrInternalError, "failed to generate policy", err)) + return + } + + session, err := s.assumeRole( + r.Context(), + s.session.ExternalID, + role, + policy, + ) + if err != nil { + msg := fmt.Sprintf("failed to assume role in account: %s", account) + handleError(w, apierror.New(apierror.ErrForbidden, msg, err)) + return + } + + service := ec2.New( + ec2.WithSession(session.Session), + ec2.WithOrg(s.org), + ) + + if err := service.DeleteSecurityGroup(r.Context(), id); err != nil { + handleError(w, err) + return + } + + handleResponseOk(w, "OK") +} diff --git a/api/policy.go b/api/policy.go new file mode 100644 index 0000000..5a76257 --- /dev/null +++ b/api/policy.go @@ -0,0 +1,64 @@ +package api + +import ( + "encoding/json" + "fmt" + + "github.com/YaleSpinup/aws-go/services/iam" + + log "github.com/sirupsen/logrus" +) + +// orgTagAccessPolicy generates the org tag conditional policy to be passed inline when assuming a role +func orgTagAccessPolicy(org string) (string, error) { + log.Debugf("generating org policy document") + + policy := iam.PolicyDocument{ + Version: "2012-10-17", + Statement: []iam.StatementEntry{ + { + Effect: "Allow", + Action: []string{"*"}, + Resource: []string{"*"}, + Condition: iam.Condition{ + "StringEquals": iam.ConditionStatement{ + "aws:ResourceTag/spinup:org": []string{org}, + }, + }, + }, + }, + } + + j, err := json.Marshal(policy) + if err != nil { + return "", err + } + + return string(j), nil +} + +func sgDeletePolicy(id string) (string, error) { + log.Debugf("generating org policy document") + + sgResource := fmt.Sprintf("arn:aws:ec2:*:*:security-group/%s", id) + + policy := iam.PolicyDocument{ + Version: "2012-10-17", + Statement: []iam.StatementEntry{ + { + Effect: "Allow", + Action: []string{ + "ec2:DeleteSecurityGroup", + }, + Resource: []string{sgResource}, + }, + }, + } + + j, err := json.Marshal(policy) + if err != nil { + return "", err + } + + return string(j), nil +} diff --git a/api/routes.go b/api/routes.go index a853f60..152631c 100644 --- a/api/routes.go +++ b/api/routes.go @@ -78,7 +78,7 @@ func (s *server) routes() { api.HandleFunc("/{account}/instances/{id}", s.ProxyRequestHandler).Methods(http.MethodDelete) api.HandleFunc("/{account}/instances/{id}/volumes/{vid}", s.ProxyRequestHandler).Methods(http.MethodDelete) api.HandleFunc("/{account}/instanceprofiles/{name}", s.ProxyRequestHandler).Methods(http.MethodDelete) - api.HandleFunc("/{account}/sgs/{id}", s.ProxyRequestHandler).Methods(http.MethodDelete) + api.HandleFunc("/{account}/sgs/{id}", s.SecurityGroupDeleteHandler).Methods(http.MethodDelete) api.HandleFunc("/{account}/volumes/{id}", s.ProxyRequestHandler).Methods(http.MethodDelete) api.HandleFunc("/{account}/snapshots/{id}", s.ProxyRequestHandler).Methods(http.MethodDelete) api.HandleFunc("/{account}/images/{id}", s.ProxyRequestHandler).Methods(http.MethodDelete) diff --git a/api/server.go b/api/server.go index a6064c6..ca28396 100644 --- a/api/server.go +++ b/api/server.go @@ -18,7 +18,6 @@ package api import ( "context" - "encoding/json" "errors" "math/rand" "net/http" @@ -26,7 +25,6 @@ import ( "time" "github.com/YaleSpinup/ec2-api/common" - "github.com/YaleSpinup/ec2-api/iam" "github.com/YaleSpinup/ec2-api/session" "github.com/gorilla/handlers" "github.com/gorilla/mux" @@ -217,34 +215,6 @@ func retry(attempts int, sleep time.Duration, f func() error) error { return nil } -// orgTagAccessPolicy generates the org tag conditional policy to be passed inline when assuming a role -func orgTagAccessPolicy(org string) (string, error) { - log.Debugf("generating org policy document") - - policy := iam.PolicyDocument{ - Version: "2012-10-17", - Statement: []iam.StatementEntry{ - { - Effect: "Allow", - Action: []string{"*"}, - Resource: "*", - Condition: iam.Condition{ - "StringEquals": iam.ConditionStatement{ - "aws:ResourceTag/spinup:org": org, - }, - }, - }, - }, - } - - j, err := json.Marshal(policy) - if err != nil { - return "", err - } - - return string(j), nil -} - // if we have an entry for the account name, return the associated account number func (s *server) mapAccountNumber(name string) string { if a, ok := s.accountsMap[name]; ok { diff --git a/ec2/sgs.go b/ec2/sgs.go index e3b1809..a625773 100644 --- a/ec2/sgs.go +++ b/ec2/sgs.go @@ -2,6 +2,7 @@ package ec2 import ( "context" + "github.com/YaleSpinup/apierror" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -66,3 +67,20 @@ func (e *Ec2) GetSecurityGroup(ctx context.Context, ids ...string) ([]*ec2.Secur return out.SecurityGroups, err } + +// DeleteSecurityGroup deletes the given security group +func (e *Ec2) DeleteSecurityGroup(ctx context.Context, id string) error { + if id == "" { + return apierror.New(apierror.ErrBadRequest, "invalid input", nil) + } + + log.Infof("deleting security group %s", id) + + if _, err := e.Service.DeleteSecurityGroupWithContext(ctx, &ec2.DeleteSecurityGroupInput{ + GroupId: aws.String(id), + }); err != nil { + return ErrCode("deleting security group", err) + } + + return nil +} diff --git a/ec2/sgs_test.go b/ec2/sgs_test.go index fd47f93..217e10e 100644 --- a/ec2/sgs_test.go +++ b/ec2/sgs_test.go @@ -2,13 +2,15 @@ package ec2 import ( "context" + "reflect" + "testing" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2/ec2iface" - "reflect" - "testing" ) var securityGroups = []*ec2.SecurityGroup{ @@ -328,6 +330,20 @@ func (m mockEC2Client) DescribeSecurityGroupsWithContext(ctx context.Context, in return &ec2.DescribeSecurityGroupsOutput{SecurityGroups: securityGroupList}, nil } +func (m mockEC2Client) DeleteSecurityGroupWithContext(ctx context.Context, input *ec2.DeleteSecurityGroupInput, opts ...request.Option) (*ec2.DeleteSecurityGroupOutput, error) { + if m.err != nil { + return nil, m.err + } + + for _, securityGroup := range securityGroups { + if aws.StringValue(input.GroupId) == aws.StringValue(securityGroup.GroupId) { + return &ec2.DeleteSecurityGroupOutput{}, nil + } + } + + return nil, awserr.New("NotFound", "Security group not found", nil) +} + func TestEc2_ListSecurityGroups(t *testing.T) { type fields struct { session *session.Session @@ -906,3 +922,95 @@ func TestEc2_GetSecurityGroup(t *testing.T) { }) } } + +func TestEc2_DeleteSecurityGroup(t *testing.T) { + type fields struct { + session *session.Session + Service ec2iface.EC2API + DefaultKMSKeyId string + DefaultSgs []string + DefaultSubnets []string + org string + } + type args struct { + ctx context.Context + id string + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "empty id", + args: args{ctx: context.TODO()}, + wantErr: true, + }, + { + name: "good id sg-0000000001", + args: args{ + ctx: context.TODO(), + id: "sg-0000000001", + }, + fields: fields{Service: newmockEC2Client(t, nil)}, + }, + { + name: "good id sg-0000000002", + args: args{ + ctx: context.TODO(), + id: "sg-0000000002", + }, + fields: fields{Service: newmockEC2Client(t, nil)}, + }, + { + name: "good id sg-0000000003", + args: args{ + ctx: context.TODO(), + id: "sg-0000000003", + }, + fields: fields{Service: newmockEC2Client(t, nil)}, + }, + { + name: "good id sg-0000000004", + args: args{ + ctx: context.TODO(), + id: "sg-0000000004", + }, + fields: fields{Service: newmockEC2Client(t, nil)}, + }, + { + name: "bad id", + args: args{ + ctx: context.TODO(), + id: "sg-missing", + }, + fields: fields{Service: newmockEC2Client(t, nil)}, + wantErr: true, + }, + { + name: "aws error", + args: args{ + ctx: context.TODO(), + id: "sg-0000000001", + }, + fields: fields{Service: newmockEC2Client(t, awserr.New("Bad Request", "boom.", nil))}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &Ec2{ + session: tt.fields.session, + Service: tt.fields.Service, + DefaultKMSKeyId: tt.fields.DefaultKMSKeyId, + DefaultSgs: tt.fields.DefaultSgs, + DefaultSubnets: tt.fields.DefaultSubnets, + org: tt.fields.org, + } + if err := e.DeleteSecurityGroup(tt.args.ctx, tt.args.id); (err != nil) != tt.wantErr { + t.Errorf("Ec2.DeleteSecurityGroup() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/go.mod b/go.mod index bf7fedc..84883f1 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,9 @@ go 1.15 require ( github.com/YaleSpinup/apierror v0.1.0 + github.com/YaleSpinup/aws-go v0.1.0 github.com/aws/amazon-ec2-instance-selector/v2 v2.0.2 - github.com/aws/aws-sdk-go v1.37.6 + github.com/aws/aws-sdk-go v1.38.50 github.com/google/uuid v1.2.0 github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 @@ -13,7 +14,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.9.0 github.com/prometheus/procfs v0.4.1 // indirect - github.com/sirupsen/logrus v1.7.0 + github.com/sirupsen/logrus v1.8.1 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect google.golang.org/protobuf v1.25.0 // indirect diff --git a/go.sum b/go.sum index e453052..fe15aa5 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/YaleSpinup/apierror v0.1.0 h1:XiL1AQTsZ9ariWE1BngpDeG+YZjQFX/a/OX25AkWQj8= github.com/YaleSpinup/apierror v0.1.0/go.mod h1:m68Zb59VAV7MUGwzcYe6vb/FU/DO/QGMbFumkHoyQMQ= +github.com/YaleSpinup/aws-go v0.1.0 h1:Yi2pAfSCoH2AASCROhrliL6givoDOThNVGxQ0RZNHXE= +github.com/YaleSpinup/aws-go v0.1.0/go.mod h1:liyJROxmYM/PCOn6CDT4B5mDcOKhFFCz7hmuymHoRJ4= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -26,8 +28,8 @@ github.com/aws/amazon-ec2-instance-selector/v2 v2.0.2/go.mod h1:qot+32DWumAuTogN github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.31.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.37.6 h1:SWYjRvyZw6DJc3pkZfRWVRD/5wiTDuwOkyb89AAkEBY= -github.com/aws/aws-sdk-go v1.37.6/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.38.50 h1:9+dEpZbgjBMeoOes6QfZMC87uDMwM8Lw4E79L0/rPZI= +github.com/aws/aws-sdk-go v1.38.50/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -295,8 +297,8 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= diff --git a/iam/errors.go b/iam/errors.go deleted file mode 100644 index 84ef06c..0000000 --- a/iam/errors.go +++ /dev/null @@ -1,232 +0,0 @@ -package iam - -import ( - "github.com/YaleSpinup/apierror" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/iam" - "github.com/pkg/errors" -) - -func ErrCode(msg string, err error) error { - if aerr, ok := errors.Cause(err).(awserr.Error); ok { - switch aerr.Code() { - case - "Forbidden": - - return apierror.New(apierror.ErrForbidden, msg, aerr) - case - // iam.ErrCodeLimitExceededException for service response error code - // "LimitExceeded". - // - // The request was rejected because it attempted to create resources beyond - // the current AWS account limitations. The error message describes the limit - // exceeded. - iam.ErrCodeLimitExceededException, - - // iam.ErrCodeReportGenerationLimitExceededException for service response error code - // "ReportGenerationLimitExceeded". - // - // The request failed because the maximum number of concurrent requests for - // this account are already running. - iam.ErrCodeReportGenerationLimitExceededException: - - return apierror.New(apierror.ErrLimitExceeded, msg, aerr) - case - - // iam.ErrCodeCredentialReportExpiredException for service response error code - // "ReportExpired". - // - // The request was rejected because the most recent credential report has expired. - // To generate a new credential report, use GenerateCredentialReport. For more - // information about credential report expiration, see Getting Credential Reports - // (https://docs.aws.amazon.com/IAM/latest/UserGuide/credential-reports.html) - // in the IAM User Guide. - iam.ErrCodeCredentialReportExpiredException, - - // iam.ErrCodeCredentialReportNotPresentException for service response error code - // "ReportNotPresent". - // - // The request was rejected because the credential report does not exist. To - // generate a credential report, use GenerateCredentialReport. - iam.ErrCodeCredentialReportNotPresentException, - - // iam.ErrCodeCredentialReportNotReadyException for service response error code - // "ReportInProgress". - // - // The request was rejected because the credential report is still being generated. - iam.ErrCodeCredentialReportNotReadyException, - - // iam.ErrCodeDeleteConflictException for service response error code - // "DeleteConflict". - // - // The request was rejected because it attempted to delete a resource that has - // attached subordinate entities. The error message describes these entities. - iam.ErrCodeDeleteConflictException, - - // iam.ErrCodeDuplicateCertificateException for service response error code - // "DuplicateCertificate". - // - // The request was rejected because the same certificate is associated with - // an IAM user in the account. - iam.ErrCodeDuplicateCertificateException, - - // iam.ErrCodeDuplicateSSHPublicKeyException for service response error code - // "DuplicateSSHPublicKey". - // - // The request was rejected because the SSH public key is already associated - // with the specified IAM user. - iam.ErrCodeDuplicateSSHPublicKeyException, - - // iam.ErrCodeEntityAlreadyExistsException for service response error code - // "EntityAlreadyExists". - // - // The request was rejected because it attempted to create a resource that already - // exists. - iam.ErrCodeEntityAlreadyExistsException, - - // iam.ErrCodeConcurrentModificationException for service response error code - // "ConcurrentModification". - // - // The request was rejected because multiple requests to change this object - // were submitted simultaneously. Wait a few minutes and submit your request - // again. - iam.ErrCodeConcurrentModificationException: - - return apierror.New(apierror.ErrConflict, msg, aerr) - case - // iam.ErrCodeEntityTemporarilyUnmodifiableException for service response error code - // "EntityTemporarilyUnmodifiable". - // - // The request was rejected because it referenced an entity that is temporarily - // unmodifiable, such as a user name that was deleted and then recreated. The - // error indicates that the request is likely to succeed if you try again after - // waiting several minutes. The error message describes the entity. - iam.ErrCodeEntityTemporarilyUnmodifiableException, - - // iam.ErrCodeInvalidAuthenticationCodeException for service response error code - // "InvalidAuthenticationCode". - // - // The request was rejected because the authentication code was not recognized. - // The error message describes the specific error. - iam.ErrCodeInvalidAuthenticationCodeException, - - // iam.ErrCodeInvalidCertificateException for service response error code - // "InvalidCertificate". - // - // The request was rejected because the certificate is invalid. - iam.ErrCodeInvalidCertificateException, - - // iam.ErrCodeInvalidInputException for service response error code - // "InvalidInput". - // - // The request was rejected because an invalid or out-of-range value was supplied - // for an input parameter. - iam.ErrCodeInvalidInputException, - - // iam.ErrCodeInvalidPublicKeyException for service response error code - // "InvalidPublicKey". - // - // The request was rejected because the public key is malformed or otherwise - // invalid. - iam.ErrCodeInvalidPublicKeyException, - - // iam.ErrCodeInvalidUserTypeException for service response error code - // "InvalidUserType". - // - // The request was rejected because the type of user for the transaction was - // incorrect. - iam.ErrCodeInvalidUserTypeException, - - // iam.ErrCodeKeyPairMismatchException for service response error code - // "KeyPairMismatch". - // - // The request was rejected because the public key certificate and the private - // key do not match. - iam.ErrCodeKeyPairMismatchException, - - // iam.ErrCodeMalformedCertificateException for service response error code - // "MalformedCertificate". - // - // The request was rejected because the certificate was malformed or expired. - // The error message describes the specific error. - iam.ErrCodeMalformedCertificateException, - - // iam.ErrCodeMalformedPolicyDocumentException for service response error code - // "MalformedPolicyDocument". - // - // The request was rejected because the policy document was malformed. The error - // message describes the specific error. - iam.ErrCodeMalformedPolicyDocumentException, - - // iam.ErrCodePasswordPolicyViolationException for service response error code - // "PasswordPolicyViolation". - // - // The request was rejected because the provided password did not meet the requirements - // imposed by the account password policy. - iam.ErrCodePasswordPolicyViolationException, - - // iam.ErrCodePolicyEvaluationException for service response error code - // "PolicyEvaluation". - // - // The request failed because a provided policy could not be successfully evaluated. - // An additional detailed message indicates the source of the failure. - iam.ErrCodePolicyEvaluationException, - - // iam.ErrCodePolicyNotAttachableException for service response error code - // "PolicyNotAttachable". - // - // The request failed because AWS service role policies can only be attached - // to the service-linked role for that service. - iam.ErrCodePolicyNotAttachableException, - - // iam.ErrCodeServiceNotSupportedException for service response error code - // "NotSupportedService". - // - // The specified service does not support service-specific credentials. - iam.ErrCodeServiceNotSupportedException, - - // iam.ErrCodeUnmodifiableEntityException for service response error code - // "UnmodifiableEntity". - // - // The request was rejected because only the service that depends on the service-linked - // role can modify or delete the role on your behalf. The error message includes - // the name of the service that depends on this service-linked role. You must - // request the change through that service. - iam.ErrCodeUnmodifiableEntityException, - - // iam.ErrCodeUnrecognizedPublicKeyEncodingException for service response error code - // "UnrecognizedPublicKeyEncoding". - // - // The request was rejected because the public key encoding format is unsupported - // or unrecognized. - iam.ErrCodeUnrecognizedPublicKeyEncodingException: - - return apierror.New(apierror.ErrBadRequest, msg, aerr) - case - // iam.ErrCodeNoSuchEntityException for service response error code - // "NoSuchEntity". - // - // The request was rejected because it referenced a resource entity that does - // not exist. The error message describes the resource. - iam.ErrCodeNoSuchEntityException: - - return apierror.New(apierror.ErrNotFound, msg, aerr) - - case - - // iam.ErrCodeServiceFailureException for service response error code - // "ServiceFailure". - // - // The request processing has failed because of an unknown error, exception - // or failure. - iam.ErrCodeServiceFailureException: - - return apierror.New(apierror.ErrServiceUnavailable, msg, aerr) - default: - m := msg + ": " + aerr.Message() - return apierror.New(apierror.ErrBadRequest, m, aerr) - } - } - - return apierror.New(apierror.ErrInternalError, msg, err) -} diff --git a/iam/errors_test.go b/iam/errors_test.go deleted file mode 100644 index e443e75..0000000 --- a/iam/errors_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package iam - -import ( - "testing" - - "github.com/YaleSpinup/apierror" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/iam" - "github.com/pkg/errors" -) - -func TestErrCode(t *testing.T) { - apiErrorTestCases := map[string]string{ - "": apierror.ErrBadRequest, - - iam.ErrCodeLimitExceededException: apierror.ErrLimitExceeded, - iam.ErrCodeReportGenerationLimitExceededException: apierror.ErrLimitExceeded, - - iam.ErrCodeCredentialReportExpiredException: apierror.ErrConflict, - iam.ErrCodeCredentialReportNotPresentException: apierror.ErrConflict, - iam.ErrCodeCredentialReportNotReadyException: apierror.ErrConflict, - iam.ErrCodeDeleteConflictException: apierror.ErrConflict, - iam.ErrCodeDuplicateCertificateException: apierror.ErrConflict, - iam.ErrCodeDuplicateSSHPublicKeyException: apierror.ErrConflict, - iam.ErrCodeEntityAlreadyExistsException: apierror.ErrConflict, - iam.ErrCodeConcurrentModificationException: apierror.ErrConflict, - - iam.ErrCodeEntityTemporarilyUnmodifiableException: apierror.ErrBadRequest, - iam.ErrCodeInvalidAuthenticationCodeException: apierror.ErrBadRequest, - iam.ErrCodeInvalidCertificateException: apierror.ErrBadRequest, - iam.ErrCodeInvalidInputException: apierror.ErrBadRequest, - iam.ErrCodeInvalidPublicKeyException: apierror.ErrBadRequest, - iam.ErrCodeInvalidUserTypeException: apierror.ErrBadRequest, - iam.ErrCodeKeyPairMismatchException: apierror.ErrBadRequest, - iam.ErrCodeMalformedCertificateException: apierror.ErrBadRequest, - iam.ErrCodeMalformedPolicyDocumentException: apierror.ErrBadRequest, - iam.ErrCodePasswordPolicyViolationException: apierror.ErrBadRequest, - iam.ErrCodePolicyEvaluationException: apierror.ErrBadRequest, - iam.ErrCodePolicyNotAttachableException: apierror.ErrBadRequest, - iam.ErrCodeServiceNotSupportedException: apierror.ErrBadRequest, - iam.ErrCodeUnmodifiableEntityException: apierror.ErrBadRequest, - iam.ErrCodeUnrecognizedPublicKeyEncodingException: apierror.ErrBadRequest, - - iam.ErrCodeNoSuchEntityException: apierror.ErrNotFound, - iam.ErrCodeServiceFailureException: apierror.ErrInternalError, - } - - for awsErr, apiErr := range apiErrorTestCases { - err := ErrCode("test error", awserr.New(awsErr, awsErr, nil)) - if aerr, ok := errors.Cause(err).(apierror.Error); ok { - t.Logf("got apierror '%s'", aerr) - } else { - t.Errorf("expected cloudwatch error %s to be an apierror.Error %s, got %s", awsErr, apiErr, err) - } - } - - err := ErrCode("test error", errors.New("Unknown")) - if aerr, ok := errors.Cause(err).(apierror.Error); ok { - t.Logf("got apierror '%s'", aerr) - } else { - t.Errorf("expected unknown error to be an apierror.ErrInternalError, got %s", err) - } -} diff --git a/iam/iam.go b/iam/iam.go deleted file mode 100644 index 04c0554..0000000 --- a/iam/iam.go +++ /dev/null @@ -1,71 +0,0 @@ -package iam - -import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/iam" - "github.com/aws/aws-sdk-go/service/iam/iamiface" - log "github.com/sirupsen/logrus" -) - -type PolicyDocument struct { - Version string - Statement []StatementEntry -} - -type StatementEntry struct { - Sid string `json:",omitempty"` - Effect string - Action []string - Resource string - Condition Condition `json:",omitempty"` -} - -// Condition maps a condition operator to the condition-key/condition-value statement -// ie. "{ "StringEquals" : { "aws:username" : "johndoe" }}" -// for more information, see https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html -type Condition map[string]ConditionStatement - -// ConditionStatement maps condition-key to condition-value -// ie. "{ "aws:username" : "johndoe" }" -type ConditionStatement map[string]string - -type IAM struct { - session *session.Session - Service iamiface.IAMAPI -} - -type IAMOption func(*IAM) - -func New(opts ...IAMOption) IAM { - i := IAM{} - - for _, opt := range opts { - opt(&i) - } - - if i.session != nil { - i.Service = iam.New(i.session) - } - - return i -} - -func WithSession(sess *session.Session) IAMOption { - return func(i *IAM) { - log.Debug("using aws session") - i.session = sess - } -} - -func WithCredentials(key, secret, token, region string) IAMOption { - return func(i *IAM) { - log.Debugf("creating new session with key id %s in region %s", key, region) - sess := session.Must(session.NewSession(&aws.Config{ - Credentials: credentials.NewStaticCredentials(key, secret, token), - Region: aws.String(region), - })) - i.session = sess - } -} diff --git a/iam/iam_test.go b/iam/iam_test.go deleted file mode 100644 index 3cd1f00..0000000 --- a/iam/iam_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package iam - -import ( - "reflect" - "testing" - "time" - - "github.com/aws/aws-sdk-go/service/iam/iamiface" -) - -var testTime = time.Now() - -// mockIAMClient is a fake IAM client -type mockIAMClient struct { - iamiface.IAMAPI - t *testing.T - err error -} - -func newMockIAMClient(t *testing.T, err error) iamiface.IAMAPI { - return &mockIAMClient{ - t: t, - err: err, - } -} -func TestNewSession(t *testing.T) { - client := New() - to := reflect.TypeOf(client).String() - if to != "iam.IAM" { - t.Errorf("expected type to be iam.IAM, got %s", to) - } -}