openfga / cli

A cross-platform CLI to interact with an OpenFGA server
https://openfga.dev
Apache License 2.0
50 stars 25 forks source link
access-control cli fga fine-grained-authorization hacktoberfest openfga openfga-client security zanzibar

OpenFGA CLI

A cross-platform CLI to interact with an OpenFGA server

Go Reference Release License FOSSA Status Join our community Twitter

Table of Contents

About

OpenFGA is an open source Fine-Grained Authorization solution inspired by Google's Zanzibar paper. It was created by the FGA team at Auth0/Okta based on Okta Fine-Grained Authorization (FGA), available under a permissive license (Apache-2) and welcomes community contributions.

OpenFGA is designed to make it easy for application builders to model their permission layer, and to add and integrate fine-grained authorization into their applications. OpenFGA’s design is optimized for reliability and low latency at a high scale.

Resources

Installation

Brew

brew install openfga/tap/fga

Linux (deb, rpm and apk) packages

Download the .deb, .rpm or .apk packages from the releases page.

Debian:

sudo apt install ./fga_<version>_linux_<arch>.deb

Fedora:

sudo dnf install ./fga_<version>_linux_<arch>.rpm

Alpine Linux:

sudo apk add --allow-untrusted ./fga_<version>_linux_<arch>.apk

Windows

via Scoop

scoop install openfga

Docker

docker pull openfga/cli; docker run -it openfga/cli

Go

go install github.com/openfga/cli/cmd/fga@latest

Manually

Download the pre-compiled binaries from the releases page.

Building from Source

Make sure you have Go 1.20 or later installed. See the Go downloads page.

  1. Clone the repo to a local directory, and navigate to that directory:

    git clone https://github.com/openfga/cli.git && cd cli
  2. Then use the build command:

    go build -o ./dist/fga ./cmd/fga/main.go

    or if you have make installed, just run:

    make build
  3. Run the OpenFGA CLI with:

    ./dist/fga

Usage

Configuration

For any command that interacts with an OpenFGA server, these configuration values can be passed (where applicable)

Name Flag CLI ~/.fga.yaml
API Url --api-url FGA_API_URL api-url
Shared Secret --api-token FGA_API_TOKEN api-token
Client ID --client-id FGA_CLIENT_ID client-id
Client Secret --client-secret FGA_CLIENT_SECRET client-secret
Scopes --api-scopes FGA_API_SCOPES api-scopes
Token Issuer --api-token-issuer FGA_API_TOKEN_ISSUER api-token-issuer
Token Audience --api-audience FGA_API_AUDIENCE api-audience
Store ID --store-id FGA_STORE_ID store-id
Authorization Model ID --model-id FGA_MODEL_ID model-id

If you are authenticating with a shared secret, you should specify the API Token value. If you are authenticating using OAuth, you should specify the Client ID, Client Secret, API Audience and Token Issuer. For example:

# Note: This example is for Okta FGA
api-url: https://api.us1.fga.dev
client-id: 4Zb..UYjaHreLKOJuU8
client-secret: J3...2pBwiauD
api-audience: https://api.us1.fga.dev/
api-token-issuer: auth.fga.dev
store-id: 01H0H015178Y2V4CX10C2KGHF4

Commands

Stores

Description command parameters example
Create a Store create --name fga store create --name="FGA Demo Store"
Import a Store import --file fga store import --file store.fga.yaml
Export a Store export --store-id fga store export --store-id=01H0H015178Y2V4CX10C2KGHF4
List Stores list fga store list
Get a Store get --store-id fga store get --store-id=01H0H015178Y2V4CX10C2KGHF4
Delete a Store delete --store-id fga store delete --store-id=01H0H015178Y2V4CX10C2KGHF4
Create Store
Command

fga store create

Parameters
Example

fga store create --name "FGA Demo Store"

Response
{
    "id": "01H0H015178Y2V4CX10C2KGHF4",
    "name": "FGA Demo Store",
    "created_at": "2023-05-19T16:10:07.637585677Z",
    "updated_at": "2023-05-19T16:10:07.637585677Z"
}

