Hi all, Following `condition` is not working. It ...
# help
a
Hi all, Following
condition
is not working. It should only
ALLOW
when
tenantId
and
organizationId
matches. But it’s not working as expected. Any suggestions?
org_staff_roles.yaml
Copy code
# yaml-language-server: $schema=<https://api.cerbos.dev/latest/cerbos/policy/v1/Policy.schema.json>
# docs: <https://docs.cerbos.dev/cerbos/latest/policies/derived_roles>
apiVersion: api.cerbos.dev/v1
derivedRoles:
  name: org_staff_roles
  definitions:
    - name: MANAGER
      parentRoles:
        - admin
      condition:
        match:
          all: 
            of:
              - expr: R.attr.tenantId == P.attr.tenantId
              - expr: R.attr.organizationId == P.attr.organizationId
    - name: READ_ONLY
      parentRoles:
        - user
      condition:
        match:
          all: 
            of:
              - expr: R.attr.tenantId == P.attr.tenantId
              - expr: R.attr.organizationId == P.attr.organizationId
    - name: APPROVER
      parentRoles:
        - user
      condition:
        match:
          all: 
            of:
              - expr: R.attr.tenantId == P.attr.tenantId
              - expr: R.attr.organizationId == P.attr.organizationId
resource-policy.yml
Copy code
apiVersion: api.cerbos.dev/v1
resourcePolicy:
  resource: inventory
  version: default
  importDerivedRoles:
    - user_roles
    - admin_roles
    - org_staff_roles
  rules:
    - actions: ["*"]
      effect: EFFECT_ALLOW
      roles:
        - ORG_ADMIN
        - SUPER_ADMIN
      name: inventory_admin_rule
    - actions:
        [ 
          "create",
          "read",
          "update",
          "delete",
          "approve"
        ]
      effect: EFFECT_ALLOW
      roles:
        - MANAGER
      name: inventory_manager_rule
    - actions:
        [
          "approve"
        ]
      effect: EFFECT_ALLOW
      roles:
        - APPROVER
      name: inventory_creator_rule
    - actions:
        [ 
          "read",
        ]
      effect: EFFECT_ALLOW
      roles:
        - CHECKER
      name: inventory_checker_rule
request body: Should be
DENY
all actions since,
organizationId
does not match between, resource and principal. But it’s being
ALLOWED
.
Copy code
{
  "requestId": "123123",
  "principal": {
    "id": "123",
    "roles": [
      "MANAGER"
    ],
    "attr": {
      "organizationId": "123",
      "tenantId": "1234"
    }
  },
  "resources": [
    {
      "resource": {
        "kind": "inventory",
        "id": "1234",
        "attr": {
          "organizationId": "123333",
          "tenantId": "1234"
        }
      },
      "actions": [
        "approve",
        "create",
        "delete",
        "read",
        "update"
      ]
    }
  ]
}
d
Hi Ankit, Here’s a playground instance with your example. Your resource policy contains the rule:
Copy code
- actions:
        [ 
          "create",
          "read",
          "update",
          "delete",
          "approve"
        ]
      effect: EFFECT_ALLOW
      roles:
        - MANAGER
The policy here does not refer to a derived role but to a “parent” role. Then, you pass this
MANAGER
role in the request, so you get the
EFFECT_ALLOW
. If you want to refer to a derived role
MANAGER
you need to make two changes: 1. In the policy, use
derivedRoles
field instead of
roles.
2. In the request pass a parent role. I reckon it is
admin
. Derived roles will be calculated by Cerbos.
🙌 1
a
Thanks @Dennis (Cerbos) it’s working as expected! Btw, in the request we shouldn’t provide
derviedRoles
. It should be either
user
or
admin
, right?
🙌 1
g
correct,
derivedRoles
are derived within cerbos based on the the information in the request, and should not be explicitly set in the request itself
👍 1
👍🏻 1
a
Thanks @Gregory O'Grady! How can I assign roles to users and differentiate them in frontend/backend? Eg: UserA is a manager and UserB is a reviewer. I need to assign them
MANAGER
and
REVIEWER
roles accordingly. And check whether they are authorised for certain actions based on those roles. How can I achieve this, if we cannot provide derived roles in request body? cc: @Dennis (Cerbos)
d
According to your policies, a derived role
MANAGER
is assigned according to the following condition:
Copy code
apiVersion: api.cerbos.dev/v1
derivedRoles:
  name: org_staff_roles
  definitions:
    - name: MANAGER
      parentRoles:
        - admin
      condition:
        match:
          all: 
            of:
              - expr: R.attr.tenantId == P.attr.tenantId
              - expr: R.attr.organizationId == P.attr.organizationId
