{"id":22787343,"url":"https://github.com/aserto-dev/go-aserto","last_synced_at":"2025-04-15T23:38:55.361Z","repository":{"id":64298695,"uuid":"559731552","full_name":"aserto-dev/go-aserto","owner":"aserto-dev","description":"Aserto golang SDK","archived":false,"fork":false,"pushed_at":"2025-04-09T00:08:12.000Z","size":368,"stargazers_count":3,"open_issues_count":3,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-09T01:19:17.410Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aserto-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-10-31T01:10:07.000Z","updated_at":"2025-03-05T20:30:33.000Z","dependencies_parsed_at":"2023-02-16T20:16:00.103Z","dependency_job_id":"24e40b8a-57c0-454f-8926-f7e552fc369f","html_url":"https://github.com/aserto-dev/go-aserto","commit_stats":null,"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserto-dev%2Fgo-aserto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserto-dev%2Fgo-aserto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserto-dev%2Fgo-aserto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserto-dev%2Fgo-aserto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aserto-dev","download_url":"https://codeload.github.com/aserto-dev/go-aserto/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249173059,"owners_count":21224481,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-12T00:54:14.059Z","updated_at":"2025-04-15T23:38:55.354Z","avatar_url":"https://github.com/aserto-dev.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# aserto-dev/go-aserto\n\n![ci](https://github.com/aserto-dev/go-aserto/workflows/ci/badge.svg)\n[![Go Reference](https://pkg.go.dev/badge/github.com/aserto-dev/go-aserto.svg)](https://pkg.go.dev/github.com/aserto-dev/go-aserto)\n[![Go Report Card](https://goreportcard.com/badge/github.com/aserto-dev/go-aserto)](https://goreportcard.com/report/github.com/aserto-dev/go-aserto)\n\nPackage `go-aserto` implements clients and middleware for [Aserto](http://aserto.com) services.\n\n* Docs: https://docs.aserto.com/docs/\n* API Reference:  https://aserto.readme.io/\n\n\n## Install\n\n```sh\ngo get -u github.com/aserto-dev/go-aserto\n```\n\n## Authorizer\n\nThe [Authorizer](https://www.topaz.sh/docs/authorizer-guide/overview) service is is an [open source authorization engine](https://www.topaz.sh)\nwhich uses the [Open Policy Agent](https://www.openpolicyagent.org) (OPA) to make decisions by computing authorization\npolicies.\n\nThe `AuthorizerClient` interface, defined in\n[`github.com/aserto-dev/go-authorizer/aserto/authorizer/v2`](https://github.com/aserto-dev/go-authorizer/blob/main/aserto/authorizer/v2/authorizer_grpc.pb.go#L34),\ndescribes the operations exposed by the Aserto authorizer service.\n\n\n### Client\n\nThe snippet below creates an authorizer client that connects to a topaz instance running locally:\n\n```go\nimport (\n\t\"github.com/aserto-dev/go-aserto\"\n\t\"github.com/aserto-dev/go-aserto/az\"\n)\n...\nazClient, err := az.New(\n\taserto.WithAddr(\"localhost:8282\"),\n)\n```\n\n#### Connection Options\n\nThe options below can be specified to override default behaviors:\n\n**`WithAddr()`** - sets the server address and port. Default: \"authorizer.prod.aserto.com:8443\".\n\n**`WithAPIKeyAuth()`** - sets an API key for authentication.\n\n**`WithTokenAuth()`** - sets an OAuth2 token to be used for authentication.\n\n**`WithTenantID()`** - sets the aserto tenant ID.\n\n**`WithInsecure()`** - enables/disables TLS verification. Default: false.\n\n**`WithCACertPath()`** - adds the specified PEM certificate file to the connection's list of trusted root CAs.\n\n\n### Making Authorization Calls\n\nUse the client's `Is()` method to request authorization decisions from the Aserto authorizer service.\n\n```go\nimport (\n\t\"context\"\n\t...\n\t\"github.com/aserto-dev/go-authorizer/aserto/authorizer/v2\"\n\t\"github.com/aserto-dev/go-authorizer/aserto/authorizer/v2/api\"\n)\n\nctx := context.Background()\n\nresp, err := azClient.Is(ctx, \u0026authorizer.IsRequest{\n\tPolicyContext: \u0026api.PolicyContext{\n\t\tPath:      \t\t\"peoplefinder.GET.users.__id\",\n\t\tDecisions: \t\t\"allowed\",\n\t},\n\tIdentityContext: \u0026api.IdentityContext{\n\t\tIdentity: \"\u003cuser name\u003e\",\n\t\tType:     api.IdentityType_IDENTITY_TYPE_SUB,\n\t},\n})\n```\n\n## Directory Service\n\nThe [Directory](https://docs.aserto.com/docs/overview/directory) stores information required to make authorization\ndecisions.\n\n\n### Directory Client\n\nThe directory client provides access to the directory services:\n\n1. Reader - provides functions to query the directory.\n2. Writer - provides functions to mutate or delete directory data.\n3. Exporter - provides bulk export of data from the directory.\n4. Importer - provides bulk import of data into the directory.\n\n\nTo create a directory client:\n\n```go\n\nimport (\n\t\"github.com/aserto-dev/go-aserto\"\n\t\"github.com/aserto-dev/go-aserto/ds/v3\"\n)\n\n...\n\ndsClient, err := ds.New(aserto.WithAPIKeyAuth('\u003capi key\u003e'))\n```\n\n[Connection options](#connection-options) are the same as those for the authorizer client.\nIf `WithAddr()` is not provided, the default address is `directory.prod.aserto.com:8443`.\n\n\n### Configuration\n\nThe hosted Aserto directory exposes all services on the same address (`directory.prod.aserto.com:8443`).\nHowever, with Topaz or in self-hosted environments, it is possible to configure the services individually and to\ndisable selected services entirely.\n\nThe `directory.Config` structs allows for customization of connection options for directory services.\n\n```go\n// Config provides configuration for connecting to the Aserto Directory service.\ntype Config struct {\n\t// Base configuration. If non-nil, this configuration is used for any client that doesn't have its own configuration.\n\t// If nil, only clients that have their own configuration will be created.\n\t*client.Config\n\n\t// Reader configuration.\n\tReader *client.Config `json:\"reader\"`\n\n\t// Writer configuration.\n\tWriter *client.Config `json:\"writer\"`\n\n\t// Importer configuration.\n\tImporter *client.Config `json:\"importer\"`\n\n\t// Exporter configuration.\n\tExporter *client.Config `json:\"exporter\"`\n}\n```\n\nThe embedded `*client.Config` acts as a fallback. If no configuration is provided for a specific service, the fallback\nconfiguration is used. If no fallback is provided, the client for that service is nil.\n\nTo create a directory client from configuration, call `Connect()` on the config struct:\n\n```go\nimport (\n\t\"context\"\n\n\t\"github.com/aserto-dev/go-aserto/ds/v3\"\n\t\"github.com/aserto-dev/go-directory/aserto/directory/common/v2\"\n\t\"github.com/aserto-dev/go-directory/aserto/directory/reader/v2\"\n)\n\n...\n\n// Use the same address for all services.\ncfg := \u0026ds.Config{Address: \"localhost:9292\"}\n\ndsClient, err := cfg.Connect()\nif err != nil {\n\tpanic(err)\n}\n\nresp, err := dsClient.Reader.GetObjects(context.Background(), \u0026reader.GetObjectsRequest{})\n```\n\n**Examples**\n\nAll services use the same configuration:\n```json\n{\n\t\"address\": \"directory.prod.aserto.com:8443\",\n\t\"api_key\": \"\u003cAPI-KEY\u003e\",\n\t\"tenant_id\": \"\u003cTENANT-ID\u003e\"\n}\n```\n\n\nAll services use the same configuration except for the writer, that uses a different address:\n```json\n{\n\t\"address\": \"localhost:9292\",\n\t\"writer\": {\n\t\t\"address\": \"localhost:9293\"\n\t}\n}\n```\n\nOnly a reader and writer are configured. `Client.Importer` and `Client.Exporter` are nil:\n```json\n{\n\t\"reader\": {\n\t\t\"address\": \"localhost:9292\"\n\t},\n\t\"writer\": {\n\t\t\"address\": \"localhost:9293\"\n\t}\n}\n```\n\n\n## Middleware\n\nTo easily integrate Aserto authorization into your own services middleware implementations for common\nframeworks are available as submodules of `go-aserto/middleware`.\n\n* `middleware/httpz` provides middleware for HTTP servers using the standard [net/http](https://pkg.go.dev/net/http) package.\n* `middleware/gorillaz` provides middleware for HTTP servers using [gorilla/mux](https://github.com/gorilla/mux).\n* `middleware/ginz` provides middleware for HTTP servers using the [Gin web framework](https://gin-gonic.com).\n* `middleware/grpcz` provides middleware for gRPC servers.\n* `middleware/humaz` provides middleware for HTTP servers using the [Huma REST API framework](https://huma.rocks/).\n\nWhen authorization middleware is configured and attached to a server, it examines incoming requests, extracts\nauthorization parameters such as the caller's identity, calls the Aserto authorizers, and rejects requests if their\naccess is denied.\n\nAll middleware are created from an `AuthorizerClient` and a `Policy` with parameters that can be shared\nby all authorization calls.\n\n```go\n// Policy holds global authorization options that apply to all requests.\ntype Policy struct {\n\t// Name is the Name of the aserto policy being queried for authorization.\n\tName string\n\n\t// Path is the name of the policy package to evaluate.\n\t// If left empty, a policy mapper must be attached to the middleware to provide\n\t// the policy path from incoming messages.\n\tPath string\n\n\t// Decision is the authorization rule to use.\n\tDecision string\n}\n```\n\nThe value of several authorization parameters often depends on the content of incoming requests. Those are:\n\n* Identity - the identity (subject name or JWT) of the caller.\n* Policy Path - the name of the authorization policy package to evaluate. A default value can be set in `Policy.Path`\n  when creating the middleware, but the path is often dependent on the details of the request being authorized.\n* Resource Context - Additional data sent to the authorizer as JSON.\n\n### Identity\n\nMiddleware offer control over the identity used in authorization calls:\n\n```go\n// Use the subject name \"george@acmecorp.com\".\nmiddleware.Identity.Subject().ID(\"george@acmecorp.com\")\n\n// Use a JWT from the Authorization header.\nmiddleware.Identity.JWT().FromHeader(\"Authorization\")\n\n// Use subject name from the \"identity\" metadata key in the request `Context`.\nmiddleware.Identity.Subject().FromMetadata(\"identity\")\n\n// Read identity from the context value \"user\". Middleware infers the identity type from the value.\nmiddleware.Identity.FromContext(\"user\")\n\n// Manually pass the identity to the authorizer without resolving it to a user.\n// Manual identities are availabe in the authorizer's policy language through the \"input.identity\" variable.\nmiddleware.Manual().ID(\"object_id\")\n```\n\nIn addition, it is possible to provide custom logic to specify the caller's identity. For example, in HTTP middleware:\n\n```go\nmiddleware.Identity.Mapper(func(r *http.Request, identity middleware.Identity) {\n\tusername := getUserFromRequest(r) // custom logic to get user identity\n\n\tidentity.Subject().ID(username) // set the caller's identity for the request\n})\n```\n\nIn all cases, if a value cannot be retrieved from the specified source (header, context, etc.), the authorization\ncall checks for unauthenticated access.\n\n### Policy\n\nThe authorization policy's ID and the decision to be evaluated are specified when creating authorization Middleware,\nbut the policy path is often derived from the URL or method being called.\n\nBy default, the policy path is derived from the URL path in HTTP middleware and the `grpc.Method` in gRPC middleware.\n\nTo provide custom logic, use `middleware.WithPolicyPathMapper()`. For example, in gRPC middleware:\n\n```go\nmiddleware.WithPolicyPathMapper(func(ctx context.Context, req any) string {\n\tpath := getPolicyPath(ctx, req) // custom logic to retrieve a JWT token\n\treturn path\n})\n```\n\n### Resource\n\nA resource can be any structured data that the authorization policy uses to evaluate decisions.\nBy default, middleware do not include a resource in authorization calls.\n\nTo add resource data, use `Middleware.WithResourceMapper()` to attach custom logic. For example, in HTTP middleware:\n\n```go\nmiddleware.WithResourceMapper(func(r *http.Request, resource map[string]any) {\n\taccountID := getAccountID(r)         // custom logic to retrieve a value from the request\n\n\tresource[\"account_id\"] = accountID   // add the value as a field to the resource context\n})\n```\n\n`Middleware.WithResourceMapper()` can be called multiple times to add more than one mapper. Each mapper can add\nor remove fields from the resoruce context. Mappers are called in the order in which they are added.\n\nIn addition to these, each middleware has built-in mappers that can handle common use-cases.\n\n\n### HTTP Middleware\n\nTwo flavors of HTTP middleware are available:\n\n* `middleware/httpz`: Middleware for HTTP servers using the standard [net/http](https://pkg.go.dev/net/http) package.\n* `middleware/gorillaz`: Middleware with support for [gorilla/mux](https://pkg.go.dev/github.com/gorilla/mux).\n* `middleware/ginz`: Middleware for the [Gin](https://github.com/gin-gonic/gin) web framework.\n* `middleware/humaz`: Middleware for the the [Huma REST API framework](https://huma.rocks/).\n\nBoth are constructed and configured in a similar way. They differ in the signature of their `Handler()`\nfunction, which is used to attach them to HTTP routes, and in the signatures of their mapper functions.\n\n#### net/http Middleware\n\n```go\nimport (\n\t\"github.com/aserto-dev/go-aserto/middleware\"\n\t\"github.com/aserto-dev/go-aserto/middleware/httpz\"\n)\n...\nmw := httpz.New(\n\tazClient,\n\tmiddleware.Policy{\n\t\tDecision:\t   \"allowed\",\n\t},\n)\n```\n\nAdding the created authorization middleware to a basic `net/http` server may look something like this:\n\n```go\nhttp.Handle(\"/users\", mw.HandlerFunc(usersHandler))\n```\n\nThe default behavior of the HTTP middleware is:\n\n* Identity is retrieved from the \"Authorization\" HTTP Header, if present.\n* Policy path is retrieved from the request URL and method to form a path of the form `METHOD.path.to.endpoint`.\n* No resource context is included in authorization calls by default.\n\n\n#### gorilla/mux Middleware\n\n```go\nimport (\n\t\"github.com/aserto-dev/go-aserto/middleware\"\n\t\"github.com/aserto-dev/go-aserto/middleware/gorillaz\"\n)\n...\nmw := gorillaz.New(\n\tazClient,\n\tmiddleware.Policy{\n\t\tDecision:\t   \"allowed\",\n\t},\n)\n```\n\nAdding the created authorization middleware to a basic `net/http` server may look something like this:\n\n```go\nhttp.Handle(\"/users\", mw.Handler(usersHandler))\n```\n\nThe popular [`gorilla/mux`](https://github.com/gorilla/mux) package provides a powerful and flexible HTTP router\nwith support for URL path paremeters.\nAttaching the standard authorization middleware to a `gorilla/mux` server is as simple as:\n\n```go\nrouter := mux.NewRouter()\nrouter.Use(mw.Handler)\n\nrouter.HandleFunc(\"/users/{id}\", userHandler).Methods(\"GET\")\n```\n\nThe default behavior of the gorilla/mux middleware is:\n\n* Identity is retrieved from the \"Authorization\" HTTP Header, if present.\n* Policy path is retrieved from the request URL and method to form a path of the form `METHOD.path.to.endpoint`.\n  If the route contains path parameters (e.g. `\"api/products/{id}\"`), the surrounding braces are replaced with a\n  double-underscore prefix. For example, a request to `GET api/products/{id}` gets the policy path `GET.api.products.__id`.\n* All path parameters are included in the resource context.\n  For example, if the route is defined as `\"api/products/{id}\"` and the incoming request URL path is\n  `\"api/products/123\"` then the resource context will be `{\"id\": \"123\"}`.\n\n\n#### Gin Middleware\n\nThe gin middleware looks and behaves just like the net/http middleware but uses `gin.Context` instead of `http.Request`.\n\n#### Huma Middleware\n\nThe huma middleware looks and behaves just like the net/http middleware but uses `huma.Context` instead of `http.Request`.\n\n\n### Relation-Based Access Control (ReBAC)\n\nIn addition to the pattern described above, in which each route is authorized by its own policy module,\nthe HTTP middleware can be used to implement Relation-Based Access Control (ReBAC) in which authorization\ndecisions are made by checking if a given subject has the necessary permission or relation to the object being accessed.\n\nSee [here](https://www.topaz.sh/docs/directory) for a more in-depth overview of ReBAC in Aserto.\n\nThe canonical policy for ReBAC is [ghcr.io/aserto-policies/policy-rebac](https://github.com/aserto-templates/policy-rebac/tree/main/content).\n\nThe `Check()` function on HTTP middleware (`httpz`, `gorillaz`, or `ginz`) to annotate individual routes with\ninstructions for populating the resource context for ReBAC checks.\n\nA check call needs three pieces of information:\n\n* The type and ID of the object being accessed.\n* The name of the relation or permission to check.\n* The type and ID of the subject attempting to access the object.\n\nExample:\n```go\nrouter := mux.NewRouter()\nrouter.Handle(\n\t\"/items/{id}\",\n\tmw.Check(\n\t\tstd.WithObjectType(\"item\"),\n\t\tstd.WithObjectIDFromVar(\"id\"),\n\t\tstd.WithRelation(\"read\"),\n\t).HandlerFunc(GetItem),\n).Methods(\"GET\")\n```\n\n`GetItem()` is an http handler function that serves GET request to the `/items/{id}` route.\nThe `mw.Check` call only authorizes requests if the calling user has the `read` permission on an object of type `item`\nwith the object ID extracted from the route's `{id}` parameter.\nThe subject type is `user` by default and the subject ID is inferred from the `Authorization` header.\n\n#### Check Options\n\nThe `Check()` function accepts options that configure the object, subject, and relation sent to the authorizer.\n\n**`WithIdentityMapper(IdentityMapper)`** can be used to override the identity context sent to the authorizer. The `mapper` is a\nfunction that takes the incoming request and a `middleware.Identity` and can set options on the `Identity` object based on\ninformation from the request.\nIf an identity mapper isn't provided, the check call uses the identity configured on the middleware object on which\nthe `Check` call is made.\n\n**`WithRelation(string)`** sets the relation name sent to the authorizer.\n\n**`WithRelationMapper(StringMapper)`** can be used in cases where the relation to be checked isn't known ahead of time. It\nreceives a function that takes the incoming request and returns the name of the relation or permission to check.\n\n**`WithObjectType(string)`** sets the object type sent to the authorizer.\n\n**`WithObjectID(string)`** sets the object ID sent to the authorizer.\n\n**`WithObjectIDMapper(StringMapper)`** is used to determine the object ID sent to the authorizer at runtime. It receives\na function that takes the incoming request and returns an object ID.\n\n**`WithObjectIDFromVar(string)`** (only in `gorillaz` and `ginz` middleware) configures the check call to use the value of\na path parameter as the object ID sent to the authorizer.\n\n**`WithObjectMapper(ObjectMapper)`** can be used to set both the object type and ID at runtime. It receives a function that\ntakes the incoming request and returns a `(objectType string, objectID string)` pair.\n\n**`WithPolicyPath(string)`** sets the name of the policy module to evaluate in check calls. It defaults to `check`.\nIf the `Policy` object used to construct the middleware contains the `Root` field, the root is used as a prefix.\nFor example, if the root is set to `\"myPolicy\"`, the `Check` call looks for a policy module named `myPolicy.check`.\n\n### gRPC Middleware\n\nThe gRPC middleware is available in the sub-package `middleware/grpcz`.\nIt implements unary and stream gRPC server interceptors in its `.Unary()` and `.Stream()` methods.\n\n```go\nimport (\n\t\"github.com/aserto-dev/go-aserto/middleware\"\n\t\"github.com/aserto-dev/go-aserto/middleware/grpcz\"\n\t\"google.golang.org/grpc\"\n)\n...\nmiddleware, err := grpcz.New(\n\tazClient,\n\tmiddleware.Policy{\n\t\tDecision: \t   \"allowed\",\n\t},\n)\n\nserver := grpc.NewServer(\n\tgrpc.UnaryInterceptor(middleware.Unary),\n\tgrpc.StreamInterceptor(middleware.Stream),\n)\n```\n\n#### Mappers\n\nIn addition to the general `WithIdentityMapper`, `WithPolicyPathMapper`, and `WithResourceMapper`, the gRPC middleware\nprovides methods to help construct resource contexts from incoming messages.\n\n**`WithResourceFromFields(fields ...string)`** selects a specified set of fields from the incoming message to be\nincluded in the resource context.\n\n**WithResourceFromMessageByPath(fieldsByPath map[string][]string, defaults ...string)** is similar to\n`WithResourceFromFields` but can select different sets  of fields depending on which service method is called.\n\n**WithResourceFromContextValue(ctxKey any, field string)** reads a value from the incoming request context\nand adds it as a field to the resource context.\n\n#### Default Mappers\n\nThe default behavior of the gRPC middleware is:\n\n* Identity is pulled form the `\"authorization\"` metadata field (i.e. `middleware.Identity.FromMetadata(\"authorization\")`).\n* Policy path is constructed from `grpc.Method()` with dots (`.`) replacing path delimiters (`/`).\n* No Resource Context is included in authorization calls by default.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faserto-dev%2Fgo-aserto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faserto-dev%2Fgo-aserto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faserto-dev%2Fgo-aserto/lists"}