Skip to content

Commit

Permalink
feat: Add Access Control Policy (sourcenetwork#2338)
Browse files Browse the repository at this point in the history
## Relevant issue(s)
Part of Epic sourcenetwork#1738
Resolves sourcenetwork#2019
Resolves sourcenetwork#2020
Resolves sourcenetwork#2228

## Description
- [Rendered ACP
Doc](https://github.com/shahzadlone/defradb/blob/lone/acp-sourcehub-module/acp/README.md)
- Introduces an ACP Interface that needs to be satisfied for any type of
access control module.
- This PR implements a local embedded access control system for DefraDB
by implementing the ACP interface.
- There should be no remote calls (everything introduced here is local).
- There should be no blocking of any kind, no async stuff was
introduced.
- Documents now have the following three states, from a requester's POV:
    - Public document (always accessible).
    - Accessible document (accessible if requested by the owner)
- Inaccessible/private document (can't access if requested by non-owner
or non-identity request)
- Ability to add permissioned schema.
- Added permissioned schema examples.
- Ability to add policy.
- Added policy examples (JSON and YAML).
- Ability to specify simple identity addresses on requests.
- Mocks and Documents Generated (old and new).

### Demo
```go
AddPolicy{
	Creator: "source1zzg43wdrhmmk89z3pmejwete2kkd4a3vn7w969",
	Policy: `
		description: a test policy which marks a collection in a database as a resource

		actor:
		  name: actor

		resources:
		  users:
			permissions:
			  read:
				expr: owner + reader
			  write:
				expr: owner

			relations:
			  owner:
				types:
				  - actor
			  reader:
				types:
				  - actor
			  admin:
				manages:
				  - reader
				types:
				  - actor
	`,
	ExpectedPolicyID: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001",
}

SchemaUpdate{
	Schema: `
		type Users @Policy(id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", resource: "users") {
			name: String
			age: Int
		}
	`,
}

CreateDoc{
	Identity: "source1zzg43wdrhmmk89z3pmejwete2kkd4a3vn7w969",
	Doc: `{"name": "John", "age": 27 }`,
}

Request{
	Identity: "source1zzg43wdrhmmk89z3pmejwete2kkd4a3vn7w969",
	Request: `
		query {
			Users {
				_docID
				name
				age
			}
		}
	`,
	Results: []map[string]any{
		{
			"_docID": "bae-88b63198-7d38-5714-a9ff-21ba46374fd1",
			"name":   "John",
			"age":    int64(27),
		},
	},
}

// The Following Requests Don't Have Access

Request{
	Request: `
		query {
			Users {
				_docID
				name
				age
			}
		}
	`,
	Results: []map[string]any{},
}

Request{
	Identity: "cosmos1x25hhksxhu86r45hqwk28dd70qzux3262hdrll".
	Request: `
		query {
			Users {
				_docID
				name
				age
			}
		}
	`,
	Results: []map[string]any{},
}

```

### Features
- [x] In-memory and filebased acp module.
- [x] Registering of a document with ACP Module on creation.
- [x] Add policy command HTTP & CLI.
- [x] Detect Policy Marshal Format on adding.
- [x] Add permissioned schema.
- [x] Reject schema on validation failure.
- [x] Accept schema on validation success.
- [x] Permissioned Fetcher
- [x] Specify Identity on doc create.
- [x] Specify Identity on doc delete.
- [x] Specify Identity on doc update.
- [x] Specify Identity on request.


#### Things That Are In Scope Of This PR:
- All previous features should behave as they were.
- There should be no performance costs/hits to any previous
functionality.
- Access Control would only be registered for a document on a collection
if:
    - Have ACP Module initialized / avaialable programatically
    - The creation request had an Indentity attached.
- The collection has a permission on it (i.e. collection is
permissioned).
- Access Controled Read/Write, after Creation and Registering:
- If there is no ACP module, have access to all documents (as if acp is
turned off).
- If there is no Indentity can only operate on public documents
(unregistered documents with acp).
- If there is Permissioned Collection, Identity and ACPModule then
operate on access controled document.
- Cosmos Identity Addresses.
- Adding of a policy (CLI & HTTP), Tests:
./tests/integration/acp/add_policy
- Validation of Linked Policy Resource DPI on Schema Add:
    - Accepting of a permissioned schema on Valid DPI Resource
    - Rejecting of a permissioned schema on Invalid DPI Resource
    - Tests for both here: ./tests/integration/acp/schema/add_dpi


#### Things That Are Out Of Scope Of This PR:
- Full Fledged Indentity with Authentication
- Using ACP with any other feature is not supported.
    - P2P
    - Secondary Indexes
    - Type Joins
    - Aggregates
    - Backup & Recover
    - Views
    - Lens


#### De-scope to after merge.
- Update tracking issue with road-map and priority of next tasks.
- Add simple identity generate utility command
- Add simple identity validation utility command
- Fix the identity validation panic

### For Reviewers:

#### Recommendations:
- To begin might want to read and familiarize yourself with some
material under:
-
[acp/README](https://github.com/shahzadlone/defradb/blob/lone/acp-sourcehub-module/acp/README.md)
- When looking at tests can read more about what they test in:
- [ACP Test
Structure](https://github.com/shahzadlone/defradb/blob/lone/acp-sourcehub-module/tests/integration/acp/README.md)
- [Add Policy
Tests](https://github.com/shahzadlone/defradb/blob/lone/acp-sourcehub-module/tests/integration/acp/add_policy/README.md)
- [Add Schema With DPI
Tests](https://github.com/shahzadlone/defradb/blob/lone/acp-sourcehub-module/tests/integration/acp/schema/add_dpi/README.md)
- Would highly encourage commit by commit review.

#### Commit Priorities:
- Commits with label `PR(-)` are unrelated/irrelevant to this PR, can be
ignored.
- Commits with label `PR(ACP)` are most important to review as they are
specific to ACP implementation.
- Commits with label `PR(IDENTITY)` are also important as they are
specific to Indentity implementation.
- Commits with label `PR(*-TEST)` are test related commits that should
be looked at.
- Commits with label `PR(ACP-*)` are assisting ACP commits (medium
priority).
- Commits with label `PR(IDENTITY-*)` are assisting ACP commits (medium
priority).
- Commits with label `PR(WIP)` Should not exist before merge (work in
progress commits).
- Commits with label `PR(DROP)` Temporary commits that will be dropped
before the merge.

## Tasks
- [x] I made sure the code is well commented, particularly
hard-to-understand areas.
- [ ] I made sure the repository-held documentation is changed
accordingly.
- [x] I made sure the pull request title adheres to the conventional
commit style (the subset used in the project can be found in
[tools/configs/chglog/config.yml](tools/configs/chglog/config.yml)).
- [x] I made sure to discuss its limitations such as threats to
validity, vulnerability to mistake and misuse, robustness to
invalidation of assumptions, resource requirements, ...

## How has this been tested?
- [x] There are add policy tests
- [x] There are adding of permissioned schema (with dpi) tests.
- [x] There are end-to-end tests with doc creation and read using
identity.

Specify the platform(s) on which this was tested:
- Manjaro WSL2
  • Loading branch information
shahzadlone authored Apr 3, 2024
1 parent 34ff648 commit a46c1fa
Show file tree
Hide file tree
Showing 232 changed files with 15,100 additions and 1,177 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,9 @@ defradb start --tls --pubkeypath ~/path-to-pubkey.key --privkeypath ~/path-to-pr

```

## Access Control System
Read more about the access control [here](./acp/README.md).

## Supporting CORS

When accessing DefraDB through a frontend interface, you may be confronted with a CORS error. That is because, by default, DefraDB will not have any allowed origins set. To specify which origins should be allowed to access your DefraDB endpoint, you can specify them when starting the database:
Expand Down
442 changes: 442 additions & 0 deletions acp/README.md

Large diffs are not rendered by default.

100 changes: 100 additions & 0 deletions acp/acp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2024 Democratized Data Foundation
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package acp

import (
"context"

"github.com/sourcenetwork/immutable"

"github.com/sourcenetwork/corelog"
)

var (
log = corelog.NewLogger("acp")

// NoACP is an empty ACP, this is used to disable access control.
NoACP = immutable.None[ACP]()
)

// ACP is the interface to all types of access control that might exist.
type ACP interface {
// Init initializes the acp, with an absolute path. The provided path indicates where the
// persistent data will be stored for acp.
//
// If the path is empty then acp will run in memory.
Init(ctx context.Context, path string)

// Start starts the acp, using the initialized path. Will recover acp state
// from a previous run if under the same path.
//
// If the path is empty then acp will run in memory.
Start(ctx context.Context) error

// Close closes the resources in use by acp.
Close() error

// AddPolicy attempts to add the given policy. Detects the format of the policy automatically
// by assuming YAML format if JSON validation fails. Upon success a policyID is returned,
// otherwise returns error.
//
// A policy can not be added without a creator identity (sourcehub address).
AddPolicy(ctx context.Context, creatorID string, policy string) (string, error)

// ValidateResourceExistsOnValidDPI performs DPI validation of the resource (matching resource name)
// that is on the policy (matching policyID), returns an error upon validation failure.
//
// Learn more about the DefraDB Policy Interface [DPI](/acp/README.md)
ValidateResourceExistsOnValidDPI(
ctx context.Context,
policyID string,
resourceName string,
) error

// RegisterDocObject registers the document (object) to have access control.
// No error is returned upon successful registering of a document.
//
// Note(s):
// - This function does not check the collection to see if the document actually exists.
// - Some documents might be created without an identity signature so they would have public access.
// - actorID here is the identity of the actor registering the document object.
RegisterDocObject(
ctx context.Context,
actorID string,
policyID string,
resourceName string,
docID string,
) error

// IsDocRegistered returns true if the document was found to be registered, otherwise returns false.
// If check failed then an error and false will be returned.
IsDocRegistered(
ctx context.Context,
policyID string,
resourceName string,
docID string,
) (bool, error)

// CheckDocAccess returns true if the check was successfull and the request has access to the document. If
// the check was successful but the request does not have access to the document, then returns false.
// Otherwise if check failed then an error is returned (and the boolean result should not be used).
//
// Note(s):
// - permission here is a valid DPI permission we are checking for ("read" or "write").
CheckDocAccess(
ctx context.Context,
permission DPIPermission,
actorID string,
policyID string,
resourceName string,
docID string,
) (bool, error)
}
Loading

0 comments on commit a46c1fa

Please sign in to comment.