As long as you pass an
admin
role in the request and all the conditions regarding
tenantId
and
organizationId
are met, the derived is assigned automatically.
Derived roles are virtual roles. They are basically a syntactic sugar. These two rules are equivalent:
Copy code
- actions:
        - create
        - read
        - update
        - delete
        - approve
      effect: EFFECT_ALLOW
      derivedRoles:
        - MANAGER
      name: inventory_manager_rule
    - actions:
        - create
        - read
        - update
        - delete
        - approve
      effect: EFFECT_ALLOW
      roles:
        - admin
      condition:
        match:
          all: 
            of:
              - expr: R.attr.tenantId == P.attr.tenantId
              - expr: R.attr.organizationId == P.attr.organizationId       
      name: inventory_manager_rule_NO_DERIVED_ROLES
I presume your application defines an admin role.
a
In that case, I need to return
admin
to request body. when user is assigned
MANAGER
or
SUPER_ADMIN
roles. Else,
user
role, if none of them are assigned.
d
Yes, according to your policies, you need to provide an
admin
or a
user
role in the request. If it also sets
includeMeta
flag, the response metadata will contain effective derived roles.
👍🏻 1
a
Hi @Dennis (Cerbos) It seems there’s some compatibility issue of cerbos-sdk-go with tier.run/tier package. Below is the error which I get when cerbos package is added
Copy code
# tier.run/refs
../../../../../pkg/mod/tier.run@v0.12.0/refs/refs.go:286:22: type func(a FeaturePlan, b FeaturePlan) bool of func(a, b FeaturePlan) bool {…} does not match inferred type func(a FeaturePlan, b FeaturePlan) int for func(a E, b E) int
I’ve tried to downgrade the versions. But, still the same error. There could be some incompatibility between package versions, any idea which internal package in
cerbos-sdk-go
would be causing this issue? 😅
c
Hi. I don't think it's due to an incompatibility between packages. It looks like you have a function somewhere that's expected to return a bool but it is returning an int instead. My guess is that one of the changes you did to add Cerbos has inadvertently changed the data type of some execution path.
a
The above error is from tier.run/tier package. Something related to type inference.
c
Yes. I think it's trying to act on something you've passed to that SDK and it's failing deep inside it.
a
Yeah, and it occurs only when cerbos-sdk-go is added into my module. Maybe some of it’s dependencies are creating issue!
c
That's quite rare in Go. It's hard to diagnose the issue without looking at the code though. Do you have a minimal reproduction case that we can use to figure out what's going on?
a
I can share the go.mod file
before
and
after
cerbos-sdk-go
is added. Will that be helpful?
c
I can do that myself in a new project. Let me try that first.
🙌🏻 1
So, I get that error from Tier with or without Cerbos as a dependency 🤔
a
You’ll only get that error when cerbos is added
c
Not in my case. I just have a dependency on tier and it still errors
Which version of Go are you on?
a
version
1.21
Can you share the error stack?
c
Copy code
# tier.run/refs
../../../go/pkg/mod/tier.run@v0.12.0/refs/refs.go:286:22: type func(a FeaturePlan, b FeaturePlan) bool of func(a, b FeaturePlan) bool {…} does not match inferred type func(a FeaturePlan, b FeaturePlan) int for func(a E, bE) int
This is all I have in my Go file.
Copy code
package main

import (
	"context"
	"fmt"
	"log"

	"tier.run/client/tier"
)

