Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Private scope for domain-namespaced definitions #1535

Open
merlante opened this issue Sep 13, 2023 · 4 comments
Open

Private scope for domain-namespaced definitions #1535

merlante opened this issue Sep 13, 2023 · 4 comments
Labels
kind/proposal Something fundamentally needs to change state/needs discussion This can't be worked on yet

Comments

@merlante
Copy link

merlante commented Sep 13, 2023

Problem Statement

In the case where authorization logic for multiple application domains are maintained in a single spicedb schema, it's possible and desirable in many cases for dependencies to develop across those domains, i.e. permissions in definition domainA/resource1 may depend on those in definition domainB/resource2. For example, in one domain, we may wish to authorize access to hosts, while in another domain, we may wish to leverage access to a host as part of granting permissions for viewing jobs on a host. (See example below.)

However, there is a need to ensure that the authorization logic in one domain does not become too tightly coupled to the logic in another over time. In particular, we would like to avoid cases where the permissions on a resource in one domain relies on a definition in another domain that should be considered a detail of the current implementation and not exposed to other domains for use in their authorization logic.

For example:

definition inventory/host_group {
     // currently most authz logic is written here at the host_group level and inherited by hosts, etc.

     permission inventory_view_hosts = ...
}

definition inventory/host {
      relation inventory/host_group: host_group

      permission view = host_group->inventory_view_hosts
}

// good – it is intended that host permissions be managed at the inventory/host level
definition jobrunner/run {
    relation inventory/host: host

    permission view = host->view
}

// bad -- host_group should remain scoped to the inventory namespace and have no external dependencies
definition jobrunner/run {
    relation inventory/host_group: host_group

    permission view = host_group->inventory_view_hosts
}

In this example, we don’t want schema definitions in jobrunner/run to relate to host_group, because host_group (for argument’s sake) is a legacy concept that we will someday remove from our application and schema. We would like the freedom to remove host_group without breaking any schema outside of inventory/. Meanwhile, inventory/host, and its permissions, are intended to remain the same into the future (even as other “internal” authorization logic changes behind it).

To avoid this problem, it seems desirable to preclude certain resources in a domain from being referred to in a permission or relation in a resource outside of that domain. That way we can have some control over which resources in a domain should be available for general use (public) and which should be considered an implementation detail (private).

Solution Brainstorm

Introduce a "private" specifier on the resource definition. Ensure that any schema which has a resource in one domain which attempts to refer to that resource from another domain in a relation or permission statement is an invalid schema. This logic can be embedded in the schema DSL itself and be validated at that level and need not necessarily imply any changes in the spicedb code.

e.g.

definition inventory/host_group **private** {
     // currently most authz logic is written here at the host_group level and inherited by hosts, etc.

     permission inventory_view_hosts = ...
}

definition inventory/host {
      relation inventory/host_group: host_group

      permission view = host_group->inventory_view_hosts
}

// the schema validation would fail on this definition because it refers to host_group in a different namespace.
definition jobrunner/run {
    relation inventory/host_group: host_group

    permission view = host_group->inventory_view_hosts
}

The addition of a specifier like “private” should ensure backwards compatibility with existing schemas.

@merlante merlante added the kind/proposal Something fundamentally needs to change label Sep 13, 2023
@josephschorr josephschorr added the state/needs discussion This can't be worked on yet label Sep 13, 2023
@josephschorr
Copy link
Member

There has been some discussion (both internal and external) about whether some sort of scoping would be beneficial, especially with this proposal: #1437

One big question immediately is whether it should be at the definition level or at the permission/relation level

@josephschorr
Copy link
Member

@merlante If we introduced syntax such as _ in front of a definition/relation/permission/caveat such that it was inaccessible outside of the schema "module" (see #1437), would that be sufficient?

@merlante
Copy link
Author

One big question immediately is whether it should be at the definition level or at the permission/relation level

It feels cleaner and simpler to me to do it at the definition level. The main intent here is to discourage any kind of dependency on certain definitions, which should be considered "implementation detail" within a given domain.

If we introduced syntax such as _ in front of a definition/relation/permission/caveat such that it was inaccessible outside of the schema "module" (see #1437), would that be sufficient?

I think that would fulfil the requirement, yes. My only slight concern is whether the intent would be clear from the underscore.

@wscalf
Copy link

wscalf commented Sep 19, 2023

The module proposal is very interesting! That kind of file separation is a totally logical next step, especially if we wanted to manage microservice subschemas with a gitops flow or similar, which we haven't explored a lot yet but would definitely be on the table if available.

As far as permission or relation- it would likely be both (though more on that in a moment.)

Hypothesis: a subschema's public contract is going to be a subset of its definitions, the ones that represent concrete things it exposes to other services and permissions that represent operations against those resources.

Hypothesis: a subschema may also include definitions and relations that represent implementation details like resource organization, relationships used to compute permissions (ex: the creator of a resource), and synthetic relations, all of which a service provider will want to be able to refactor without impacting other services.

Taking these for granted, being able to mark it at the relation/permission level would work but would mean that an entirely internal definition would need all of its members marked that way. Applying it only to definitions would mean any references to a private definition would need to be private implicitly, and there would be no way to hide synthetic relations and such.

Allowing both with the requirement that any references to private items are also private should cover all the bases. And being able to partition those modules into separate files would be awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/proposal Something fundamentally needs to change state/needs discussion This can't be worked on yet
Projects
None yet
Development

No branches or pull requests

3 participants