Skip to content

Commit

Permalink
Revert "fix!: temporarily remove validate and drop dependency on Open…
Browse files Browse the repository at this point in the history
…FGA (#117)
  • Loading branch information
rhamzeh committed Aug 11, 2023
2 parents 82b96c4 + ab67195 commit 890d6dd
Show file tree
Hide file tree
Showing 9 changed files with 331 additions and 28 deletions.
2 changes: 1 addition & 1 deletion .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ linters-settings:
- github.com/muesli/roff
- github.com/nwidger/jsoncolor
- github.com/oklog/ulid/v2
- github.com/openfga/api
- github.com/openfga/cli
- github.com/openfga/go-sdk
- github.com/openfga/language
- github.com/openfga/openfga
- github.com/spf13/cobra
- github.com/spf13/pflag
- github.com/spf13/viper
- buf.build/gen/go/openfga/api/protocolbuffers/go/openfga
- google.golang.org/protobuf/encoding/protojson
- gopkg.in/yaml.v3
test:
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ A cross-platform CLI to interact with an OpenFGA server
- [Write Authorization Model](#write-authorization-model)
- [Read a Single Authorization Model](#read-a-single-authorization-model)
- [Read the Latest Authorization Model](#read-the-latest-authorization-model)
- [Validate an Authorization Model](#validate-an-authorization-model)
- [Transform an Authorization Model](#transform-an-authorization-model)
- [Relationship Tuples](#relationship-tuples)
- [Read Relationship Tuple Changes (Watch)](#read-relationship-tuple-changes-watch)
Expand Down Expand Up @@ -277,6 +278,7 @@ fga store **delete**
| [Read Authorization Models](#read-authorization-models) | `list` | `--store-id` | `fga model list --store-id=01H0H015178Y2V4CX10C2KGHF4` |
| [Write Authorization Model ](#write-authorization-model) | `write` | `--store-id`, `--file` | `fga model write --store-id=01H0H015178Y2V4CX10C2KGHF4 --file model.fga` |
| [Read a Single Authorization Model](#read-a-single-authorization-model) | `get` | `--store-id`, `--model-id` | `fga model get --store-id=01H0H015178Y2V4CX10C2KGHF4 --model-id=01GXSA8YR785C4FYS3C0RTG7B1` |
| [Validate an Authorization Model](#validate-an-authorization-model) | `validate` | `--file`, `--format` | `fga model validate --file model.fga` |
| [Transform an Authorization Model](#transform-an-authorization-model) | `transform` | `--file`, `--input-format` | `fga model transform --file model.json` |
Expand Down Expand Up @@ -383,6 +385,36 @@ type document
define can_view: [user]
```
##### Validate an Authorization Model
###### Command
fga model **validate**
###### Parameters
* `--file`: File containing the authorization model.
* `--format`: Authorization model input format. Can be "fga" or "json". Defaults to the file extension if provided (optional)
###### Example
`fga model validate --file model.json`
###### JSON Response
* Valid model with an ID
```json5
{"id":"01GPGWB8R33HWXS3KK6YG4ETGH","created_at":"2023-01-11T16:59:22Z","is_valid":true}
```
* Valid model without an ID
```json5
{"is_valid":true}
```
* Invalid model with an ID
```json5
{"id":"01GPGTVEH5NYTQ19RYFQKE0Q4Z","created_at":"2023-01-11T16:33:15Z","is_valid":false,"error":"invalid schema version"}
```
* Invalid model without an ID
```json5
{"is_valid":false,"error":"the relation type 'employee' on 'member' in object type 'group' is not valid"}
```
##### Transform an Authorization Model
###### Command
Expand Down
1 change: 1 addition & 0 deletions cmd/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func init() {
ModelCmd.AddCommand(writeCmd)
ModelCmd.AddCommand(listCmd)
ModelCmd.AddCommand(getCmd)
ModelCmd.AddCommand(validateCmd)
ModelCmd.AddCommand(transformCmd)
ModelCmd.PersistentFlags().String("store-id", "", "Store ID")
}
133 changes: 133 additions & 0 deletions cmd/model/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
Copyright © 2023 OpenFGA
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 model

import (
"context"
"time"

"github.com/oklog/ulid/v2"
pb "github.com/openfga/api/proto/openfga/v1"
"github.com/openfga/cli/internal/authorizationmodel"
"github.com/openfga/cli/internal/output"
openfga "github.com/openfga/go-sdk"
"github.com/openfga/openfga/pkg/typesystem"
"github.com/spf13/cobra"
"google.golang.org/protobuf/encoding/protojson"
)

type validationResult struct {
ID string `json:"id,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
IsValid bool `json:"is_valid"`
Error *string `json:"error,omitempty"`
}

func validate(inputModel authorizationmodel.AuthzModel) validationResult {
model := &pb.AuthorizationModel{}
output := validationResult{
IsValid: true,
}

modelJSONString, err := inputModel.GetAsJSONString()
if err != nil {
output.IsValid = false
errorString := "unable to parse json input"
output.Error = &errorString

return output
}

err = protojson.Unmarshal([]byte(*modelJSONString), model)
if err != nil {
output.IsValid = false
errorString := "unable to parse json input"
output.Error = &errorString

return output
}

if model.Id != "" {
output.ID = model.Id

modelID, err := ulid.Parse(model.Id)
if err != nil {
output.IsValid = false
errorString := "unable to parse id: invalid ulid format"
output.Error = &errorString

return output
}

createdAt := time.Unix(int64(modelID.Time()/1_000), 0).UTC() //nolint:gomnd
output.CreatedAt = &createdAt
}

if _, err = typesystem.NewAndValidate(context.Background(), model); err != nil {
errString := err.Error()
output.IsValid = false
output.Error = &errString
}

return output
}

// validateCmd represents the validate command.
var validateCmd = &cobra.Command{
Use: "validate",
Short: "Validate Authorization Model",
Long: "Validates that an authorization model is valid.",
Example: `fga model validate --file model.json`,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
var inputModel string
if err := authorizationmodel.ReadFromInputFileOrArg(
cmd,
args,
"file",
false,
&inputModel,
openfga.PtrString(""),
&validateInputFormat); err != nil {
return err //nolint:wrapcheck
}

authModel := authorizationmodel.AuthzModel{}
var err error

if validateInputFormat == authorizationmodel.ModelFormatJSON {
err = authModel.ReadFromJSONString(inputModel)
} else {
err = authModel.ReadFromDSLString(inputModel)
}

if err != nil {
return err //nolint:wrapcheck
}

response := validate(authModel)

return output.Display(response) //nolint:wrapcheck
},
}

var validateInputFormat = authorizationmodel.ModelFormatDefault

func init() {
validateCmd.Flags().String("file", "", "File Name. The file should have the model in the JSON or DSL format")
validateCmd.Flags().Var(&validateInputFormat, "format", `Authorization model input format. Can be "fga" or "json"`)
}
96 changes: 96 additions & 0 deletions cmd/model/validate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package model

import (
"reflect"
"testing"

"github.com/openfga/cli/internal/authorizationmodel"
openfga "github.com/openfga/go-sdk"
)

func TestValidateCmdWithArgs(t *testing.T) {
t.Parallel()
validateCmd.SetArgs([]string{`{"schema_version":"1.1"}`})

if err := validateCmd.Execute(); err != nil {
t.Errorf("failed to execute validateCmd")
}
}

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

type validationTest struct {
Name string
Input string
IsValid bool
ExpectedOutput validationResult
}

tests := []validationTest{
{
Name: "missing schema version",
Input: "{",
IsValid: false,
ExpectedOutput: validationResult{
IsValid: false,
Error: openfga.PtrString("unable to parse json input"),
},
},
{
Name: "missing schema version",
Input: `{"id":"abcde","schema_version":"1.1"}`,
IsValid: false,
ExpectedOutput: validationResult{
ID: "abcde",
IsValid: false,
Error: openfga.PtrString("unable to parse id: invalid ulid format"),
},
},
{
Name: "missing schema version",
Input: "{}",
IsValid: false,
ExpectedOutput: validationResult{
IsValid: false,
Error: openfga.PtrString("invalid schema version"),
},
},
{
Name: "invalid schema version",
Input: `{"schema_version":"1.0"}`,
IsValid: false,
ExpectedOutput: validationResult{
IsValid: false,
Error: openfga.PtrString("invalid schema version"),
},
},
{
Name: "only schema",
Input: `{"schema_version":"1.1"}`,
IsValid: true,
ExpectedOutput: validationResult{
IsValid: true,
},
},
}

for index := 0; index < len(tests); index++ {
test := tests[index]

t.Run(test.Name, func(t *testing.T) {
t.Parallel()

model := authorizationmodel.AuthzModel{}
err := model.ReadFromJSONString(test.Input)
if err != nil {
return
}
output := validate(model)

if !reflect.DeepEqual(output, test.ExpectedOutput) {
t.Fatalf("Expect output %v actual %v", test.ExpectedOutput, output)
}
})
}
}
21 changes: 16 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ module github.com/openfga/cli
go 1.20

require (
buf.build/gen/go/openfga/api/protocolbuffers/go v1.31.0-20230801152118-8e3dffda6e23.1
github.com/golang/mock v1.6.0
github.com/mattn/go-isatty v0.0.19
github.com/muesli/mango-cobra v1.2.0
github.com/muesli/roff v0.1.0
github.com/nwidger/jsoncolor v0.3.2
github.com/oklog/ulid/v2 v2.1.0
github.com/openfga/api/proto v0.0.0-20230801154117-db20ad164368
github.com/openfga/go-sdk v0.2.3-0.20230710203920-f6922b2d8c6d
github.com/openfga/language/pkg/go v0.0.0-20230801132711-2f31356dd7c8
github.com/openfga/language/pkg/go v0.0.0-20230811152251-92f6edfc4ce9
github.com/openfga/openfga v1.3.1-0.20230810204353-968f851732b3
github.com/spf13/cobra v1.7.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.16.0
Expand All @@ -20,15 +21,19 @@ require (
)

require (
buf.build/gen/go/envoyproxy/protoc-gen-validate/protocolbuffers/go v1.31.0-20221025150516-6607b10f00ed.1 // indirect
buf.build/gen/go/grpc-ecosystem/grpc-gateway/protocolbuffers/go v1.31.0-20230727201445-fed2dcdcfd69.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.2 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/karlseguin/ccache/v3 v3.0.3 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
Expand All @@ -39,11 +44,17 @@ require (
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 // indirect
go.opentelemetry.io/otel v1.16.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
google.golang.org/genproto v0.0.0-20230807174057-1744710a1577 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230807174057-1744710a1577 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 // indirect
google.golang.org/grpc v1.57.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
Loading

0 comments on commit 890d6dd

Please sign in to comment.