Skip to content

Commit

Permalink
Merge 586e3d8 into d7db1e9
Browse files Browse the repository at this point in the history
  • Loading branch information
evalphobia committed Mar 1, 2019
2 parents d7db1e9 + 586e3d8 commit edd72a4
Show file tree
Hide file tree
Showing 7 changed files with 421 additions and 26 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
sudo: required
language: go
go:
- 1.9
# - 1.10
- 1.11
- 1.12
- tip
matrix:
allow_failures:
Expand Down
113 changes: 113 additions & 0 deletions iam/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package iam

import (
SDK "github.com/aws/aws-sdk-go/service/iam"

"github.com/evalphobia/aws-sdk-go-wrapper/config"
"github.com/evalphobia/aws-sdk-go-wrapper/log"
"github.com/evalphobia/aws-sdk-go-wrapper/private/pointers"
)

const (
serviceName = "IAM"
)

// IAM has IAM client.
type IAM struct {
client *SDK.IAM

logger log.Logger
}

// New returns initialized *IAM.
func New(conf config.Config) (*IAM, error) {
sess, err := conf.Session()
if err != nil {
return nil, err
}

svc := &IAM{
client: SDK.New(sess),
logger: log.DefaultLogger,
}
return svc, nil
}

// SetLogger sets logger.
func (svc *IAM) SetLogger(logger log.Logger) {
svc.logger = logger
}

// GetGroup executes GetGroup operation.
func (svc *IAM) GetGroup(groupName string) (*SDK.GetGroupOutput, error) {
output, err := svc.client.GetGroup(&SDK.GetGroupInput{
GroupName: pointers.String(groupName),
})
if err != nil {
svc.Errorf("error on `GetGroup` operation; error=[%s]; groupName=[%s];", err.Error(), groupName)
return nil, err
}
return output, nil
}

// GetPolicyVersion executes GetPolicyVersion operation.
func (svc *IAM) GetPolicyVersion(arn, versionID string) (*SDK.PolicyVersion, error) {
output, err := svc.client.GetPolicyVersion(&SDK.GetPolicyVersionInput{
PolicyArn: pointers.String(arn),
VersionId: pointers.String(versionID),
})
if err != nil {
svc.Errorf("error on `GetPolicyVersion` operation; error=[%s]; arn=[%s];", err.Error())
return nil, err
}
return output.PolicyVersion, nil
}

// ListAllPolicies fetches all of the policies list.
func (svc *IAM) ListAllPolicies() ([]Policy, error) {
return svc.listPolicies(&SDK.ListPoliciesInput{})
}

// ListAttachedPolicies fetches attached policy list.
func (svc *IAM) ListAttachedPolicies() ([]Policy, error) {
return svc.listPolicies(&SDK.ListPoliciesInput{
OnlyAttached: pointers.Bool(true),
})
}

// listPolicies executes ListPolicies operation.
func (svc *IAM) listPolicies(input *SDK.ListPoliciesInput) ([]Policy, error) {
// set default limit
if input.MaxItems == nil {
input.MaxItems = pointers.Long64(1000)
}

output, err := svc.client.ListPolicies(input)
if err != nil {
svc.Errorf("error on `ListPolicies` operation; error=%s;", err.Error())
return nil, err
}
return NewPolicies(output.Policies), nil
}

// ListEntitiesForPolicy executes ListEntitiesForPolicy operation.
func (svc *IAM) ListEntitiesForPolicy(arn string) ([]PolicyEntity, error) {
output, err := svc.client.ListEntitiesForPolicy(&SDK.ListEntitiesForPolicyInput{
PolicyArn: pointers.String(arn),
})
if err != nil {
svc.Errorf("error on `ListEntitiesForPolicy` operation; error=%s;", err.Error())
return nil, err
}
return NewPolicyEntityList(output), nil
}

// Infof logging information.
func (svc *IAM) Infof(format string, v ...interface{}) {
svc.logger.Infof(serviceName, format, v...)
}

// Errorf logging error information.
func (svc *IAM) Errorf(format string, v ...interface{}) {
svc.logger.Errorf(serviceName, format, v...)
}
33 changes: 33 additions & 0 deletions iam/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package iam

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/evalphobia/aws-sdk-go-wrapper/config"
)

func getTestConfig() config.Config {
return config.Config{
AccessKey: "access",
SecretKey: "secret",
}
}

func TestNew(t *testing.T) {
a := assert.New(t)

svc, err := New(getTestConfig())
a.NoError(err)
a.NotNil(svc.client)
a.Equal("iam", svc.client.ServiceName)
a.Equal("https://iam.amazonaws.com", svc.client.Endpoint)

region := "us-west-2"
svc, err = New(config.Config{
Region: region,
})
a.NoError(err)
a.Equal("https://iam.amazonaws.com", svc.client.Endpoint, "IAM endpoint is global")
}
58 changes: 58 additions & 0 deletions iam/policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package iam