fga store create --model Model.fga

Response
{
  "store": {
    "id":"01H6H9CNQRP2TVCFR7899XGNY8",
    "name":"Model",
    "created_at":"2023-07-29T16:58:28.984402Z",
    "updated_at":"2023-07-29T16:58:28.984402Z"
  },
  "model": {
    "authorization_model_id":"01H6H9CNQV36Y9WS1RJGRN8D06"
  }
}

To automatically set the created store id as an environment variable that will then be used by the CLI, you can use the following command:

export FGA_STORE_ID=$(fga store create --model model.fga | jq -r .store.id)
Import Store
Command

fga store import

Parameters
Example

fga store import --file model.fga.yaml

Response
{}
Export Store
Command

fga store export

Parameters
Example

fga store export --store-id=01H0H015178Y2V4CX10C2KGHF4

Response
name: Test
model: |+
    model
      schema 1.1

    type user

    type group
      relations
        define member: [user]
        define moderator: [user]

tuples:
    - user: user:1
      relation: member
      object: group:admins
    - user: user:1
      relation: member
      object: group:employees
    - user: user:2
      relation: member
      object: group:employees
    - user: user:1
      relation: moderator
      object: group:employees
tests:
    - name: Tests
      check:
        - user: user:1
          object: group:admins
          assertions:
            member: true
        - user: user:2
          object: group:admins
          assertions:
            member: false
        - user: user:1
          object: group:employees
          assertions:
            member: true
            moderator: true
        - user: user:2
          object: group:employees
          assertions:
            member: true
            moderator: false

If using output-file, the response will be written to the specified file on disk. If the desired file already exists, you will be prompted to overwrite the file.

List Stores
Command

fga store list

Parameters
Example

fga store list

Response
{
  "stores": [{
    "id": "..",
    "name": "..",
    "created_at": "",
    "updated_at": "",
    "deleted_at": ""
  }, { .. }]
}
Get Store
Command

fga store get

Parameters
Example

fga store get --store-id=01H0H015178Y2V4CX10C2KGHF4

Response
{
    "id": "01H0H015178Y2V4CX10C2KGHF4",
    "name": "FGA Demo Store",
    "created_at": "2023-05-19T16:10:07.637585677Z",
    "updated_at": "2023-05-19T16:10:07.637585677Z"
}
Delete Store
Command

fga store delete

Parameters
Example

fga store delete --store-id=01H0H015178Y2V4CX10C2KGHF4

Response
{}

Authorization Models

Description command parameters example
Read Authorization Models list --store-id fga model list --store-id=01H0H015178Y2V4CX10C2KGHF4
Write Authorization Model write --store-id, --file fga model write --store-id=01H0H015178Y2V4CX10C2KGHF4 --file model.fga
Read a Single Authorization Model get --store-id, --model-id fga model get --store-id=01H0H015178Y2V4CX10C2KGHF4 --model-id=01GXSA8YR785C4FYS3C0RTG7B1
Validate an Authorization Model validate --file, --format fga model validate --file model.fga
Run Tests on an Authorization Model test --tests, --verbose fga model test --tests tests.fga.yaml
Transform an Authorization Model transform --file, --input-format fga model transform --file model.json
Read Authorization Models

List all authorization models for a store, in descending order by creation date. The first model in the list is the latest one.

Command

fga model list

Parameters
Example

fga model list --store-id=01H0H015178Y2V4CX10C2KGHF4

Response
{
  "authorization_models": [
    {
      "id":"01H6H9XH1G5Q6DK6PFMGDZNH9S",
      "created_at":"2023-07-29T17:07:41Z"
    },
    {
      "id":"01H6H9PPR6C3P45R75X55ZFP46",
      "created_at":"2023-07-29T17:03:57Z"
    }
  ]
}
Write Authorization Model
Command

fga model write