func main() {
	t, err := tier.FromEnv()
	errExit(err)
	can := t.Can(context.Background(), "org:foo", "feature:bar")
	fmt.Println(can)
}

func errExit(err error) {
	if err != nil {
		log.Fatal("Error: %v", err)
	}
}
a
You haven’t added
cerbos
yet?
c
Nope
a
Oh, that’s strange!
1
c
Well, the problem seems to be that tier is using a fork of the
slices
package which is now in the standard library and has a different signature for the function they are using. So, the error message kind of makes sense. What I don't understand is why Go is mixing those two packages up at runtime. I do have a hunch that it's related to the linker search path.
a
That's interesting! In my case, the error occurred whenever cerbos was added, and when I removed the cerbos package. The error was gone! Hence, I thought it was due to some newly added package from cerbos which was incompatible with the dependency of the tier.run.
I can try to downgrade the tier.run/tier version and test again!
c
I ran the code in a container just to check that it wasn't something in my environment. The problem still exists. So my conclusion here is that the issue is entirely in the tier package. What's baffling is how it works in your machine at all. Could you maybe check with a container as well to see if that makes a difference?
a
Surely! Thanks for looking into this @Charith (Cerbos)!
🙇 1
c
FYI, the issue seems to be fixed in trunk.
go get -u tier.run/client/tier@main
a
Yes, it’s resolved. Thank you once again @Charith (Cerbos)! Should have tried to
go get
from the
main
branch, haha!
🙂 1
Hi @Charith (Cerbos) How can I connect to
cerbos
container through golang sdk using docker-compose? value of
CERBOS_URL
env variable is the container name provided in docker-compose.yml main.go
Copy code
path := os.Getenv("CERBOS_URL")

			c, err := cerbos.New(path)
			if err != nil {
				log.Fatalf("Failed to create client: %v", err)
			}

			allowed, err := c.IsAllowed(
				context.TODO(),
				cerbos.NewPrincipal("sally").WithRoles("user"),
				cerbos.NewResource("album:object", "A001"),
				"view",
			)

			log.Println(">>>>allowed>>>", allowed)

			if err != nil {
				log.Fatalf("Failed to check permission: %v", err)
			}
cerbos.yaml
Copy code
---
server:
  adminAPI:
    enabled: false
  httpListenAddr: ":3592"

engine:
  defaultPolicyVersion: "default"

auxData:
  jwt:
    disableVerification: true

schema:
  cacheSize: 1024 
  enforcement: reject

storage:
  driver: disk
  disk:
    directory: /data/policies
    watchForChanges: true
Getting the below error:
Failed to check permission: request failed: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp: address gatekeeper: mis
c
The address should include the port, so something like
cerbos:3593
if your container is named
cerbos
. Looks like you've called it
gatekeeper
? If so the address would be
gatekeeper:3593
.
a
I had tried with the port number, still the same issue.
c
Odd. Can you post the compose file
a
Sure @Charith (Cerbos) Below is the docker-compose.yml Btw, I had tried with
<http://gatekeeper:3592>
but still received an error.
Copy code
version: "3.7"

services:
  gatekeeper:
    container_name: gatekeeper
    image: <http://ghcr.io/cerbos/cerbos:latest|ghcr.io/cerbos/cerbos:latest>
    volumes:
      - ./gatekeeper:/data
    restart: unless-stopped
    ports:
      - 3592:3592
  sample-app:
    container_name: sample-app
    image: some-image
    ports:
      - 3000:3000
    env_file: .env
    restart: unless-stopped
    environment:
      - GATEKEEPER_URL=gatekeeper:3592
c
Ah, the SDK works over gRPC so you need to use port 3593.
a
Hi @Charith (Cerbos) Getting the following error:
Copy code
2023/11/30 20:51:34 Failed to check permission: request failed: rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: tls: first record does not look like a TLS handshake"
I’ve disabled TLS check, what could be the issue? Following the client code:
Copy code
c, err := cerbos.New("localhost:3593", cerbos.WithTLSInsecure())
			if err != nil {
				log.Fatalf("Failed to create client: %v", err)
			}

			allowed, err := c.IsAllowed(
				context.TODO(),
				cerbos.NewPrincipal("sally").WithRoles("user"),
				cerbos.NewResource("album:object", "A001"),
				"view",
			)

			log.Println(">>>>allowed>>>", allowed)

			if err != nil {
				log.Fatalf("Failed to check permission: %v", err)
			}