import (
"time"

SDK "github.com/aws/aws-sdk-go/service/iam"
)

// Policy contains IAM policy data.
type Policy struct {
ARN string
PolicyID string
PolicyName string
VersionID string
Description string
AttachmentCount int64
CreateDate time.Time
UpdateDate time.Time
}

// NewPoilicy returns initialized Policy from *SDK.Policy.
func NewPoilicy(p *SDK.Policy) Policy {
pp := Policy{}
if p.Arn != nil {
pp.ARN = *p.Arn
}
if p.PolicyId != nil {
pp.PolicyID = *p.PolicyId
}
if p.PolicyName != nil {
pp.PolicyName = *p.PolicyName
}
if p.DefaultVersionId != nil {
pp.VersionID = *p.DefaultVersionId
}
if p.Description != nil {
pp.Description = *p.Description
}
if p.AttachmentCount != nil {
pp.AttachmentCount = *p.AttachmentCount
}
if p.CreateDate != nil {
pp.CreateDate = *p.CreateDate
}
if p.UpdateDate != nil {
pp.UpdateDate = *p.UpdateDate
}
return pp
}

// NewPolicies converts from []*SDK.Policy to []Policy.
func NewPolicies(list []*SDK.Policy) []Policy {
result := make([]Policy, len(list))
for i, p := range list {
result[i] = NewPoilicy(p)
}
return result
}
132 changes: 132 additions & 0 deletions iam/policy_document.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package iam

import (
"encoding/json"
"fmt"
"net/url"
)

const (
effectAllow = "Allow"
effectDeny = "Deny"
)

// PolicyDocument contains permission data of a policy.
type PolicyDocument struct {
Version string `json:"Version"`
Statement []Statement `json:"Statement"`
}

// used for multiple statements.
type documentForUnmarshal struct {
Version string `json:"Version"`
Statement []Statement `json:"Statement"`
}

// used for single statement.
type documentForUnmarshalSingle struct {
Version string `json:"Version"`
Statement Statement `json:"Statement"`
}

// NewPolicyDocumentFromDocument returns initialized PolicyDocument from response data.
func NewPolicyDocumentFromDocument(document string) (PolicyDocument, error) {
s, err := url.QueryUnescape(document)
if err != nil {
return PolicyDocument{}, err
}
return NewPolicyDocumentFromJSONString(s)
}

// NewPolicyDocumentFromJSONString returns initialized PolicyDocument from JSON data.
func NewPolicyDocumentFromJSONString(data string) (PolicyDocument, error) {
p := PolicyDocument{}
err := json.Unmarshal([]byte(data), &p)
if err != nil {
fmt.Printf("[%s]\n\n", string(data))
}
return p, err
}

// UnmarshalJSON converts from json to *PolicyDocument.
func (p *PolicyDocument) UnmarshalJSON(data []byte) error {
d1 := documentForUnmarshal{}
err := json.Unmarshal(data, &d1)
if err == nil {
p.Version = d1.Version
p.Statement = d1.Statement
return nil
}

d2 := documentForUnmarshalSingle{}
err = json.Unmarshal(data, &d2)
if err != nil {
return err
}

p.Version = d2.Version
p.Statement = []Statement{d2.Statement}
return nil
}

// Statement represents statement of iam policy.
type Statement struct {
Sid string `json:"Sid"`
Effect string `json:"Effect"`
Action []string `json:"Action"`
Resource []string `json:"Resource"`
}

// IsAllow checks that effect is allow.
func (s *Statement) IsAllow() bool {
return s.Effect == effectAllow
}

// IsDeny checks that effect is deny.
func (s *Statement) IsDeny() bool {
return s.Effect == effectDeny
}

// UnmarshalJSON converts from json to *Statement.
func (s *Statement) UnmarshalJSON(data []byte) error {
var m map[string]interface{}
json.Unmarshal(data, &m)
return s.setFromMap(m)
}

// used for converting from json data to *Statement
func (s *Statement) setFromMap(m map[string]interface{}) error {
if v, ok := m["Sid"].(string); ok {
s.Sid = v
}
if v, ok := m["Effect"].(string); ok {
s.Effect = v
}
if v, ok := m["Action"]; ok {
switch v := v.(type) {
case []interface{}:
s.Action = toStringList(v)
case string:
s.Action = []string{v}
}
}
if v, ok := m["Resource"]; ok {
switch v := v.(type) {
case []interface{}:
s.Resource = toStringList(v)
case string:
s.Resource = []string{v}
}
}
return nil
}

func toStringList(list []interface{}) []string {
result := make([]string, 0, len(list))
for _, v := range list {
if v, ok := v.(string); ok {
result = append(result, v)
}
}
return result
}

0 comments on commit edd72a4

Please sign in to comment.