From c738abc8b00a635330b520ed67cf66afac8f7c2b Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Thu, 21 Dec 2023 11:14:20 -0500 Subject: [PATCH 1/3] fix(go-sdk): send Conditions in WriteAuthorizationModel --- config/clients/go/template/client/client.mustache | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/config/clients/go/template/client/client.mustache b/config/clients/go/template/client/client.mustache index e9a0d903..114b0279 100644 --- a/config/clients/go/template/client/client.mustache +++ b/config/clients/go/template/client/client.mustache @@ -779,11 +779,7 @@ type SdkClientWriteAuthorizationModelRequestInterface interface { GetContext() _context.Context } - -type ClientWriteAuthorizationModelRequest struct { - TypeDefinitions []fgaSdk.TypeDefinition `json:"type_definitions"` - SchemaVersion string `json:"schema_version,omitempty"` -} +type ClientWriteAuthorizationModelRequest = fgaSdk.WriteAuthorizationModelRequest; type ClientWriteAuthorizationModelOptions struct { } From 67b740079ecbb7a110e60b66bb34f44fd05a92d0 Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Thu, 21 Dec 2023 11:14:45 -0500 Subject: [PATCH 2/3] chore(go-sdk): add example --- config/clients/go/config.overrides.json | 7 +- config/clients/go/template/example/Makefile | 14 + config/clients/go/template/example/README.md | 34 ++ .../go/template/example/example1/example1.go | 290 ++++++++++++++++++ .../go/template/example/example1/go.mod | 10 + .../go/template/example/example1/go.sum | 5 + 6 files changed, 359 insertions(+), 1 deletion(-) create mode 100644 config/clients/go/template/example/Makefile create mode 100644 config/clients/go/template/example/README.md create mode 100644 config/clients/go/template/example/example1/example1.go create mode 100644 config/clients/go/template/example/example1/go.mod create mode 100644 config/clients/go/template/example/example1/go.sum diff --git a/config/clients/go/config.overrides.json b/config/clients/go/config.overrides.json index 6469ab16..4af5d96a 100644 --- a/config/clients/go/config.overrides.json +++ b/config/clients/go/config.overrides.json @@ -75,6 +75,11 @@ "internal/utils/ulid_test.mustache": { "destinationFilename": "internal/utils/ulid_test.go", "templateType": "SupportingFiles" - } + }, + "example/Makefile": {}, + "example/README.md": {}, + "example/example1/example1.go": {}, + "example/example1/go.mod": {}, + "example/example1/go.sum": {} } } diff --git a/config/clients/go/template/example/Makefile b/config/clients/go/template/example/Makefile new file mode 100644 index 00000000..8f8921eb --- /dev/null +++ b/config/clients/go/template/example/Makefile @@ -0,0 +1,14 @@ +all: run + +project_name=example1 +openfga_version=latest + +restore: + go mod tidy + +run: restore + go run ${project_name}/${project_name}.go + +run-openfga: + docker pull docker.io/openfga/openfga:${openfga_version} && \ + docker run -p 8080:8080 docker.io/openfga/openfga:${openfga_version} diff --git a/config/clients/go/template/example/README.md b/config/clients/go/template/example/README.md new file mode 100644 index 00000000..23fedf38 --- /dev/null +++ b/config/clients/go/template/example/README.md @@ -0,0 +1,34 @@ +## Examples of using the OpenFGA Go SDK + +A set of examples on how to call the OpenFGA Go SDK + +### Examples +Example 1: +A bare bones example. It creates a store, and runs a set of calls against it including creating a model, writing tuples and checking for access. + + +### Running the Examples + +Prerequisites: +- `docker` +- `make` +- `go` 1.20+ + +#### Run using a published SDK + +Steps +1. Clone/Copy the example folder +2. If you have an OpenFGA server running, you can use it, otherwise run `make run-openfga` to spin up an instance (you'll need to switch to a different terminal after - don't forget to close it when done) +3. Run `make run` to run the example + +#### Run using a local unpublished SDK build + +Steps +1. Build the SDK +2. In the Example `go.mod`, uncomment out the part that replaces the remote SDK with the local one, e.g. +``` +// To refrence local build, uncomment below and run `go mod tidy` +replace github.com/openfga/go-sdk v0.3.2 => ../../ +``` +3. If you have an OpenFGA server running, you can use it, otherwise run `make run-openfga` to spin up an instance (you'll need to switch to a different terminal after - don't forget to close it when done) +4. Run `make run` to run the example \ No newline at end of file diff --git a/config/clients/go/template/example/example1/example1.go b/config/clients/go/template/example/example1/example1.go new file mode 100644 index 00000000..3d2ad43d --- /dev/null +++ b/config/clients/go/template/example/example1/example1.go @@ -0,0 +1,290 @@ +package main + +import ( + "context" + "fmt" + openfga "github.com/openfga/go-sdk" + "github.com/openfga/go-sdk/client" + "github.com/openfga/go-sdk/credentials" + "os" +) + +func mainInner() error { + + ctx := context.Background() + creds := credentials.Credentials{} + if os.Getenv("FGA_CLIENT_ID") != "" { + creds = credentials.Credentials{ + Method: credentials.CredentialsMethodClientCredentials, + Config: &credentials.Config{ + ClientCredentialsClientId: os.Getenv("FGA_CLIENT_ID"), + ClientCredentialsClientSecret: os.Getenv("FGA_CLIENT_SECRET"), + ClientCredentialsApiAudience: os.Getenv("FGA_API_AUDIENCE"), + ClientCredentialsApiTokenIssuer: os.Getenv("FGA_API_TOKEN_ISSUER"), + }, + } + } + + apiUrl := os.Getenv("FGA_API_URL") + if apiUrl == "" { + apiUrl = "http://localhost:8080" + } + fgaClient, err := client.NewSdkClient(&client.ClientConfiguration{ + ApiUrl: apiUrl, + StoreId: os.Getenv("FGA_STORE_ID"), // not needed when calling `CreateStore` or `ListStores` + AuthorizationModelId: os.Getenv("FGA_AUTHORIZATION_MODEL_ID"), // optional, recommended to be set for production + Credentials: &creds, + }) + + if err != nil { + return err + } + + // ListStores + fmt.Println("Listing Stores") + stores1, err := fgaClient.ListStores(ctx).Execute() + if err != nil { + return err + } + fmt.Printf("Stores Count: %d\n", len(stores1.GetStores())) + + // CreateStore + fmt.Println("Creating Test Store") + store, err := fgaClient.CreateStore(ctx).Body(client.ClientCreateStoreRequest{Name: "Test Store"}).Execute() + if err != nil { + return err + } + fmt.Printf("Test Store ID: %v\n", store.Id) + + // Set the store id + fgaClient.SetStoreId(store.Id) + + // ListStores after Create + fmt.Println("Listing Stores") + stores, err := fgaClient.ListStores(ctx).Execute() + if err != nil { + return err + } + fmt.Printf("Stores Count: %d\n", len(stores.Stores)) + + // GetStore + fmt.Println("Getting Current Store") + currentStore, err := fgaClient.GetStore(ctx).Execute() + if err != nil { + return err + } + fmt.Println("Current Store Name: %v\n" + currentStore.Name) + + // ReadAuthorizationModels + fmt.Println("Reading Authorization Models") + models, err := fgaClient.ReadAuthorizationModels(ctx).Execute() + if err != nil { + return err + } + fmt.Printf("Models Count: %d\n", len(models.AuthorizationModels)) + + // ReadLatestAuthorizationModel + latestAuthorizationModel, err := fgaClient.ReadLatestAuthorizationModel(ctx).Execute() + if err != nil { + return err + } + if latestAuthorizationModel.AuthorizationModel != nil { + fmt.Printf("Latest Authorization Model ID: %v\n", (*latestAuthorizationModel.AuthorizationModel).Id) + } else { + fmt.Println("Latest Authorization Model not found") + } + + // WriteAuthorizationModel + fmt.Println("Writing an Authorization Model") + body := client.ClientWriteAuthorizationModelRequest{ + SchemaVersion: "1.1", + TypeDefinitions: []openfga.TypeDefinition{ + { + Type: "user", + Relations: &map[string]openfga.Userset{}, + }, + { + Type: "document", + Relations: &map[string]openfga.Userset{ + "writer": {This: &map[string]interface{}{}}, + "viewer": {Union: &openfga.Usersets{ + Child: []openfga.Userset{ + {This: &map[string]interface{}{}}, + {ComputedUserset: &openfga.ObjectRelation{ + Object: openfga.PtrString(""), + Relation: openfga.PtrString("writer"), + }}, + }, + }}, + }, + Metadata: &openfga.Metadata{ + Relations: &map[string]openfga.RelationMetadata{ + "writer": { + DirectlyRelatedUserTypes: &[]openfga.RelationReference{ + {Type: "user"}, + {Type: "user", Condition: openfga.PtrString("ViewCountLessThan200")}, + }, + }, + "viewer": { + DirectlyRelatedUserTypes: &[]openfga.RelationReference{ + {Type: "user"}, + }, + }, + }, + }, + }, + }, + Conditions: &map[string]openfga.Condition{ + "ViewCountLessThan200": { + Name: "ViewCountLessThan200", + Expression: "ViewCount < 200", + Parameters: &map[string]openfga.ConditionParamTypeRef{ + "ViewCount": { + TypeName: openfga.INT, + }, + "Type": { + TypeName: openfga.STRING, + }, + "Name": { + TypeName: openfga.STRING, + }, + }, + }, + }, + } + authorizationModel, err := fgaClient.WriteAuthorizationModel(ctx).Body(body).Execute() + if err != nil { + return err + } + fmt.Printf("Authorization Model ID: %v\n", authorizationModel.AuthorizationModelId) + + // ReadAuthorizationModels - after Write + fmt.Println("Reading Authorization Models") + models, err = fgaClient.ReadAuthorizationModels(ctx).Execute() + if err != nil { + return err + } + fmt.Printf("Models Count: %d\n", len(models.AuthorizationModels)) + + // ReadLatestAuthorizationModel - after Write + latestAuthorizationModel, err = fgaClient.ReadLatestAuthorizationModel(ctx).Execute() + if err != nil { + return err + } + fmt.Printf("Latest Authorization Model ID: %v\n", (*latestAuthorizationModel.AuthorizationModel).Id) + + // Write + fmt.Println("Writing Tuples") + _, err = fgaClient.Write(ctx).Body(client.ClientWriteRequest{ + Writes: []client.ClientTupleKey{ + { + User: "user:anne", + Relation: "writer", + Object: "document:roadmap", + Condition: &openfga.RelationshipCondition{ + Name: "ViewCountLessThan200", + Context: &map[string]interface{}{"Name": "Roadmap", "Type": "document"}, + }, + }, + }, + }).Options(client.ClientWriteOptions{ + AuthorizationModelId: &authorizationModel.AuthorizationModelId, + }).Execute() + if err != nil { + return err + } + fmt.Println("Done Writing Tuples") + + // Set the model ID + err = fgaClient.SetAuthorizationModelId(latestAuthorizationModel.AuthorizationModel.Id) + if err != nil { + return err + } + + // Read + fmt.Println("Reading Tuples") + readTuples, err := fgaClient.Read(ctx).Execute() + if err != nil { + return err + } + fmt.Printf("Read Tuples: %v\n", readTuples) + + // ReadChanges + fmt.Println("Reading Tuple Changes") + readChangesTuples, err := fgaClient.ReadChanges(ctx).Execute() + if err != nil { + return err + } + fmt.Printf("Read Changes Tuples: %v\n", readChangesTuples) + + // Check + fmt.Println("Checking for access") + failingCheckResponse, err := fgaClient.Check(ctx).Body(client.ClientCheckRequest{ + User: "user:anne", + Relation: "viewer", + Object: "document:roadmap", + }).Execute() + if err != nil { + fmt.Printf("Failed due to: %w\n", err.Error()) + } else { + fmt.Printf("Allowed: %v\n", failingCheckResponse.Allowed) + } + + // Checking for access with context + fmt.Println("Checking for access with context") + checkResponse, err := fgaClient.Check(ctx).Body(client.ClientCheckRequest{ + User: "user:anne", + Relation: "viewer", + Object: "document:roadmap", + Context: &map[string]interface{}{"ViewCount": 100}, + }).Execute() + if err != nil { + return err + } + fmt.Printf("Allowed: %v\n", checkResponse.Allowed) + + // WriteAssertions + _, err = fgaClient.WriteAssertions(ctx).Body([]client.ClientAssertion{ + { + User: "user:carl", + Relation: "writer", + Object: "document:budget", + Expectation: true, + }, + { + User: "user:anne", + Relation: "viewer", + Object: "document:roadmap", + Expectation: false, + }, + }).Execute() + if err != nil { + return err + } + fmt.Println("Assertions updated") + + // ReadAssertions + fmt.Println("Reading Assertions") + assertions, err := fgaClient.ReadAssertions(ctx).Execute() + if err != nil { + return err + } + fmt.Printf("Assertions: %v\n", assertions) + + // DeleteStore + fmt.Println("Deleting Current Store") + _, err = fgaClient.DeleteStore(ctx).Execute() + if err != nil { + return err + } + fmt.Printf("Deleted Store: %v\n", currentStore.Name) + + return nil +} + +func main() { + if err := mainInner(); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } +} diff --git a/config/clients/go/template/example/example1/go.mod b/config/clients/go/template/example/example1/go.mod new file mode 100644 index 00000000..3af990a7 --- /dev/null +++ b/config/clients/go/template/example/example1/go.mod @@ -0,0 +1,10 @@ +module example1 + +go 1.20 + +require github.com/openfga/go-sdk v0.3.2 + +require golang.org/x/sync v0.5.0 // indirect + +// To refrence local build, uncomment below and run `go mod tidy` +//replace github.com/openfga/go-sdk v0.3.2 => ../../ \ No newline at end of file diff --git a/config/clients/go/template/example/example1/go.sum b/config/clients/go/template/example/example1/go.sum new file mode 100644 index 00000000..bb9ab40d --- /dev/null +++ b/config/clients/go/template/example/example1/go.sum @@ -0,0 +1,5 @@ +github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= +github.com/openfga/go-sdk v0.3.2 h1:sed3zQLZTLLGzdqZYQZmiBoSxKWqJD+sk4KtUb+yVpU= +github.com/openfga/go-sdk v0.3.2/go.mod h1:W4SNYMSxptGOtA9aGYxsYUmSC7LaZYP7y9qbT36ouCc= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= From f1a71e2fcfccc8d6082426d5f30f2dc2446f9841 Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Thu, 21 Dec 2023 11:23:20 -0500 Subject: [PATCH 3/3] release(go-sdk): v0.3.3 --- config/clients/go/CHANGELOG.md.mustache | 7 +++++++ config/clients/go/config.overrides.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/config/clients/go/CHANGELOG.md.mustache b/config/clients/go/CHANGELOG.md.mustache index de976b21..abe17fc2 100644 --- a/config/clients/go/CHANGELOG.md.mustache +++ b/config/clients/go/CHANGELOG.md.mustache @@ -1,5 +1,12 @@ # Changelog +## v0.3.3 + +### [0.3.3](https://{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/compare/v0.3.2...v0.3.3) (2023-12-21) + +- fix: WriteAuthorizationModel was not passing conditions to API +- chore: add example project + ## v0.3.2 ### [0.3.2](https://{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/compare/v0.3.1...v0.3.2) (2023-12-20) diff --git a/config/clients/go/config.overrides.json b/config/clients/go/config.overrides.json index 4af5d96a..542032de 100644 --- a/config/clients/go/config.overrides.json +++ b/config/clients/go/config.overrides.json @@ -2,7 +2,7 @@ "sdkId": "go", "gitRepoId": "go-sdk", "packageName": "openfga", - "packageVersion": "0.3.2", + "packageVersion": "0.3.3", "packageDescription": "Go SDK for OpenFGA", "packageDetailedDescription": "This is an autogenerated Go SDK for OpenFGA. It provides a wrapper around the [OpenFGA API definition](https://openfga.dev/api).", "fossaComplianceNoticeId": "41c01c64-f74a-414a-9e39-7aeca87bc47b",