Parameters
Example
Response
{
  "authorization_model_id":"01GXSA8YR785C4FYS3C0RTG7B1"
}
Read a Single Authorization Model
Command

fga model get

Parameters
Example

fga model get --store-id=01H0H015178Y2V4CX10C2KGHF4 --model-id=01GXSA8YR785C4FYS3C0RTG7B1

Response
model
  schema 1.1

type user

type document
  relations
    define can_view: [user]
Read the Latest Authorization Model

If model-id is not specified when using the get command, the latest authorization model will be returned.

Command

fga model get

Parameters
Example

fga model get --store-id=01H0H015178Y2V4CX10C2KGHF4

Response
model
  schema 1.1

type user

type document
  relations
    define can_view: [user]
Validate an Authorization Model
Command

fga model validate

Parameters
Example

fga model validate --file model.json

JSON Response
Run Tests on an Authorization Model

Given a model, and a set of tests (tuples, check and list objects requests, and expected results) report back on any tests that do not return the same results as expected.

Command

fga model test

Parameters

If a model is provided, the test will run in a built-in OpenFGA instance (you do not need a separate server). Otherwise, the test will be run against the configured store of your OpenFGA instance. When running against a remote instance, the tuples will be sent as contextual tuples, and will have to abide by the OpenFGA server limits (20 contextual tuples per request).

The tests file should be in yaml and have the following format:

---
name: Store Name # store name, optional
# model_file: ./model.fga # a global model that would apply to all tests, optional
# model can be used instead of model_file, optional
model: |
  model
    schema 1.1
  type user
  type folder
    relations
      define owner: [user] 
      define parent: [folder]
      define can_view: owner or can_view from parent
      define can_write: owner or can_write from parent
      define can_share: owner

# tuple_file: ./tuples.yaml # global tuples that would apply to all tests, optional
tuples: # global tuples that would apply to all tests, optional
  - user: folder:1
    relation: parent
    object: folder:2
tests: # required
  - name: test-1
    description: testing that the model works # optional
    # tuple_file: ./tuples.json # tuples that would apply per test
    tuples:
      - user: user:anne
        relation: owner
        object: folder:1
    check: # a set of checks to run
      - user: user:anne
        object: folder:1
        assertions:
          # a set of expected results for each relation
          can_view: true
          can_write: true
          can_share: false
    list_objects: # a set of list objects to run
      - user: user:anne
        type: folder
        assertions:
          # a set of expected results for each relation
          can_view:
            - folder:1
            - folder:2
          can_write:
            - folder:1
            - folder:2
  - name: test-2
    description: another test
    tuples:
      - user: user:anne
        relation: owner
        object: folder:1
    check:
      - user: user:anne
        object: folder:1
        assertions:
          # a set of expected results for each relation
          can_view: true
    list_objects:
      - user: user:anne
        type: folder
        assertions:
          # a set of expected results for each relation
          can_view:
            - folder:1
            - folder:2
Example

fga model test --tests tests.fga.yaml

For more examples of .fga.yaml files, check the sample-stores repository/

Response
(FAILING) test-1: Checks (2/3 passing) | ListObjects (2/2 passing)
ⅹ Check(user=user:anne,relation=can_share,object=folder:1): expected=false, got=true
---
# Test Summary #
Tests 1/2 passing
Checks 3/4 passing
ListObjects 3/3 passing
Transform an Authorization Model
Command

fga model transform

Parameters
Example

fga model transform --file model.json

Response
model
  schema 1.1

type user

type document
  relations
    define can_view: [user]

Relationship Tuples

Description command parameters example
Write Relationship Tuples write --store-id, --model-id fga tuple write user:anne can_view document:roadmap --store-id=01H0H015178Y2V4CX10C2KGHF4
Delete Relationship Tuples delete --store-id, --model-id fga tuple delete user:anne can_view document:roadmap --store-id=01H0H015178Y2V4CX10C2KGHF4
Read Relationship Tuples read --store-id, --model-id fga tuple read --store-id=01H0H015178Y2V4CX10C2KGHF4 --model-id=01GXSA8YR785C4FYS3C0RTG7B1
Read Relationship Tuple Changes (Watch) changes --store-id, --type, --continuation-token, fga tuple changes --store-id=01H0H015178Y2V4CX10C2KGHF4 --type=document --continuation-token=M3w=
Import Relationship Tuples import --store-id, --model-id, --file fga tuple import --store-id=01H0H015178Y2V4CX10C2KGHF4 --model-id=01GXSA8YR785C4FYS3C0RTG7B1 --file tuples.json
Write Relationship Tuples
Command

fga tuple write --store-id=

Parameters
  • <user>: User
  • <relation>: Relation
  • <object>: Object
  • --condition-name: Condition name (optional)
  • --condition-context: Condition context (optional)
  • --store-id: Specifies the store id
  • --model-id: Specifies the model id to target (optional)
  • --file: Specifies the file name, json, yaml and csv files are supported
  • --max-tuples-per-write: Max tuples to send in a single write (optional, default=1)
  • --max-parallel-requests: Max requests to send in parallel (optional, default=4)
Example (with arguments)
  • fga tuple write --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document:roadmap
  • fga tuple write --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document:roadmap --condition-name inOffice --condition-context '{"office_ip":"10.0.1.10"}'
Response
{
  "successful": [
    {
      "object":"document:roadmap",
      "relation":"writer",
      "user":"user:annie"
    }
  ],
}
Example (with file)

fga tuple write --store-id=01H0H015178Y2V4CX10C2KGHF4 --file tuples.json

If using a csv file, the format should be:

user_type,user_id,user_relation,relation,object_type,object_id,condition_name,condition_context
folder,product,,parent,folder,product-2021,inOfficeIP,"{""ip_addr"":""10.0.0.1""}"

If using a yaml file, the format should be:

- user: folder:5
  relation: parent
  object: folder:product-2021
- user: folder:product-2021
  relation: parent
  object: folder:product-2021Q1

If using a json file, the format should be:

[
  {
    "user": "user:anne",
    "relation": "owner",
    "object": "folder:product"
  },
  {
    "user": "folder:product",
    "relation": "parent",
    "object": "folder:product-2021"
  },
  {
    "user": "user:beth",
    "relation": "viewer",
    "object": "folder:product-2021"
  }
]
Response
{
  "successful": [
    {
      "object":"document:roadmap",
      "relation":"writer",
      "user":"user:annie"
    }
  ],
  "failed": [
    {
      "tuple_key": {
        "object":"document:roadmap",
        "relation":"writer",
        "user":"carl"
      },
      "reason":"Write validation error ..."
    }
  ]
}
Delete Relationship Tuples
Command

fga tuple delete --store-id=

Parameters
  • <user>: User
  • <relation>: Relation
  • <object>: Object
  • --store-id: Specifies the store id
  • --model-id: Specifies the model id to target (optional)
  • --file: Specifies the file name, yaml and json files are supported
  • --max-tuples-per-write: Max tuples to send in a single write (optional, default=1)
  • --max-parallel-requests: Max requests to send in parallel (optional, default=4)
Example (with arguments)

fga tuple delete --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document:roadmap

Response
{}
Example (with file)

fga tuple delete --store-id=01H0H015178Y2V4CX10C2KGHF4 --file tuples.json

Response
{
  "successful": [
    {
      "object":"document:roadmap",
      "relation":"writer",
      "user":"user:annie"
    }
  ],
  "failed": [
    {
      "tuple_key": {
        "object":"document:roadmap",
        "relation":"writer",
        "user":"carl"
      },
      "reason":"Write validation error ..."
    }
  ]
}

If you want to delete all the tuples in a store, you can use the following code:

fga tuple read --output-format=simple-json --max-pages=0 > tuples.json
fga tuple delete --file tuples.json
Read Relationship Tuples
Command

fga tuple read [--user=] [--relation=] [--object=] --store-id=

Parameters
  • --store-id: Specifies the store id
  • --user: User
  • --relation: Relation
  • --object: Object
  • --max-pages: Max number of pages to get. (default 20)
  • --output-format: Can be csv, yaml, json or simple-json. Use simple-json for a simpler json format that can be piped to the write and delete commands
Example

fga tuple read --store-id=01H0H015178Y2V4CX10C2KGHF4 --user user:anne --relation can_view --object document:roadmap

Response
{
  "tuples": [
    {
      "key": {
        "object": "document:roadmap",
        "relation": "can_view",
        "user": "user:anne"
      },
      "timestamp": "2023-07-06T15:12:55.080666875Z"
    }
  ]
}
Response (--output-format=simple-json)
[
  {
    "object": "document:roadmap",
    "relation": "can_view",
    "user": "user:anne"
  }
]

If you want to transform this output in a way that can be then imported using the fga tuple import you can run

fga tuple read --output-format=simple-json --max-pages 0 > tuples.json
fga tuple import --file tuples.json
Read Relationship Tuple Changes (Watch)
Command

fga tuple changes --type --store-id=

Parameters
  • --store-id: Specifies the store id
  • --type: Restrict to a specific type (optional)
  • --max-pages: Max number of pages to retrieve (default: 20)
  • --continuation-token: Continuation token to start changes from
Example

fga tuple changes --store-id=01H0H015178Y2V4CX10C2KGHF4 --type=document --continuation-token=M3w=

Response
{
  "changes": [
    {
      "operation": "TUPLE_OPERATION_WRITE",
      "timestamp": "2023-07-06T15:12:40.294950382Z",
      "tuple_key": {
        "object": "document:roadmap",
        "relation": "can_view",
        "user": "user:anne"
      }
    }
  ],
  "continuation_token":"NHw="
}
Import Relationship Tuples
Command

fga tuple import --store-id= [--model-id=] --file [--max-tuples-per-write=] [--max-parallel-requests=]

Parameters
  • --store-id: Specifies the store id
  • --model-id: Specifies the model id to target (optional)
  • --file: Specifies the file name, yaml and json files are supported
  • --max-tuples-per-write: Max tuples to send in a single write (optional, default=1)
  • --max-parallel-requests: Max requests to send in parallel (optional, default=4)

File format should be: In YAML:

- user: user:anne
  relation: can_view
  object: document:roadmap
- user: user:beth
  relation: can_view
  object: document:roadmap

In JSON:

[{
  "user": "user:anne",
  "relation": "can_view",
  "object": "document:roadmap"
}, {
  "user": "user:beth",
  "relation": "can_view",
  "object": "document:roadmap"
}]
Example

fga tuple import --store-id=01H0H015178Y2V4CX10C2KGHF4 --file tuples.json

Response
{
  "successful": [
    {
      "object":"document:roadmap",
      "relation":"writer",
      "user":"user:annie"
    }
  ],
  "failed": [
    {
      "tuple_key": {
        "object":"document:roadmap",
        "relation":"writer",
        "user":"carl"
      },
      "reason":"Write validation error ..."
    }
  ]
}

Relationship Queries

  • query
Description command parameters example
Check check --store-id, --model-id fga query check --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document:roadmap
List Objects list-objects --store-id, --model-id fga query list-objects --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document
List Relations list-relations --store-id, --model-id fga query list-relations --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne document
Expand expand --store-id, --model-id fga query expand --store-id=01H0H015178Y2V4CX10C2KGHF4 can_view document:roadmap
Check
Command

fga query check [--condition] [--contextual-tuple "\<user> \<relation> \<object>"]* --store-id= [--model-id=]

Parameters
  • --store-id: Specifies the store id
  • --model-id: Specifies the model id to target (optional)
  • --contextual-tuple: Contextual tuples (optional)
  • --context: Condition context (optional)
  • --consistency: Consistency preference (optional)
