ofga

package module
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 16, 2024 License: LGPL-3.0 Imports: 11 Imported by: 1

README

ofga

ofga is a wrapper library for conveniently interacting with OpenFGA instances.

OpenFGA is an open-source Fine-Grained Authorization (FGA) solution that provides a framework and set of tools for implementing fine-grained access control and permission management in applications.

This Go library builds upon the default OpenFGA client by providing a more convenient and streamlined interface. It simplifies common interactions with OpenFGA instances, offering an alternative API that implements a commonly-used set of opinionated operations.

Why ofga?

  • Convenience methods: ofga provides a set of high-level convenience methods that abstract away the complexities of working directly with the OpenFGA client. This saves developers time and effort when performing common tasks.
  • Structured representation: ofga introduces a well-defined structure for representing relationship tuples within the OpenFGA system. This structured approach simplifies the management of relationship tuples and their associated system entities within your application.
  • Enhanced response format: The library transforms certain responses returned by the underlying client, allowing for easier data access and manipulation. One example is the Expand API, where the underlying client returns a tree-like response, while the library recursively expands this tree (upto the specified depth) to provide the actual set of users/usersets that possess the relevant permissions.

Quickstart

  1. Install the library using the following command:

        go get -u github.com/canonical/ofga
    
  2. Import the library in your code:

        import "github.com/canonical/ofga"
    
  3. Create a new ofga client and handle any errors:

    ctx = context.Background()
    
    // Create a new ofga client
    client, err := ofga.NewClient(ctx, 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 error
    }
    
  4. Use the client to interact with OpenFGA instances based on your requirements. For example:

    err = client.AddRelation(ctx, ofga.Tuple{
        Object:   &ofga.Entity{Kind: "user", ID: "123"},
        Relation: "editor",
        Target:   &ofga.Entity{Kind: "document", ID: "ABC"},
    })
    if err != nil {
        // Handle error
    }
    
  5. Use the client to check for relations:

    allowed, err = client.CheckRelation(ctx, ofga.Tuple{
        Object:   &ofga.Entity{Kind: "user", ID: "123"},
        Relation: "viewer",
        Target:   &ofga.Entity{Kind: "document", ID: "ABC"},
    })
    if err != nil {
        // Handle error
    }
    if !allowed {
        // Permission denied
    }
    ... // Perform action
    

Documentation

The documentation for this package can be found on pkg.go.dev.

Contributing

If you encounter any issues or have suggestions for improvements, please open an issue on the GitHub repository.

Authors

Canonical Commercial Systems Team

Documentation

Overview

Package ofga provides utilities for interacting with an OpenFGA instance.

Index

Examples

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)
}
Output:

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())
}
Output:

func (*Client) AddRelation

func (c *Client) AddRelation(ctx context.Context, tuples ...Tuple) error

AddRelation adds the specified relation(s) between the objects & targets as specified by the given tuple(s).

Example
package main

import (
	"context"

	"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
		return
	}
}
Output:

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
	}
}
Output:

func (*Client) AddRemoveRelations added in v0.7.0

func (c *Client) AddRemoveRelations(ctx context.Context, addTuples, removeTuples []Tuple) error

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
	}
}
Output:

func (*Client) AuthModelID added in v0.9.0

func (c *Client) AuthModelID() string

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)
}
Output:

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)
}
Output:

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)
}
Output:

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)
}
Output:

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)
}
Output:

func (*Client) CreateStore added in v0.2.0

func (c *Client) CreateStore(ctx context.Context, name string) (string, error)

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)
}
Output:

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)
	}
}
Output:

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)
		}
	}
}
Output:

func (*Client) FindUsersByRelation added in v0.2.0

func (c *Client) FindUsersByRelation(ctx context.Context, tuple Tuple, maxDepth int) ([]Entity, error)

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), and recursively expands these relationships upto `maxDepth` levels deep to obtain the final list of users. A `maxDepth` of `1` causes the current tuple to be expanded and the immediate expansion results to be returned. `maxDepth` can be any positive number.

This method requires that Tuple.Target and Tuple.Relation be specified.

Note that this method call is expensive and has high latency, and should be used with caution. The official docs state that the underlying API method was intended to be used for debugging: https://openfga.dev/docs/interacting/relationship-queries#caveats-and-when-not-to-use-it-2

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, 2)
	if err != nil {
		// Handle error
	}

	for _, user := range users {
		// Process the matching users
		fmt.Println(user)
	}
}
Output:

func (*Client) GetAuthModel added in v0.2.0

func (c *Client) GetAuthModel(ctx context.Context, ID string) (openfga.AuthorizationModel, error)

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)
	}
}
Output:

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())
		}
	}
}
Output:

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)
		}
	}
}
Output:

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)
	}
}
Output:

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)
	}
}
Output:

func (*Client) RemoveRelation

func (c *Client) RemoveRelation(ctx context.Context, tuples ...Tuple) error

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
	}
}
Output:

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
	}
}
Output:

func (*Client) SetAuthModelID added in v0.9.0

func (c *Client) SetAuthModelID(authModelID string)

SetAuthModelID sets the authorization model ID to be used by the client.

func (*Client) SetStoreID added in v0.9.0

func (c *Client) SetStoreID(storeID string)

SetStoreID sets the store ID to be used by the client.

func (*Client) StoreID added in v0.9.0

func (c *Client) StoreID() string

StoreID gets the currently configured store ID.

type Entity

type Entity struct {
	Kind     Kind
	ID       string
	Relation Relation
}

Entity represents an entity/entity-set in OpenFGA. Example: `user:<user-id>`, `org:<org-id>#member`

func ParseEntity added in v0.2.0

func ParseEntity(s string) (Entity, error)

ParseEntity will parse a string representation into an Entity. It expects to find entities of the form:

  • <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

func (e *Entity) IsPublicAccess() bool

IsPublicAccess returns true when the entity ID is the * wildcard, representing any entity.

func (*Entity) String

func (e *Entity) String() string

String returns a string representation of the entity/entity-set.

type Kind

type Kind string

Kind represents the type of the entity in OpenFGA.

func (Kind) String

func (k Kind) String() string

String implements the Stringer interface.

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
}

OpenFGAParams holds parameters needed to connect to the OpenFGA server.

type OpenFgaApi added in v0.2.0

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.

func (Relation) String

func (r Relation) String() string

String implements the Stringer interface.

type TimestampedTuple added in v0.2.0

type TimestampedTuple struct {
	Tuple     Tuple
	Timestamp time.Time
}

TimestampedTuple is a tuple accompanied by a timestamp that represents the timestamp at which the tuple was created.

type Tuple

type Tuple struct {
	Object   *Entity
	Relation Relation
	Target   *Entity
}

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

func FromOpenFGATupleKey(key openfga.TupleKey) (Tuple, error)

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

func (t Tuple) ToOpenFGATupleKey() *openfga.TupleKey

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL