Unmarshal AWS IAM Document, но условная строка или строка [] в документе - PullRequest
3 голосов
/ 09 июня 2019

Я работаю с политикой IAM примеров для AWS Go SDK и пытаюсь сделать противоположное примеру Create Policy - в основном, получить всю политику IAM в учетной записи, получить версии политики по умолчанию, затем демонтируйте этот документ json в структуру, чтобы его можно было легко проанализировать.

Я зашел так далеко, но застрял в том, как go обрабатывает условный тип структуры. В ответе версии документа политики AWS данные json для StatementEntry могут быть string или []string в зависимости от документа.

Что было бы наилучшей практикой? Добавить другую структуру и использовать логику повторных попыток при обработке ошибок?

package main

import (
    "encoding/json"
    "fmt"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/iam"
    "log"
    "net/url"
)

type PolicyDocument struct {
    Version   string
    Statement []StatementEntry
}

type StatementEntry struct {
    Effect   string
    Action   []string
    Resource []string
}

func main() {
    sess, _ := session.NewSession(&aws.Config{
        Region: aws.String("us-west-2")},
    )

    svc := iam.New(sess)

    fmt.Printf("%s - %s\n", arn, *result.Policy.Description)

    results, _ := svc.ListPolicies(&iam.ListPoliciesInput{})

    for _, policy := range results.Policies {

        arn := policy.Arn
        version := policy.DefaultVersionId

        pv, _ := svc.GetPolicyVersion(&iam.GetPolicyVersionInput{
            PolicyArn: arn,
            VersionId: version,
        })

        decodedValue, err := url.QueryUnescape(aws.StringValue(pv.PolicyVersion.Document))
        if err != nil {
            log.Fatal(err)
            return
        }
        //fmt.Println(decodedValue)

        data := []byte(decodedValue)

        var doc PolicyDocument
        err1 := json.Unmarshal(data, &doc)

        if err1 != nil {
            log.Fatal(err1)
        }

        fmt.Printf("\n----\n%v\n---\n", doc)
    }
}

Пример PolicyDocuments это:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ssm:PutParameter",
                "ssm:DeleteParameter",
                "ssm:DescribeInstancePatchStates",
                "elasticloadbalancing:RegisterTargets",
                "elasticloadbalancing:DescribeTargetHealth",
                "elasticloadbalancing:DescribeTargetGroups",
                "elasticloadbalancing:DeregisterTargets",
                "ssm:GetParameter"
            ],
            "Resource": "*"
        }
    ]
}

И это (для Resource []string в StatementEntry):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListBucket"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::SageMaker"
            ]
        },
        {
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::SageMaker/*"
            ]
        }
    ]
}

Ответы [ 2 ]

2 голосов
/ 09 июня 2019

Этого можно добиться, используя пользовательский тип для среза с помощью метода Unmarshal, который сначала демарширует пустой интерфейс, а затем определяет, является ли это срез или единственная строка:

package main

import (
    "fmt"
    "encoding/json"
    "errors"
)

type container struct {
    Field customSlice
}

type customSlice []string

func (c *customSlice) UnmarshalJSON(data []byte) error {
    var tmp interface{}
    err := json.Unmarshal(data, &tmp)
    if err != nil {
        return err
    }
    slice, ok := tmp.([]interface{})
    if ok {
        for _, item := range slice {
            *c = append(*c, item.(string))
        }
        return nil
    }
    theString, ok := tmp.(string)
    if ok {
        *c = append(*c, theString)
        return nil
    }
    return errors.New("Field neither slice or string")
}

func main() {
    jsonInputSlice := `{"Field":["a"]}`
    jsonInputString := `{"Field":"a"}`
    var containerSlice container
    var containerString container
    err := json.Unmarshal([]byte(jsonInputSlice), &containerSlice)
    if err != nil {
        panic(err)
    }
    fmt.Println(containerSlice)
    err = json.Unmarshal([]byte(jsonInputString), &containerString)
    if err != nil {
        panic(err)
    }
    fmt.Println(containerString)

}

https://play.golang.org/p/mAhJBNhE1yc

1 голос
/ 09 июня 2019

Вы можете сделать это несколькими способами.Одним из способов является определение Resource как interface{} и последующий анализ.Детская площадка - https://play.golang.org/p/PiLaa0DySEj

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

type PolicyDocument struct {
    Version   string
    Statement []StatementEntry
}

type StatementEntry struct {
    Effect   string
    Action   []string
    Resource interface{}
}

func main() {

    data := `{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ssm:PutParameter",
                "ssm:DeleteParameter",
                "ssm:DescribeInstancePatchStates",
                "elasticloadbalancing:RegisterTargets",
                "elasticloadbalancing:DescribeTargetHealth",
                "elasticloadbalancing:DescribeTargetGroups",
                "elasticloadbalancing:DeregisterTargets",
                "ssm:GetParameter"
            ],
            "Resource": "*"
        }
    ]
}`

    convertPolicy(data)

    data = `{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListBucket"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::SageMaker"
            ]
        },
        {
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::SageMaker/*"
            ]
        }
    ]
}`

    convertPolicy(data)

}

func convertPolicy(data string) {
    var doc PolicyDocument
    err1 := json.Unmarshal([]byte(data), &doc)

    if err1 != nil {
        fmt.Println(err1)
    }

    //find out the type of resource string or []string
    for _, statement := range doc.Statement {
        fmt.Println("statement.Resource was of type - ", reflect.TypeOf(statement.Resource))
        if reflect.TypeOf(statement.Resource).Name() != "string" {
            // we will convert the []interface to []string
            x := statement.Resource.([]interface{})
            y := make([]string, len(x))
            for i := 0; i < len(x); i++ {
                y[i] = x[i].(string)
            }
            statement.Resource = y
            fmt.Println("statement.Resource is converted to type - ", reflect.TypeOf(statement.Resource))
        }
    }

    fmt.Printf("\n----\n%v\n---\n", doc)
}
...