Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add validate cli #18

Merged
merged 3 commits into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,40 @@ go 1.20
require (
cloud.google.com/go/iam v0.12.0
cloud.google.com/go/resourcemanager v1.5.0
github.com/abcxyz/pkg v0.3.0
github.com/abcxyz/pkg v0.3.1-0.20230410214104-4b190bd00925
github.com/google/go-cmp v0.5.9
github.com/googleapis/gax-go/v2 v2.7.0
github.com/sethvargo/go-retry v0.2.4
google.golang.org/api v0.110.0
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4
google.golang.org/grpc v1.53.0
google.golang.org/protobuf v1.30.0
gopkg.in/yaml.v3 v3.0.1
)

require (
cloud.google.com/go v0.110.0 // indirect
cloud.google.com/go/compute v1.18.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/longrunning v0.4.1 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/goccy/go-json v0.10.1 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/kr/text v0.1.0 // indirect
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.4 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/jwx/v2 v2.0.8 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/oauth2 v0.5.0 // indirect
Expand Down
21 changes: 19 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+
cloud.google.com/go/resourcemanager v1.5.0 h1:m2RQU8UzBCIO+wsdwoehpuyAaF1i7ahFhj7TLocxuJE=
cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/abcxyz/pkg v0.3.0 h1:xygbuwjJBmiC90ieYXQyRdHqG8HzNrwgDrqMxojOsGE=
github.com/abcxyz/pkg v0.3.0/go.mod h1:9x3KtJ6HorqpFPHd0AyS4g72SMsbmvbVoJOno4+mhbg=
github.com/abcxyz/pkg v0.3.1-0.20230410214104-4b190bd00925 h1:JBNe8wNNjKdWwe1ESV426GHiUcg0cIXY0ZDMdH212H0=
github.com/abcxyz/pkg v0.3.1-0.20230410214104-4b190bd00925/go.mod h1:eGVWC+pDBOUVNtGwQwkllVOctUCHCnLHkDyRpDE9x/g=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
Expand Down Expand Up @@ -61,6 +63,9 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ=
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
Expand All @@ -74,6 +79,9 @@ github.com/lestrrat-go/jwx/v2 v2.0.8/go.mod h1:zLxnyv9rTlEvOUHbc48FAfIL8iYu2hHvI
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
Expand All @@ -89,6 +97,13 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
Expand Down Expand Up @@ -121,6 +136,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
Expand Down Expand Up @@ -167,6 +183,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
95 changes: 95 additions & 0 deletions pkg/cli/iam_validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2023 The Authors (see AUTHORS file)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cli

import (
"context"
"fmt"

"github.com/abcxyz/access-on-demand/apis/v1alpha1"
"github.com/abcxyz/access-on-demand/pkg/requestutil"
"github.com/abcxyz/pkg/cli"
)

var _ cli.Command = (*IAMValidateCommand)(nil)

// IAMValidateCommand validates IAM requests.
type IAMValidateCommand struct {
cli.BaseCommand

flagPath string
}

func (c *IAMValidateCommand) Desc() string {
return `Validate the IAM request YAML file at the given path`
}

func (c *IAMValidateCommand) Help() string {
return `
Usage: {{ COMMAND }} [options]

Validate the IAM request YAML file at the given path:

aod iam validate -path "/path/to/file.yaml"
`
}

func (c *IAMValidateCommand) Flags() *cli.FlagSet {
set := cli.NewFlagSet()

// Command options
f := set.NewSection("COMMAND OPTIONS")

f.StringVar(&cli.StringVar{
Name: "path",
Target: &c.flagPath,
Example: "/path/to/file.yaml",
Usage: `The path of IAM request file, in YAML format.`,
})

return set
}

func (c *IAMValidateCommand) Run(ctx context.Context, args []string) error {
f := c.Flags()
if err := f.Parse(args); err != nil {
return fmt.Errorf("failed to parse flags: %w", err)
}
args = f.Args()
if len(args) > 0 {
return fmt.Errorf("unexpected arguments: %q", args)
}

if c.flagPath == "" {
return fmt.Errorf("path is required")
}

return c.validate(ctx)
}

func (c *IAMValidateCommand) validate(ctx context.Context) error {
// Read request from YAML file.
req, err := requestutil.ReadFromPath(c.flagPath)
if err != nil {
return fmt.Errorf("failed to read %T: %w", req, err)
}

if err := v1alpha1.ValidateIAMRequest(req); err != nil {
return fmt.Errorf("failed to validate %T: %w", req, err)
}
c.Outf("Successfully validated IAM request")

return nil
}
118 changes: 118 additions & 0 deletions pkg/cli/iam_validate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2023 The Authors (see AUTHORS file)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cli

import (
"context"
"os"
"path/filepath"
"strings"
"testing"

"github.com/abcxyz/pkg/logging"
"github.com/abcxyz/pkg/testutil"
"github.com/google/go-cmp/cmp"
)

func TestIAMValidateCommand(t *testing.T) {
t.Parallel()

// Set up IAM request file.
requestFileContentByName := map[string]string{
"valid-request.yaml": `
policies:
- resource: organizations/foo
bindings:
- members:
- user:test-org-userA@example.com
- user:test-org-userB@example.com
role: roles/cloudkms.cryptoOperator
`,
"invalid-request.yaml": `
policies:
- resource: organizations/foo
bindings:
- members:
- group:test-org-group@example.com
- user:test-org-userB@example.com
role: roles/cloudkms.cryptoOperator
`,
"invalid-yaml.yaml": `bananas`,
}
dir := t.TempDir()
for name, content := range requestFileContentByName {
path := filepath.Join(dir, name)
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
t.Fatal(err)
}
}

cases := []struct {
name string
args []string
fileData []byte
expOut string
expErr string
}{
{
name: "success",
args: []string{"-path", filepath.Join(dir, "valid-request.yaml")},
expOut: "Successfully validated IAM request",
},
{
name: "invalid_yaml",
args: []string{"-path", filepath.Join(dir, "invalid-yaml")},
expErr: "failed to read *v1alpha1.IAMRequest",
},
{
name: "invalid_request",
args: []string{"-path", filepath.Join(dir, "invalid-request.yaml")},
expErr: "failed to validate *v1alpha1.IAMRequest",
},
{
name: "unexpected_args",
args: []string{"foo"},
expErr: `unexpected arguments: ["foo"]`,
},
{
name: "missing_path",
args: []string{},
expErr: `path is required`,
},
}

for _, tc := range cases {
tc := tc

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

ctx := logging.WithLogger(context.Background(), logging.TestLogger(t))

var cmd IAMValidateCommand
_, stdout, _ := cmd.Pipe()

args := append([]string{}, tc.args...)

err := cmd.Run(ctx, args)
if diff := testutil.DiffErrString(err, tc.expErr); diff != "" {
t.Errorf("Process(%+v) got error diff (-want, +got):\n%s", tc.name, diff)
}
if diff := cmp.Diff(strings.TrimSpace(tc.expOut), strings.TrimSpace(stdout.String())); diff != "" {
t.Errorf("Process(%+v) got output diff (-want, +got):\n%s", tc.name, diff)
}
})
}
}
47 changes: 47 additions & 0 deletions pkg/requestutil/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2023 The Authors (see AUTHORS file)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package requestutil contains common requestutil functions to parse AOD request files.
package requestutil

import (
"fmt"
"io"
"os"

"github.com/abcxyz/access-on-demand/apis/v1alpha1"
"gopkg.in/yaml.v3"
)

// ReadFromPath reads a YAML file at the given path and unmarshal it to
// IAMRequest.
func ReadFromPath(path string) (*v1alpha1.IAMRequest, error) {
sqin2019 marked this conversation as resolved.
Show resolved Hide resolved
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to read file at %q, %w", path, err)
}
defer f.Close()

data, err := io.ReadAll(io.LimitReader(f, 64*1_000))
if err != nil {
return nil, fmt.Errorf("failed to read file content at %q, %w", path, err)
}

var req v1alpha1.IAMRequest
if err := yaml.Unmarshal(data, &req); err != nil {
return nil, fmt.Errorf("failed to unmarshal yaml to %T: %w", req, err)
}

return &req, nil
}
Loading