Documentation
¶
Overview ¶
Package ofga provides utilities for interacting with an OpenFGA instance.
Index ¶
- func AuthModelFromJSON(data []byte) (*openfga.AuthorizationModel, error)
- type Client
- func (c *Client) AddRelation(ctx context.Context, tuples ...Tuple) error
- func (c *Client) AddRelationIdempotent(ctx context.Context, tuples ...Tuple) error
- func (c *Client) AddRemoveRelations(ctx context.Context, addTuples, removeTuples []Tuple) error
- func (c *Client) AddRemoveRelationsIdempotent(ctx context.Context, addTuples, removeTuples []Tuple) error
- func (c *Client) AuthModelID() string
- func (c *Client) CheckRelation(ctx context.Context, tuple Tuple, contextualTuples ...Tuple) (bool, error)
- func (c *Client) CheckRelationWithTracing(ctx context.Context, tuple Tuple, contextualTuples ...Tuple) (bool, error)
- func (c *Client) CreateAuthModel(ctx context.Context, authModel *openfga.AuthorizationModel) (string, error)
- func (c *Client) CreateStore(ctx context.Context, name string) (string, error)
- func (c *Client) FindAccessibleObjectsByRelation(ctx context.Context, tuple Tuple, contextualTuples ...Tuple) ([]Entity, error)
- func (c *Client) FindMatchingTuples(ctx context.Context, tuple Tuple, pageSize int32, continuationToken string) ([]TimestampedTuple, string, error)
- func (c *Client) FindUsersByRelation(ctx context.Context, tuple Tuple) ([]Entity, error)
- func (c *Client) GetAuthModel(ctx context.Context, ID string) (openfga.AuthorizationModel, error)
- func (c *Client) ListAuthModels(ctx context.Context, pageSize int32, continuationToken string) (openfga.ReadAuthorizationModelsResponse, error)
- func (c *Client) ListStores(ctx context.Context, pageSize int32, continuationToken string) (openfga.ListStoresResponse, error)
- func (c *Client) ReadChanges(ctx context.Context, entityType string, pageSize int32, ...) (openfga.ReadChangesResponse, error)
- func (c *Client) RemoveRelation(ctx context.Context, tuples ...Tuple) error
- func (c *Client) RemoveRelationIdempotent(ctx context.Context, tuples ...Tuple) error
- func (c *Client) SetAuthModelID(authModelID string)
- func (c *Client) SetStoreID(storeID string)
- func (c *Client) StoreID() string
- type Entity
- type Kind
- type OpenFGAParams
- type OpenFgaApi
- type Relation
- type TimestampedTuple
- type Tuple
- func (t Tuple) ToOpenFGACheckRequestTupleKey() *openfga.CheckRequestTupleKey
- func (t Tuple) ToOpenFGAExpandRequestTupleKey() *openfga.ExpandRequestTupleKey
- func (t Tuple) ToOpenFGAReadRequestTupleKey() *openfga.ReadRequestTupleKey
- func (t Tuple) ToOpenFGATupleKey() *openfga.TupleKey
- func (t Tuple) ToOpenFGATupleKeyWithoutCondition() *openfga.TupleKeyWithoutCondition
Examples ¶
- AuthModelFromJSON
- Client.AddRelation
- Client.AddRelation (Multiple)
- Client.AddRelationIdempotent
- Client.AddRelationIdempotent (Multiple)
- Client.AddRemoveRelations
- Client.AddRemoveRelationsIdempotent
- Client.CheckRelation
- Client.CheckRelation (ContextualTuples)
- Client.CheckRelationWithTracing
- Client.CheckRelationWithTracing (ContextualTuples)
- Client.CreateAuthModel
- Client.CreateStore
- Client.FindAccessibleObjectsByRelation
- Client.FindMatchingTuples
- Client.FindUsersByRelation
- Client.GetAuthModel
- Client.ListAuthModels
- Client.ListStores
- Client.ReadChanges
- Client.ReadChanges (ForSpecificEntityType)
- Client.RemoveRelation
- Client.RemoveRelation (Multiple)
- Client.RemoveRelationIdempotent
- Client.RemoveRelationIdempotent (Multiple)
- NewClient
- NewClient (Telemetry)
- ParseEntity
- ParseEntity (EntitySet)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AuthModelFromJSON ¶ added in v0.2.0
func AuthModelFromJSON(data []byte) (*openfga.AuthorizationModel, error)
AuthModelFromJSON converts the input json representation of an authorization model into an openfga.AuthorizationModel that can be used with the API.
Example ¶
package main
import (
"fmt"
"github.com/canonical/ofga"
)
func main() {
// Assume we have the following auth model
json := []byte(`{
"type_definitions": [
{
"type": "user",
"relations": {}
}
],
"schema_version": "1.1"
}`)
// Convert json into internal representation
model, err := ofga.AuthModelFromJSON(json)
if err != nil {
// Handle err
}
// Use the model
fmt.Println(model)
}
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a wrapper over the client provided by OpenFGA (https://github.com/openfga/go-sdk). The wrapper contains convenient utility methods for interacting with OpenFGA. It also ensures that it is able to connect to the specified OpenFGA instance, and verifies the existence of a Store and AuthorizationModel if such IDs are provided during configuration.
func NewClient ¶
func NewClient(ctx context.Context, p OpenFGAParams) (*Client, error)
NewClient returns a wrapped OpenFGA API client ensuring all calls are made to the provided authorisation model (id) and returns what is necessary.
Example ¶
package main
import (
"context"
"fmt"
"os"
"github.com/canonical/ofga"
)
func main() {
client, err := ofga.NewClient(context.Background(), ofga.OpenFGAParams{
Scheme: os.Getenv("OPENFGA_API_SCHEME"), // defaults to `https` if not specified.
Host: os.Getenv("OPENFGA_API_HOST"),
Port: os.Getenv("OPENFGA_API_PORT"),
Token: os.Getenv("SECRET_TOKEN"), // Optional, based on the OpenFGA instance configuration.
StoreID: os.Getenv("OPENFGA_STORE_ID"), // Required only when connecting to a pre-existing store.
AuthModelID: os.Getenv("OPENFGA_AUTH_MODEL_ID"), // Required only when connecting to a pre-existing auth model.
})
if err != nil {
// Handle err
return
}
fmt.Print(client.AuthModelID())
}
Example (Telemetry) ¶
package main
import (
"context"
"fmt"
"os"
"github.com/openfga/go-sdk/telemetry"
"github.com/canonical/ofga"
)
func main() {
client, err := ofga.NewClient(context.Background(), ofga.OpenFGAParams{
Scheme: os.Getenv("OPENFGA_API_SCHEME"), // defaults to `https` if not specified.
Host: os.Getenv("OPENFGA_API_HOST"),
Port: os.Getenv("OPENFGA_API_PORT"),
Token: os.Getenv("SECRET_TOKEN"), // Optional, based on the OpenFGA instance configuration.
StoreID: os.Getenv("OPENFGA_STORE_ID"), // Required only when connecting to a pre-existing store.
AuthModelID: os.Getenv("OPENFGA_AUTH_MODEL_ID"), // Required only when connecting to a pre-existing auth model.
Telemetry: telemetry.DefaultTelemetryConfiguration(), // Optional, used to enable OpenTelemetry tracing.
})
if err != nil {
// Handle err
return
}
fmt.Print(client.AuthModelID())
}
func (*Client) AddRelation ¶
AddRelation adds the specified relation(s) between the objects & targets as specified by the given tuple(s).
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Add a relationship tuple
err := client.AddRelation(context.Background(), ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
})
if err != nil {
// Handle err
fmt.Printf("err: %v", err)
return
}
}
Example (Multiple) ¶
package main
import (
"context"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Add relationship tuples
err := client.AddRelation(context.Background(),
ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "456"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "789"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
)
if err != nil {
// Handle err
return
}
}
func (*Client) AddRelationIdempotent ¶ added in v0.16.0
AddRelationIdempotent adds the specified relation(s) between the objects & targets as specified by the given tuple(s), and ignores duplicate tuples that already exist in the store. Note: Duplicates within the same request are not allowed and will cause an error. It requires OpenFGA server version >= 1.10.0.
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Add a relationship tuple, ignoring duplicates if it already exists.
// Requires OpenFGA server version >= 1.10.0.
err := client.AddRelationIdempotent(context.Background(), ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
})
if err != nil {
fmt.Printf("err: %v", err)
return
}
}
Example (Multiple) ¶
package main
import (
"context"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Add relationship tuples idempotently, ignoring duplicates.
// Requires OpenFGA server version >= 1.10.0.
err := client.AddRelationIdempotent(context.Background(),
ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "456"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "789"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
)
if err != nil {
// Handle err
return
}
}
func (*Client) AddRemoveRelations ¶ added in v0.7.0
AddRemoveRelations adds and removes the specified relation tuples in a single atomic write operation. If you want to solely add relations or solely remove relations, consider using the AddRelation or RemoveRelation methods instead.
Example ¶
package main
import (
"context"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Add and remove tuples to modify a user's relation with a document
// from viewer to editor.
addTuples := []ofga.Tuple{{
Object: &ofga.Entity{Kind: "user", ID: "456"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
}}
removeTuples := []ofga.Tuple{{
Object: &ofga.Entity{Kind: "user", ID: "456"},
Relation: "viewer",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
}}
// Add and remove tuples atomically.
err := client.AddRemoveRelations(context.Background(), addTuples, removeTuples)
if err != nil {
// Handle err
return
}
}
func (*Client) AddRemoveRelationsIdempotent ¶ added in v0.16.0
func (c *Client) AddRemoveRelationsIdempotent(ctx context.Context, addTuples, removeTuples []Tuple) error
AddRemoveRelationsIdempotent adds and removes the specified relation tuples in a single atomic write operation. If you want to solely add relations or solely remove relations, consider using the AddRelation or RemoveRelation methods instead. This method ignores missing tuples during removal and duplicate tuples during addition that already exist in the store. Note: Duplicates within the same request are not allowed and will cause an error. It requires OpenFGA server version >= 1.10.0.
Example ¶
package main
import (
"context"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Add and remove tuples to modify a user's relation with a document
// from viewer to editor, ignoring duplicates and missing tuples.
// Requires OpenFGA server version >= 1.10.0.
addTuples := []ofga.Tuple{{
Object: &ofga.Entity{Kind: "user", ID: "456"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
}}
removeTuples := []ofga.Tuple{{
Object: &ofga.Entity{Kind: "user", ID: "456"},
Relation: "viewer",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
}}
// Add and remove tuples atomically and idempotently.
err := client.AddRemoveRelationsIdempotent(context.Background(), addTuples, removeTuples)
if err != nil {
// Handle err
return
}
}
func (*Client) AuthModelID ¶ added in v0.9.0
AuthModelID returns the currently configured authorization model ID.
func (*Client) CheckRelation ¶
func (c *Client) CheckRelation(ctx context.Context, tuple Tuple, contextualTuples ...Tuple) (bool, error)
CheckRelation checks whether the specified relation exists (either directly or indirectly) between the object and the target specified by the tuple.
Additionally, this method allows specifying contextualTuples to augment the check request with temporary, non-persistent relationship tuples that exist solely within the scope of this specific check. Contextual tuples are not written to the store but are taken into account for this particular check request as if they were present in the store.
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Check if the relation exists
allowed, err := client.CheckRelation(context.Background(), ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
})
if err != nil {
// Handle err
return
}
fmt.Printf("allowed: %v", allowed)
}
Example (ContextualTuples) ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
contextualTuples := []ofga.Tuple{{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
}}
// Check if the relation exists
allowed, err := client.CheckRelation(context.Background(), ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
contextualTuples...,
)
if err != nil {
// Handle err
return
}
fmt.Printf("allowed: %v", allowed)
}
func (*Client) CheckRelationWithTracing ¶ added in v0.3.0
func (c *Client) CheckRelationWithTracing(ctx context.Context, tuple Tuple, contextualTuples ...Tuple) (bool, error)
CheckRelationWithTracing verifies that the specified relation exists (either directly or indirectly) between the object and the target as specified by the tuple. This method enables the tracing option.
Additionally, this method allows specifying contextualTuples to augment the check request with temporary, non-persistent relationship tuples that exist solely within the scope of this specific check. Contextual tuples are not written to the store but are taken into account for this particular check request as if they were present in the store.
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Check if the relation exists
allowed, err := client.CheckRelationWithTracing(context.Background(), ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
})
if err != nil {
// Handle err
return
}
fmt.Printf("allowed: %v", allowed)
}
Example (ContextualTuples) ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
contextualTuples := []ofga.Tuple{{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
}}
// Check if the relation exists
allowed, err := client.CheckRelationWithTracing(context.Background(), ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
contextualTuples...,
)
if err != nil {
// Handle err
return
}
fmt.Printf("allowed: %v", allowed)
}
func (*Client) CreateAuthModel ¶ added in v0.2.0
func (c *Client) CreateAuthModel(ctx context.Context, authModel *openfga.AuthorizationModel) (string, error)
CreateAuthModel creates a new authorization model as per the provided type definitions and schemaVersion and returns its ID. The AuthModelFromJSON function can be used to convert an authorization model from json to the slice of type definitions required by this method.
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Assume we have the following json auth model
json := []byte(`{
"type_definitions": [
{
"type": "user",
"relations": {}
}
],
"schema_version": "1.1"
}`)
// Convert json into internal representation
model, err := ofga.AuthModelFromJSON(json)
if err != nil {
// Handle err
}
// Create an auth model in OpenFGA using the internal representation
authModelID, err := client.CreateAuthModel(context.Background(), model)
if err != nil {
// Handle error
}
fmt.Println(authModelID)
}
func (*Client) CreateStore ¶ added in v0.2.0
CreateStore creates a new store on the openFGA instance and returns its ID.
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Create a store named "Alpha"
storeID, err := client.CreateStore(context.Background(), "Alpha")
if err != nil {
// Handle err
return
}
fmt.Println(storeID)
}
func (*Client) FindAccessibleObjectsByRelation ¶ added in v0.2.0
func (c *Client) FindAccessibleObjectsByRelation(ctx context.Context, tuple Tuple, contextualTuples ...Tuple) ([]Entity, error)
FindAccessibleObjectsByRelation returns a list of all objects of a specified type that a user (or any other entity) has access to via the specified relation. This method checks both actual tuples and implied relations by the authorization model. This method does not recursively expand relations, it will simply check for exact matches between the specified user/entity and the target entity.
This method has some constraints on the tuples passed in (the constraints are from the underlying openfga.ListObjects API):
- The tuple.Object field must have only the Kind and ID fields set.
- The tuple.Relation field must be set.
- The tuple.Target field must specify only the Kind.
Note that there are some important caveats to using this method (suboptimal performance depending on the authorization model, experimental, subject to context deadlines, See: https://openfga.dev/docs/interacting/relationship-queries#caveats-and-when-not-to-use-it-3
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Find all documents that the user bob can view by virtue of direct or
// implied relationships.
searchTuple := ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "bob"},
Relation: "viewer",
Target: &ofga.Entity{Kind: "document"},
}
docs, err := client.FindAccessibleObjectsByRelation(context.Background(), searchTuple)
if err != nil {
// Handle error
}
for _, doc := range docs {
// Process the matching documents
fmt.Println(doc)
}
}
func (*Client) FindMatchingTuples ¶ added in v0.2.0
func (c *Client) FindMatchingTuples(ctx context.Context, tuple Tuple, pageSize int32, continuationToken string) ([]TimestampedTuple, string, error)
FindMatchingTuples fetches all stored relationship tuples that match the given input tuple. This method uses the underlying Read API from openFGA. Note that this method only fetches actual tuples that were stored in the system. It does not show any implied relationships (as defined in the authorization model)
This method has some constraints on the tuples passed in (the constraints are from the underlying openfga.Read API):
- Tuple.Target must have the Kind specified. The ID is optional.
- If Tuple.Target.ID is not specified then Tuple.Object is mandatory and must be fully specified (Kind & ID & possibly Relation as well).
- Alternatively, Tuple can be an empty struct passed in with all nil/empty values. In this case, all tuples from the system are returned.
This method can be used to find all tuples where:
- a specific user has a specific relation with objects of a specific type eg: Find all documents where bob is a writer - ("user:bob", "writer", "document:")
- a specific user has any relation with objects of a specific type eg: Find all documents related to bob - ("user:bob", "", "document:")
- any user has any relation with a specific object eg: Find all documents related by a writer relation - ("", "", "document:planning")
This method is also useful during authorization model migrations.
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Find all tuples where bob is a writer of a document
searchTuple := ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "bob"},
Relation: "writer",
Target: &ofga.Entity{Kind: "document"},
}
tuples, continuationToken, err := client.FindMatchingTuples(context.Background(), searchTuple, 0, "")
if err != nil {
// Handle error
}
for _, tuple := range tuples {
// Process the matching tuples
fmt.Println(tuple)
}
// If required, fetch the next tuples using the continuation token
if continuationToken != "" {
tuples, continuationToken, err = client.FindMatchingTuples(context.Background(), searchTuple, 0, continuationToken)
if err != nil {
// Handle error
}
for _, tuple := range tuples {
// Process the matching tuples
fmt.Println(tuple)
}
}
}
func (*Client) FindUsersByRelation ¶ added in v0.2.0
FindUsersByRelation fetches the list of users that have a specific relation with a specific target object. This method not only searches through the relationship tuples present in the system, but also takes into consideration the authorization model and the relationship tuples implied by the model (for instance, a writer of a document is also a viewer of the document).
This method requires Tuple.Target and Tuple.Relation be specified. The Tuple.Object.Kind field can optionally be used to filter the users returned to a specific type (defaults to "user" if not specified).
An example: to find all users of kind "user" that have "viewer" relation to a document with ID "doc1":
ofga.FindUsersByRelation(ctx, ofga.Tuple{
Object: &ofga.Entity{
Kind: "user",
},
Relation: "viewer",
Target: &ofga.Entity{Kind: "document", ID: "doc1"},
})
FindUsersByRelation uses `ListUsers` and is available in OpenFGA server version >= 1.5.6.
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Find all users that have the viewer relation with document ABC, expanding
// matching user sets upto two levels deep only.
searchTuple := ofga.Tuple{
Relation: "viewer",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
}
users, err := client.FindUsersByRelation(context.Background(), searchTuple)
if err != nil {
// Handle error
}
for _, user := range users {
// Process the matching users
fmt.Println(user)
}
}
func (*Client) GetAuthModel ¶ added in v0.2.0
GetAuthModel fetches an authorization model by ID from the openFGA instance.
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// fetch an auth model by ID
model, err := client.GetAuthModel(context.Background(), "ABC1234")
if err != nil {
// Use the model
fmt.Println(model)
}
}
func (*Client) ListAuthModels ¶ added in v0.2.0
func (c *Client) ListAuthModels(ctx context.Context, pageSize int32, continuationToken string) (openfga.ReadAuthorizationModelsResponse, error)
ListAuthModels returns the list of authorization models present on the openFGA instance. If pageSize is set to 0, then the default pageSize is used. If this is the initial request, an empty string should be passed in as the continuationToken.
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Fetch a list of auth models using the default page size
resp, err := client.ListAuthModels(context.Background(), 0, "")
if err != nil {
// Handle err
return
}
for _, model := range resp.GetAuthorizationModels() {
// Processing
fmt.Println(model.GetId())
}
// If it exists, fetch the next page of auth models
if resp.HasContinuationToken() {
resp, err = client.ListAuthModels(context.Background(), 0, resp.GetContinuationToken())
if err != nil {
// Handle err
return
}
for _, model := range resp.GetAuthorizationModels() {
// Processing
fmt.Println(model.GetId())
}
}
}
func (*Client) ListStores ¶ added in v0.2.0
func (c *Client) ListStores(ctx context.Context, pageSize int32, continuationToken string) (openfga.ListStoresResponse, error)
ListStores returns the list of stores present on the openFGA instance. If pageSize is set to 0, then the default pageSize is used. If this is the initial request, an empty string should be passed in as the continuationToken.
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Fetch a list of stores using the default page size
resp, err := client.ListStores(context.Background(), 0, "")
if err != nil {
// Handle err
return
}
for _, store := range resp.GetStores() {
// Processing
fmt.Println(store)
}
// If it exists, fetch the next page of stores
if resp.GetContinuationToken() != "" {
resp, err = client.ListStores(context.Background(), 0, resp.GetContinuationToken())
if err != nil {
// Handle err
return
}
for _, store := range resp.GetStores() {
// Processing
fmt.Println(store)
}
}
}
func (*Client) ReadChanges ¶ added in v0.2.0
func (c *Client) ReadChanges(ctx context.Context, entityType string, pageSize int32, continuationToken string) (openfga.ReadChangesResponse, error)
ReadChanges returns a paginated list of tuple changes (additions and deletions) sorted by ascending time. The response will include a continuation token that can be used to get the next set of changes. If there are no changes after the provided continuation token, the same token will be returned in order for it to be used when new changes are recorded. If no tuples have been added or removed, this token will be empty. The entityType parameter can be used to restrict the response to show only changes affecting a specific type. For more information, check: https://openfga.dev/docs/interacting/read-tuple-changes#02-get-changes-for-all-object-types
Example ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Fetch all tuple changes since the start. Use the default page_size.
resp, err := client.ReadChanges(context.Background(), "", 0, "")
if err != nil {
// Handle err
return
}
for _, change := range resp.GetChanges() {
// Processing
fmt.Println(change)
}
}
Example (ForSpecificEntityType) ¶
package main
import (
"context"
"fmt"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Fetch all tuple changes affecting organizations since the start.
// Use the default page_size.
resp, err := client.ReadChanges(context.Background(), "organization", 0, "")
if err != nil {
// Handle err
return
}
for _, change := range resp.GetChanges() {
// Processing
fmt.Println(change)
}
}
func (*Client) RemoveRelation ¶
RemoveRelation removes the specified relation(s) between the objects & targets as specified by the given tuples.
Example ¶
package main
import (
"context"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Remove a relationship tuple
err := client.RemoveRelation(context.Background(), ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
})
if err != nil {
// Handle err
return
}
}
Example (Multiple) ¶
package main
import (
"context"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Remove relationship tuples
err := client.RemoveRelation(context.Background(),
ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "456"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "789"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
)
if err != nil {
// Handle err
return
}
}
func (*Client) RemoveRelationIdempotent ¶ added in v0.16.0
RemoveRelationIdempotent removes the specified relation(s) between the objects & targets as specified by the given tuples and ignores missing tuples that don't exist in the store. Note: Duplicates within the same request are not allowed and will cause an error. It requires OpenFGA server version >= 1.10.0.
Example ¶
package main
import (
"context"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Remove a relationship tuple, ignoring if it doesn't exist.
// Requires OpenFGA server version >= 1.10.0.
err := client.RemoveRelationIdempotent(context.Background(), ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
})
if err != nil {
// Handle err
return
}
}
Example (Multiple) ¶
package main
import (
"context"
"github.com/canonical/ofga"
)
var client *ofga.Client
func main() {
// Remove relationship tuples idempotently, ignoring missing tuples.
// Requires OpenFGA server version >= 1.10.0.
err := client.RemoveRelationIdempotent(context.Background(),
ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "123"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "456"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
ofga.Tuple{
Object: &ofga.Entity{Kind: "user", ID: "789"},
Relation: "editor",
Target: &ofga.Entity{Kind: "document", ID: "ABC"},
},
)
if err != nil {
// Handle err
return
}
}
func (*Client) SetAuthModelID ¶ added in v0.9.0
SetAuthModelID sets the authorization model ID to be used by the client.
func (*Client) SetStoreID ¶ added in v0.9.0
SetStoreID sets the store ID to be used by the client.
type Entity ¶
Entity represents an entity/entity-set in OpenFGA. Example: `user:<user-id>`, `org:<org-id>#member`, `document:` (only kind sometimes used in the Read API)
func ParseEntity ¶ added in v0.2.0
ParseEntity will parse a string representation into an Entity. It expects to find entities of the form:
- <entityType>: eg. organization:
- <entityType>:<ID> eg. organization:canonical
- <entityType>:<ID>#<relationship-set> eg. organization:canonical#member
Example ¶
package main
import (
"fmt"
"github.com/canonical/ofga"
)
func main() {
entity, err := ofga.ParseEntity("organization:canonical")
fmt.Printf("%+v %v", entity, err)
}
Output: {Kind:organization ID:canonical Relation:} <nil>
Example (EntitySet) ¶
package main
import (
"fmt"
"github.com/canonical/ofga"
)
func main() {
entity, err := ofga.ParseEntity("organization:canonical#member")
fmt.Printf("%+v %v", entity, err)
}
Output: {Kind:organization ID:canonical Relation:member} <nil>
func (*Entity) IsPublicAccess ¶ added in v0.10.0
IsPublicAccess returns true when the entity ID is the * wildcard, representing any entity.
type OpenFGAParams ¶
type OpenFGAParams struct {
// Scheme must be `http` or `https`.
Scheme string
// Host is the URL to the OpenFGA server and must be specified without the
// scheme (i.e. `api.fga.example` instead of `https://api.fga.example`)
Host string
// Port specifies the port on which the server is running.
Port string
// Token specifies the authentication token to use while communicating with
// the server.
Token string
// StoreID specifies the ID of the OpenFGA Store to be used for
// authorization checks.
StoreID string
// AuthModelID specifies the ID of the OpenFGA Authorization model to be
// used for authorization checks.
AuthModelID string
// Telemetry specifies the OpenTelemetry metrics configuration.
Telemetry *telemetry.Configuration
// HTTPClient optionally specifies http.Client to allow
// for advanced customizations.
HTTPClient *http.Client
}
OpenFGAParams holds parameters needed to connect to the OpenFGA server.
type OpenFgaApi ¶ added in v0.2.0
type OpenFgaApi interface {
Check(ctx context.Context, storeID string) openfga.ApiCheckRequest
CreateStore(ctx context.Context) openfga.ApiCreateStoreRequest
Expand(ctx context.Context, storeID string) openfga.ApiExpandRequest
GetStore(ctx context.Context, storeID string) openfga.ApiGetStoreRequest
ListObjects(ctx context.Context, storeID string) openfga.ApiListObjectsRequest
ListStores(ctx context.Context) openfga.ApiListStoresRequest
Read(ctx context.Context, storeID string) openfga.ApiReadRequest
ReadAuthorizationModel(ctx context.Context, storeID string, id string) openfga.ApiReadAuthorizationModelRequest
ReadAuthorizationModels(ctx context.Context, storeID string) openfga.ApiReadAuthorizationModelsRequest
ReadChanges(ctx context.Context, storeID string) openfga.ApiReadChangesRequest
Write(ctx context.Context, storeID string) openfga.ApiWriteRequest
WriteAuthorizationModel(ctx context.Context, storeID string) openfga.ApiWriteAuthorizationModelRequest
ListUsers(ctx context.Context, storeID string) openfga.ApiListUsersRequest
}
OpenFgaApi defines the methods of the underlying api client that our Client depends upon.
type Relation ¶
type Relation string
Relation represents the type of relation between entities in OpenFGA.
type TimestampedTuple ¶ added in v0.2.0
TimestampedTuple is a tuple accompanied by a timestamp that represents the timestamp at which the tuple was created.
type Tuple ¶
Tuple represents a relation between an object and a target. Note that OpenFGA represents a Tuple as (User, Relation, Object). However, the `User` field is not restricted to just being users, it could also refer to objects when we need to create object to object relationships. Hence, we chose to use (Object, Relation, Target), as it results in more consistent naming.
func FromOpenFGATupleKey ¶ added in v0.8.0
FromOpenFGATupleKey converts an openfga.TupleKey struct into a Tuple.
func (Tuple) ToOpenFGACheckRequestTupleKey ¶ added in v0.11.0
func (t Tuple) ToOpenFGACheckRequestTupleKey() *openfga.CheckRequestTupleKey
ToOpenFGACheckRequestTupleKey converts our Tuple struct into an OpenFGA CheckRequestTupleKey.
func (Tuple) ToOpenFGAExpandRequestTupleKey ¶ added in v0.11.0
func (t Tuple) ToOpenFGAExpandRequestTupleKey() *openfga.ExpandRequestTupleKey
ToOpenFGAExpandRequestTupleKey converts our Tuple struct into an OpenFGA ExpandRequestTupleKey.
func (Tuple) ToOpenFGAReadRequestTupleKey ¶ added in v0.11.0
func (t Tuple) ToOpenFGAReadRequestTupleKey() *openfga.ReadRequestTupleKey
ToOpenFGAReadRequestTupleKey converts our Tuple struct into an OpenFGA ReadRequestTupleKey.
func (Tuple) ToOpenFGATupleKey ¶ added in v0.8.0
ToOpenFGATupleKey converts our Tuple struct into an OpenFGA TupleKey.
func (Tuple) ToOpenFGATupleKeyWithoutCondition ¶ added in v0.11.0
func (t Tuple) ToOpenFGATupleKeyWithoutCondition() *openfga.TupleKeyWithoutCondition
ToOpenFGATupleKeyWithoutCondition converts our Tuple struct into an OpenFGA TupleKeyWithoutCondition.
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
version
Package version provides server version information.
|
Package version provides server version information. |
|
Package mockhttp provides the ability to define mocked routes, specifying request validations to be performed on incoming requests to these routes, and mock responses to be returned for these requests.
|
Package mockhttp provides the ability to define mocked routes, specifying request validations to be performed on incoming requests to these routes, and mock responses to be returned for these requests. |