Example
  • fga query check --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document:roadmap --contextual-tuple "user:anne can_view folder:product" --contextual-tuple "folder:product parent document:roadmap"
  • fga query check --store-id="01H4P8Z95KTXXEP6Z03T75Q984" user:anne can_view document:roadmap --context '{"ip_address":"127.0.0.1"}' --consistency="HIGHER_CONSISTENCY"
Response
{
    "allowed": true,
}
List Objects
Command

fga query list-objects [--contextual-tuple " "]* --store-id= [--model-id=]

Parameters
  • --store-id: Specifies the store id
  • --model-id: Specifies the model id to target (optional)
  • --contextual-tuple: Contextual tuples (optional) (can be multiple)
  • --context: Condition context (optional)
  • --consistency: Consistency preference (optional)
Example
  • fga query list-objects --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document --contextual-tuple "user:anne can_view folder:product" --contextual-tuple "folder:product parent document:roadmap"
  • fga query list-objects --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne can_view document --context '{"ip_address":"127.0.0.1"} --consistency="HIGHER_CONSISTENCY"
Response
{
    "objects": [
      "document:roadmap",
      "document:budget"
    ],
}
List Relations
Command

fga query list-relations [--relation ] [--contextual-tuple " "] --store-id= [--model-id=]

Parameters
  • --store-id: Specifies the store id
  • --model-id: Specifies the model id to target (optional)
  • --contextual-tuple: Contextual tuples (optional) (can be multiple)
  • --context: Condition context (optional)
  • --consistency: Consistency preference (optional)
Example
  • fga query list-relations --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne document:roadmap --relation can_view
  • fga query list-relations --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne document:roadmap --relation can_view --contextual-tuple "user:anne can_view folder:product"
  • fga query list-relations --store-id=01H0H015178Y2V4CX10C2KGHF4 user:anne document:roadmap --relation can_view --context '{"ip_address":"127.0.0.1"} --consistency="HIGHER_CONSISTENCY"
Response
{
    "relations": [
      "can_view"
    ],
}
Expand
Command

fga query expand --store-id= [--model-id=]

Parameters
  • --store-id: Specifies the store id
  • --model-id: Specifies the model id to target (optional)
  • --consistency: Consistency preference (optional)
Example

fga query expand --store-id=01H0H015178Y2V4CX10C2KGHF4 can_view document:roadmap

Response
{
  "tree": {
    "root": {
      "name": "repo:openfga/openfga#reader",
      "union": {
        "nodes": [{
          "leaf": {
            "users": {
              "users": ["user:anne"]
            }
          },
          "name": "repo:openfga/openfga#reader"
        }]
      }
    }
  }
}
List Users
Command

fga query list-users --object --relation --user-filter [--contextual-tuple " "]* --store-id= [--model-id=]

Parameters
  • --store-id: Specifies the store id
  • --object: Specifies the object to list users for
  • --relation: Specifies the relation to search on
  • --user-filter: Specifies the type or userset to filter with
  • --model-id: Specifies the model id to target (optional)
  • --contextual-tuple: Contextual tuples (optional) (can be multiple)
  • --context: Condition context (optional)
  • --consistency: Consistency preference (optional)
Example
  • fga query list-users --store-id=01H0H015178Y2V4CX10C2KGHF4 --object document:roadmap --relation can_view --user-filter user
  • fga query list-users --store-id=01H0H015178Y2V4CX10C2KGHF4 --object document:roadmap --relation can_view --user-filter user --contextual-tuple "user:anne can_view folder:product"
  • fga query list-users --store-id=01H0H015178Y2V4CX10C2KGHF4 --object document:roadmap --relation can_view --user-filter group#member --context '{"ip_address":"127.0.0.1"} --consistency="HIGHER_CONSISTENCY"
Response
{
    {
      "users": [
        {
          "object": {
            "type": "user",
            "id": "anne"
          }
        }
      ]
    }
}

Contributing

See CONTRIBUTING.

Author

OpenFGA

License

This project is licensed under the Apache-2.0 license. See the LICENSE file for more info.