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"

This reverts commit fb25b96.
  • Loading branch information
rhamzeh committed Aug 1, 2023
1 parent b66b641 commit faa1878
Show file tree
Hide file tree
Showing 7 changed files with 534 additions and 11 deletions.
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"

pb "buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1"
"github.com/oklog/ulid/v2"
"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 {

Check failure on line 80 in cmd/model/validate.go

View workflow job for this annotation

GitHub Actions / Lints

cannot use model (variable of type *"buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1".AuthorizationModel) as *"go.buf.build/openfga/go/openfga/api/openfga/v1".AuthorizationModel value in argument to typesystem.NewAndValidate) (typecheck)

Check failure on line 80 in cmd/model/validate.go

View workflow job for this annotation

GitHub Actions / Lints

cannot use model (variable of type *"buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1".AuthorizationModel) as *"go.buf.build/openfga/go/openfga/api/openfga/v1".AuthorizationModel value in argument to typesystem.NewAndValidate (typecheck)

Check failure on line 80 in cmd/model/validate.go

View workflow job for this annotation

GitHub Actions / Lints

cannot use model (variable of type *"buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1".AuthorizationModel) as *"go.buf.build/openfga/go/openfga/api/openfga/v1".AuthorizationModel value in argument to typesystem.NewAndValidate) (typecheck)

Check failure on line 80 in cmd/model/validate.go

View workflow job for this annotation

GitHub Actions / Security Audits

cannot use model (variable of type *"buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1".AuthorizationModel) as *"go.buf.build/openfga/go/openfga/api/openfga/v1".AuthorizationModel value in argument to typesystem.NewAndValidate

Check failure on line 80 in cmd/model/validate.go

View workflow job for this annotation

GitHub Actions / Tests

cannot use model (variable of type *"buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1".AuthorizationModel) as *"go.buf.build/openfga/go/openfga/api/openfga/v1".AuthorizationModel value in argument to typesystem.NewAndValidate

Check failure on line 80 in cmd/model/validate.go

View workflow job for this annotation

GitHub Actions / Test Release Process

cannot use model (variable of type *"buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1".AuthorizationModel) as *"go.buf.build/openfga/go/openfga/api/openfga/v1".AuthorizationModel value in argument to typesystem.NewAndValidate

Check failure on line 80 in cmd/model/validate.go

View workflow job for this annotation

GitHub Actions / Tests

cannot use model (variable of type *"buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1".AuthorizationModel) as *"go.buf.build/openfga/go/openfga/api/openfga/v1".AuthorizationModel value in argument to typesystem.NewAndValidate

Check failure on line 80 in cmd/model/validate.go

View workflow job for this annotation

GitHub Actions / Security Audits

cannot use model (variable of type *"buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1".AuthorizationModel) as *"go.buf.build/openfga/go/openfga/api/openfga/v1".AuthorizationModel value in argument to typesystem.NewAndValidate

Check failure on line 80 in cmd/model/validate.go

View workflow job for this annotation

GitHub Actions / Lints

cannot use model (variable of type *"buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1".AuthorizationModel) as *"go.buf.build/openfga/go/openfga/api/openfga/v1".AuthorizationModel value in argument to typesystem.NewAndValidate) (typecheck)

Check failure on line 80 in cmd/model/validate.go

View workflow job for this annotation

GitHub Actions / Lints

cannot use model (variable of type *"buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1".AuthorizationModel) as *"go.buf.build/openfga/go/openfga/api/openfga/v1".AuthorizationModel value in argument to typesystem.NewAndValidate (typecheck)

Check failure on line 80 in cmd/model/validate.go

View workflow job for this annotation

GitHub Actions / Lints

cannot use model (variable of type *"buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1".AuthorizationModel) as *"go.buf.build/openfga/go/openfga/api/openfga/v1".AuthorizationModel value in argument to typesystem.NewAndValidate) (typecheck)

Check failure on line 80 in cmd/model/validate.go

View workflow job for this annotation

GitHub Actions / Test Release Process

cannot use model (variable of type *"buf.build/gen/go/openfga/api/protocolbuffers/go/openfga/v1".AuthorizationModel) as *"go.buf.build/openfga/go/openfga/api/openfga/v1".AuthorizationModel value in argument to typesystem.NewAndValidate
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)
}
})
}
}
15 changes: 15 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/oklog/ulid/v2 v2.1.0
github.com/openfga/go-sdk v0.2.3-0.20230710203920-f6922b2d8c6d
github.com/openfga/language/pkg/go v0.0.0-20230731233450-dec0847af884
github.com/openfga/openfga v1.2.0
github.com/spf13/cobra v1.7.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.16.0
Expand All @@ -25,10 +26,15 @@ require (
github.com/antlr4-go/antlr/v4 v4.13.0 // 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.0.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 +45,20 @@ 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
go.buf.build/openfga/go/envoyproxy/protoc-gen-validate v1.2.8 // indirect
go.buf.build/openfga/go/grpc-ecosystem/grpc-gateway v1.2.51 // indirect
go.buf.build/openfga/go/openfga/api v1.2.58 // 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-20230728194245-b0cb94b80691 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
google.golang.org/genproto v0.0.0-20230731193218-e0aa005b6bdf // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230731193218-e0aa005b6bdf // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf // indirect
google.golang.org/grpc v1.57.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
Loading

0 comments on commit faa1878

Please sign in to comment.