c
If you've disabled TLS, then the client should be created with the
cerbos.WithPlaintext()
option
a
Hi @Charith (Cerbos) I’m currently testing with golang sdk. But, IsAllowed is returning
false
instead should be
true
The same policy is working as expected in golang playground. middleware.go
Copy code
c, err := cerbos.New("localhost:3593", cerbos.WithPlaintext())
			if err != nil {
				log.Fatalf("Failed to create client: %v", err)
			}

			roles := []string{"INVENTORY_VIEW_ONLY"}
			principal := cerbos.NewPrincipal("1234").WithRoles(roles...).WithAttr("organizationId", "123").WithAttr("tenantId", "123")
			resource := cerbos.NewResource("inventory", "A001").WithAttr("organizationId", "123").WithAttr("tenantId", "123")

			allowed, err := c.IsAllowed(
				ctx.Request().Context(),
				principal,
				resource,
				"VIEW",
			)

			log.Println(">>>>>allowed>>>>>", allowed)
resource policy
Copy code
apiVersion: api.cerbos.dev/v1
resourcePolicy:
  resource: inventory
  version: default
  importDerivedRoles:
    - admin_roles
    - org_inventory_staff_roles
  rules:
    # - actions: ["CREATE","VIEW","UPDATE","DELETE","APPROVE"]
    #   effect: EFFECT_ALLOW
    #   derivedRoles:
    #     - INVENTORY_MANAGER
    #   name: inventory_manager_rule

    - actions: ["VIEW"]
      effect: EFFECT_ALLOW
      roles:
        - INVENTORY_VIEW_ONLY
      name: inventory_view_only_rule
      condition:
        match:
          all: 
            of:
              - expr: R.attr.tenantId == P.attr.tenantId
              - expr: R.attr.organizationId == P.attr.organizationId
              - expr: R.kind == "inventory"

    - actions: ["VIEW","APPROVE"]
      effect: EFFECT_ALLOW
      roles:
        - INVENTORY_APPROVE_ONLY
      name: inventory_approve_only_rule
      condition:
        match:
          all: 
            of:
              - expr: R.attr.tenantId == P.attr.tenantId
              - expr: R.attr.organizationId == P.attr.organizationId
              - expr: R.kind == "inventory"
o
Hi, I think the policy version might be the culprit. Would you mind setting it to
default
and gave a try?
a
Hi @oguzhan Below is the cerbos.yaml.
default
is being provided.
Copy code
---
server:
  adminAPI:
    enabled: true
  httpListenAddr: ":3592"
  grpcListenAddr: ":3593"

engine:
  defaultPolicyVersion: "default"

auxData:
  jwt:
    disableVerification: true

schema:
  cacheSize: 1024
  enforcement: warn
metricsEnabled: true
storage:
  driver: disk
  disk:
    directory: /data/policies
    watchForChanges: true
o
I meant setting it to the
default
in the SDK part, sorry for the confusion (😬). you could change this;
Copy code
resource := cerbos.NewResource("inventory", "A001")
.WithAttr("organizationId", "123")
.WithAttr("tenantId", "123")
to this;
Copy code
resource := cerbos.NewResource("inventory", "A001")
.WithPolicyVersion("default")
.WithAttr("organizationId", "123")
.WithAttr("tenantId", "123")
a
@oguzhan Still,
allowed
is
false
. I don’t think issue is related to version. Since, it’s already provided in
cerbos.yaml
file.
👀 1
o
Copy code
allowed, err := c.IsAllowed(
				ctx.Request().Context(),
				principal,
				resource,
				"VIEW",
			)
I think this call returns an error. Since the
err
is ignored and the zero value for the boolean is false, you see the
false
output in the stdout.