diff --git a/README.md b/README.md index a7156888b9..4924170e79 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/acp/README.md b/acp/README.md new file mode 100644 index 0000000000..3fb49968f8 --- /dev/null +++ b/acp/README.md @@ -0,0 +1,442 @@ +# Introduction + +In the realm of information technology (IT) and cybersecurity, **access control** plays a pivotal role in ensuring the confidentiality, integrity, and availability of sensitive resources. Let's delve into why access control policies are crucial for protecting your valuable data. + +## What Is Access Control? + +**Access control** is a mechanism that regulates who or what can view, use, or access a specific resource within a computing environment. Its primary goal is to minimize security risks by ensuring that only **authorized users**, systems, or services have access to the resources they need. But it's more than just granting or denying access, it involves several key components: + +1. **Authentication**: Verifying the identity of an individual or system. +2. **Authorization**: Determining what actions or operations an actor is allowed to perform. +3. **Access**: Granting or denying access based on authorization. +4. **Management**: Administering access rights and permissions. +5. **Audit**: Tracking and monitoring access patterns for accountability. + +## Why Is Access Control Important? + +1. **Mitigating Security Risks**: Cybercriminals are becoming increasingly sophisticated, employing advanced techniques to breach security systems. By controlling who has access to your database, you significantly reduce the risk of unauthorized access, both from external attackers and insider threats. + +2. **Compliance with Regulations**: Various regulatory requirements, such as the **General Data Protection Regulation (GDPR)** and the **Health Insurance Portability and Accountability Act (HIPAA)**, mandate stringent access control measures to protect personal data. Implementing access control ensures compliance with these regulations. + +3. **Preventing Data Breaches**: Access control acts as a proactive measure to deter, detect, and prevent unauthorized access. It ensures that only those with the necessary permissions can access sensitive data or services. + +4. **Managing Complexity**: Modern IT infrastructure, including cloud computing and mobile devices, has exponentially increased the number of access points. Technologies like **identity and access management (IAM)** and approaches like **zero trust** help manage this complexity effectively. + +## Types of Security Access Controls + +Several access control models exist, including: + +- **Role-Based Access Control (RBAC)**: Assigns permissions to roles, roles then are granted to users. A user's active role then defines their access. (e.g., admin, user, manager). +- **Attribute-Based Access Control (ABAC)**: Considers various attributes (e.g., user attributes, resource attributes) for access decisions. +- **Discretionary Access Control (DAC)**: Users with sufficient permissions (resource owners) are to grant / share an object with other users. +- **Mandatory Access Control (MAC)**: Users are not allowed to grant access to other users. Permissions are granted based on a minimum role / hierarchy (security labels and clearances) that must be met. +- **Policy-Based Access Control (PBAC)**: Enforces access based on defined policies. +- **Relation-Based Access Control (ReBac)**: Relations between objects and users in the system are used to derive their permissions. + +- Note: **DefraDB** access control rules strongly resembles **Discretionary Access Control (DAC)**, which is implemented through a **Relation-Based Access Control System (ReBac) Engine** + +## Challenges of Access Control in Cybersecurity + +- **Distributed IT Environments**: Cloud computing and remote work create new challenges. +- **Rise of Mobility**: Mobile devices in the workplace add complexity. +- **Password Fatigue**: Balancing security with usability. +- **Data Governance**: Ensuring visibility and control. +- **Multi-Tenancy**: Managing complex permissions in SaaS applications. + +## Key takeaway +A robust access control policy system is your first line of defense against unauthorized access and data breaches. + + +# DefraDB's Access Control System + +## ReBac Authorization Model + +### Zanzibar +In 2019, Google published their [Zanzibar](https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/) paper, a paper explaining how they handle authorization across their many services. It uses access control lists but with relationship-based access control rather than role-based access control. Relationship-Based Access Control (ReBAC) establishes an authorization model where a subject's permission to access an object is defined by the presence of relationships between those subjects and objects. +The way Zanzibar works is it exposes an API with (mainly) operations to manage `Relationships` (`tuples`) and Verify Access Requests (can Bob do X) through the `Check` call. A `tuple` includes subject, relation, and object. The Check call performs Graph Search over the `tuples` to find a path between the user and the object, if such a path exist then according to `RelBAC` the user has the queried permission. It operates as a Consistent and Partition-Tolerant System. + +### Zanzi +However the Zanzibar API is centralized, so we (Source Network) created a decentralized implementation of Zanzibar called **Zanzi**. Which is powered by our SourceHub trust protocol. Zanzi is a general purpose Zanzibar implementation which operates over a KV persistence layer. + +### SourceHub ACP Module +DefraDB wraps the `local` and `remote` SourceHub ACP Modules to bring all that magic to DefraDB. + +In order to setup the relation based access control, SourceHub requires an agreed upon contract which models the `relations`, `permissions`, and `actors`. That contract is refered to as a `SourceHub Policy`. The policy model's all the `relations` and `permissions` under a `resource`. +A `resource` corresponds to that "thing" that we want to gate the access control around. This can be a `Type`, `Container`, `Schema`, `Shape` or anything that has Objects that need access control. Once the policy is finalized, it has to be uploaded to the `SourceHub Module` so it can be used. +Once the `Policy` is uploaded to the `SourceHub Module` then an `Actor` can begin registering the `Object` for access control by linking to a `Resource` that exists on the uploaded `Policy`. +After the `Object` is registered successfully, the `Actor` will then get a special built-in relation with that `Object` called the `"owner"` relation. This relation is given to the `Registerer` of an `Object`. +Then an `Actor` can issue `Check` calls to see if they have access to an `Object`. + +## Document Access Control (DAC) +In DefraDB's case we wanted to gate access control around the `Documents` that belonged to a specific `Collection`. Here, the `Collection` (i.e. the type/shape of the `Object`) can be thought of as the `Resource`, and the `Documents` are the `Objects`. + + +## Field Access Control (FAC) (coming soon) +We also want the ability to do a more granular access control than just DAC. Therefore we have `Field` level access control for situations where some fields of a `Document` need to be private, while others do not. In this case the `Document` becomes the `Resource` and the `Fields` are the `Objects` being gated. + + +## Admin Access Control (AAC) (coming soon) +We also want to model access control around the `Admin Level Operations` that exist in `DefraDB`. In this case the entire `Database` would be the `Resource` and the `Admin Level Operations` are the `Objects` being gated. + +A non-exhastive list of some operations only admins should have access for: +- Ability to turnoff ACP +- Ability to interact with the P2P system + +## SourceHub Policies Are Too Flexible +SourceHub Policies are too flexible (atleast until the ability to define `Meta Policies` is implemented). This is because SourceHub leaves it up to the user to specify any type of `Permissions` and `Relations`. However for DefraDB, there are certain guarantees that **MUST** be maintained in order for the `Policy` to be effective. For example the user can input any name for a `Permission`, or `Relation` that DefraDB has no knowledge of. Another example is when a user might make a `Policy` that does not give any `Permission` to the `owner`. Which means in the case of DAC no one will have any access to the `Document` they created. +Therefore There was a very clear need to define some rules while writing a `Resource` in a `Policy` which will be used with DefraDB's DAC, FAC, or AAC. These rules will guarantee that certain `Required Permissions` will always be there on a `Resource` and that `Owner` has the correct `Permissions`. + +We call these rules DPI A.K.A DefraDB Policy Interface. + +## Terminologies +- 'SourceHub Address' is a `Bech32` Address with a specific SourceHub prefix. +- 'Identity' is a combination of SourceHub Address and a Key-Pair Signature. +- 'DPI' means 'DefraDB Policy Interface'. +- 'Partially-DPI' policy means a policy with at least one DPI compliant resource. +- 'Permissioned Collection' means to have a policy on the collection, like: `@policy(id:".." resource: "..")` +- 'Permissioned Request' means to have a request with a SourceHub Identity. + + +## DAC DPI Rules + +To qualify as a DPI-compliant `resource`, the following rules **MUST** be satisfied: +- The resource **must include** the mandatory `registerer` (`owner`) relation within the `relations` attribute. +- The resource **must encompass** all the required permissions under the `permissions` attribute. +- Every required permission must have the required registerer relation (`owner`) in `expr`. +- The required registerer relation **must be positioned** as the leading (first) relation in `expr` (see example below). +- Any relation after the required registerer relation must only be a union set operation (`+`). + +For a `Policy` to be `DPI` compliant for DAC, all of its `resources` must be DPI compliant. +To be `Partially-DPI` at least one of its `resource` must be DPI compliant. + +### More Into The Weeds: + +All mandatory permissions are: +- Specified in the `dpi.go` file within the variable `dpiRequiredPermissions`. + +The name of the required 'registerer' relation is: +- Specified in the `dpi.go` file within the variable `requiredRegistererRelationName`. + +### DPI Resource Examples: +- Check out tests here: [tests/integration/acp/schema/add_dpi](/tests/integration/acp/schema/add_dpi) +- The tests linked are broken into `accept_*_test.go` and `reject_*_test.go` files. +- Accepted tests document the valid DPIs (as the schema is accepted). +- Rejected tests document invalid DPIs (as the schema is rejected). +- There are also some Partially-DPI tests that are both accepted and rejected depending on the resource. + +### Required Permission's Expression: +Even though the following expressions are valid generic policy expressions, they will make a +DPI compliant resource lose its DPI status as these expressions are not in accordance to +our DPI [rules](#dac-dpi-rules). Assuming these `expr` are under a required permission label: +- `expr: owner-owner` +- `expr: owner-reader` +- `expr: owner&reader` +- `expr: owner - reader` +- `expr: ownerMalicious + owner` +- `expr: ownerMalicious` +- `expr: owner_new` +- `expr: reader+owner` +- `expr: reader-owner` +- `expr: reader - owner` + +Here are some valid expression examples. Assuming these `expr` are under a required permission label: +- `expr: owner` +- `expr: owner + reader` +- `expr: owner +reader` +- `expr: owner+reader` + + +## DAC Usage CLI: + +### Adding a Policy: + +We have in `examples/dpi_policy/user_dpi_policy.yml`: +```yaml +description: A Valid DefraDB Policy Interface (DPI) + +actor: + name: actor + +resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor +``` + +CLI Command: +```sh +defradb client acp policy add -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j -f examples/dpi_policy/user_dpi_policy.yml + +``` + +Result: +```json +{ + "PolicyID": "24ab8cba6d6f0bcfe4d2712c7d95c09dd1b8076ea5a8896476413fd6c891c18c" +} +``` + +### Add schema, linking to a resource within the policy we added: + +We have in `examples/schema/permissioned/users.graphql`: +```graphql +type Users @policy( + id: "24ab8cba6d6f0bcfe4d2712c7d95c09dd1b8076ea5a8896476413fd6c891c18c", + resource: "users" +) { + name: String + age: Int +} +``` + +CLI Command: +```sh +defradb client schema add -f examples/schema/permissioned/users.graphql +``` + +Result: +```json +[ + { + "Name": "Users", + "ID": 1, + "RootID": 1, + "SchemaVersionID": "bafkreibthhctfd3rykinfa6ivvkhegp7sbhk5yvujdkhase7ilj5dz5gqi", + "Sources": [], + "Fields": [ + { + "Name": "_docID", + "ID": 0 + }, + { + "Name": "age", + "ID": 1 + }, + { + "Name": "name", + "ID": 2 + } + ], + "Indexes": [], + "Policy": { + "ID": "24ab8cba6d6f0bcfe4d2712c7d95c09dd1b8076ea5a8896476413fd6c891c18c", + "ResourceName": "users" + } + } +] + +``` + +### Create private documents (with identity) + +CLI Command: +```sh +defradb client collection create -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name Users '[{ "name": "SecretShahzad" }, { "name": "SecretLone" }]' +``` + +### Create public documents (without identity) + +CLI Command: +```sh +defradb client collection create --name Users '[{ "name": "PublicShahzad" }, { "name": "PublicLone" }]' +``` + +### Get all docIDs without an identity (shows only public): +CLI Command: +```sh +defradb client collection docIDs -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j +``` + +Result: +```json +{ + "docID": "bae-63ba68c9-78cb-5060-ab03-53ead1ec5b83", + "error": "" +} +{ + "docID": "bae-ba315e98-fb37-5225-8a3b-34a1c75cba9e", + "error": "" +} +``` + + +### Get all docIDs with an identity (shows public and owned documents): +```sh +defradb client collection docIDs -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j +``` + +Result: +```json +{ + "docID": "bae-63ba68c9-78cb-5060-ab03-53ead1ec5b83", + "error": "" +} +{ + "docID": "bae-a5830219-b8e7-5791-9836-2e494816fc0a", + "error": "" +} +{ + "docID": "bae-ba315e98-fb37-5225-8a3b-34a1c75cba9e", + "error": "" +} +{ + "docID": "bae-eafad571-e40c-55a7-bc41-3cf7d61ee891", + "error": "" +} +``` + + +### Access the private document (including field names): +CLI Command: +```sh +defradb client collection get -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name Users "bae-a5830219-b8e7-5791-9836-2e494816fc0a" +``` + +Result: +```json +{ + "_docID": "bae-a5830219-b8e7-5791-9836-2e494816fc0a", + "name": "SecretShahzad" +} +``` + +### Accessing the private document without an identity: +CLI Command: +```sh +defradb client collection get --name Users "bae-a5830219-b8e7-5791-9836-2e494816fc0a" +``` + +Error: +``` + Error: document not found or not authorized to access +``` + +### Accessing the private document with wrong identity: +CLI Command: +```sh +defradb client collection get -i cosmos1x25hhksxhu86r45hqwk28dd70qzux3262hdrll --name Users "bae-a5830219-b8e7-5791-9836-2e494816fc0a" +``` + +Error: +``` + Error: document not found or not authorized to access +``` + +### Update private document: +CLI Command: +```sh +defradb client collection update -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name Users --docID "bae-a5830219-b8e7-5791-9836-2e494816fc0a" --updater '{ "name": "SecretUpdatedShahzad" }' +``` + +Result: +```json +{ + "Count": 1, + "DocIDs": [ + "bae-a5830219-b8e7-5791-9836-2e494816fc0a" + ] +} +``` + +#### Check if it actually got updated: +CLI Command: +```sh +defradb client collection get -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name Users "bae-a5830219-b8e7-5791-9836-2e494816fc0a" +``` + +Result: +```json +{ + "_docID": "bae-a5830219-b8e7-5791-9836-2e494816fc0a", + "name": "SecretUpdatedShahzad" +} +``` + +### Update With Filter example (coming soon) + +### Delete private document: +CLI Command: +```sh +defradb client collection delete -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name Users --docID "bae-a5830219-b8e7-5791-9836-2e494816fc0a" +``` + +Result: +```json +{ + "Count": 1, + "DocIDs": [ + "bae-a5830219-b8e7-5791-9836-2e494816fc0a" + ] +} +``` + +#### Check if it actually got deleted: +CLI Command: +```sh +defradb client collection get -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name Users "bae-a5830219-b8e7-5791-9836-2e494816fc0a" +``` + +Error: +``` + Error: document not found or not authorized to access +``` + +### Delete With Filter example (coming soon) + +### Typejoin example (coming soon) + +### View example (coming soon) + +### P2P example (coming soon) + +### Backup / Import example (coming soon) + +### Secondary Indexes example (coming soon) + +### Execute Explain example (coming soon) + + +## DAC Usage HTTP: +HTTP requests work similar to their CLI counter parts, the main difference is that the identity will just be specified within the Auth Header like so: `Authorization: Basic `. + +Note: The `Basic` label will change to `Bearer ` after JWS Authentication Tokens are supported. + +## _AAC DPI Rules (coming soon)_ +## _AAC Usage: (coming soon)_ + +## _FAC DPI Rules (coming soon)_ +## _FAC Usage: (coming soon)_ + +## Warning / Caveats +The following features currently don't work with ACP, they are being actively worked on. +- [P2P: Adding a replicator with permissioned collection](https://github.com/sourcenetwork/defradb/issues/2366) +- [P2P: Subscription to a permissioned collection](https://github.com/sourcenetwork/defradb/issues/2366) +- [Adding Secondary Indexes](https://github.com/sourcenetwork/defradb/issues/2365) +- [Backing/Restoring Private Documents](https://github.com/sourcenetwork/defradb/issues/2430) + +The following features may have undefined/unstable behavior until they are properly tested: +- [Views](https://github.com/sourcenetwork/defradb/issues/2018) +- [Average Operations](https://github.com/sourcenetwork/defradb/issues/2475) +- [Count Operations](https://github.com/sourcenetwork/defradb/issues/2474) +- [Group Operations](https://github.com/sourcenetwork/defradb/issues/2473) +- [Limit Operations](https://github.com/sourcenetwork/defradb/issues/2472) +- [Order Operations](https://github.com/sourcenetwork/defradb/issues/2471) +- [Sum Operations](https://github.com/sourcenetwork/defradb/issues/2470) +- [Dag/Commit Operations](https://github.com/sourcenetwork/defradb/issues/2469) +- [Delete With Filter Operations](https://github.com/sourcenetwork/defradb/issues/2468) +- [Update With Filter Operations](https://github.com/sourcenetwork/defradb/issues/2467) +- [Type Join Many Operations](https://github.com/sourcenetwork/defradb/issues/2466) +- [Type Join One Operations](https://github.com/sourcenetwork/defradb/issues/2466) +- [Parallel Operations](https://github.com/sourcenetwork/defradb/issues/2465) +- [Execute Explain](https://github.com/sourcenetwork/defradb/issues/2464) diff --git a/acp/acp.go b/acp/acp.go new file mode 100644 index 0000000000..af99bcb86f --- /dev/null +++ b/acp/acp.go @@ -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) +} diff --git a/acp/acp_local.go b/acp/acp_local.go new file mode 100644 index 0000000000..e569efd5d0 --- /dev/null +++ b/acp/acp_local.go @@ -0,0 +1,310 @@ +// 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" + + protoTypes "github.com/cosmos/gogoproto/types" + "github.com/sourcenetwork/corelog" + "github.com/sourcenetwork/immutable" + "github.com/sourcenetwork/sourcehub/x/acp/embedded" + "github.com/sourcenetwork/sourcehub/x/acp/types" + "github.com/valyala/fastjson" + + "github.com/sourcenetwork/defradb/errors" +) + +var ( + _ ACP = (*ACPLocal)(nil) +) + +// ACPLocal represents a local acp implementation that makes no remote calls. +type ACPLocal struct { + pathToStore immutable.Option[string] + localACP *embedded.LocalACP +} + +func (l *ACPLocal) Init(ctx context.Context, path string) { + if path == "" { + l.pathToStore = immutable.None[string]() + } else { + l.pathToStore = immutable.Some(path) + } +} + +func (l *ACPLocal) Start(ctx context.Context) error { + var localACP embedded.LocalACP + var err error + + if !l.pathToStore.HasValue() { // Use a non-persistent, i.e. in memory store. + localACP, err = embedded.NewLocalACP( + embedded.WithInMemStore(), + ) + + if err != nil { + return NewErrInitializationOfACPFailed(err, "Local", "in-memory") + } + } else { // Use peristent storage. + acpStorePath := l.pathToStore.Value() + "/" + embedded.DefaultDataDir + localACP, err = embedded.NewLocalACP( + embedded.WithPersistentStorage(acpStorePath), + ) + if err != nil { + return NewErrInitializationOfACPFailed(err, "Local", l.pathToStore.Value()) + } + } + + l.localACP = &localACP + return nil +} + +func (l *ACPLocal) Close() error { + return l.localACP.Close() +} + +func (l *ACPLocal) AddPolicy( + ctx context.Context, + creatorID string, + policy string, +) (string, error) { + // Having a creator identity is a MUST requirement for adding a policy. + if creatorID == "" { + return "", ErrPolicyCreatorMustNotBeEmpty + } + + if policy == "" { + return "", ErrPolicyDataMustNotBeEmpty + } + + // Assume policy is in YAML format by default. + policyMarshalType := types.PolicyMarshalingType_SHORT_YAML + if isJSON := fastjson.Validate(policy) == nil; isJSON { // Detect JSON format. + policyMarshalType = types.PolicyMarshalingType_SHORT_JSON + } + + createPolicy := types.MsgCreatePolicy{ + Creator: creatorID, + Policy: policy, + MarshalType: policyMarshalType, + CreationTime: protoTypes.TimestampNow(), + } + + createPolicyResponse, err := l.localACP.GetMsgService().CreatePolicy( + l.localACP.GetCtx(), + &createPolicy, + ) + + if err != nil { + return "", NewErrFailedToAddPolicyWithACP(err, "Local", creatorID) + } + + policyID := createPolicyResponse.Policy.Id + log.InfoContext(ctx, "Created Policy", corelog.Any("PolicyID", policyID)) + + return policyID, nil +} + +func (l *ACPLocal) ValidateResourceExistsOnValidDPI( + ctx context.Context, + policyID string, + resourceName string, +) error { + if policyID == "" && resourceName == "" { + return ErrNoPolicyArgs + } + + if policyID == "" { + return ErrPolicyIDMustNotBeEmpty + } + + if resourceName == "" { + return ErrResourceNameMustNotBeEmpty + } + + queryPolicyRequest := types.QueryPolicyRequest{Id: policyID} + queryPolicyResponse, err := l.localACP.GetQueryService().Policy( + l.localACP.GetCtx(), + &queryPolicyRequest, + ) + + if err != nil { + if errors.Is(err, types.ErrPolicyNotFound) { + return newErrPolicyDoesNotExistWithACP(err, policyID) + } else { + return newErrPolicyValidationFailedWithACP(err, policyID) + } + } + + // So far we validated that the policy exists, now lets validate that resource exists. + resourceResponse := queryPolicyResponse.Policy.GetResourceByName(resourceName) + if resourceResponse == nil { + return newErrResourceDoesNotExistOnTargetPolicy(resourceName, policyID) + } + + // Now that we have validated that policyID exists and it contains a corresponding + // resource with the matching name, validate that all required permissions + // for DPI actually exist on the target resource. + for _, requiredPermission := range dpiRequiredPermissions { + permissionResponse := resourceResponse.GetPermissionByName(requiredPermission) + if permissionResponse == nil { + return newErrResourceIsMissingRequiredPermission( + resourceName, + requiredPermission, + policyID, + ) + } + + // Now we need to ensure that the "owner" relation has access to all the required + // permissions for DPI. This is important because even if the policy has the required + // permissions under the resource, it's possible that those permissions are not granted + // to the "owner" relation, this will help users not shoot themseleves in the foot. + // TODO-ACP: Better validation, once sourcehub implements meta-policies. + // Issue: https://github.com/sourcenetwork/defradb/issues/2359 + if err := validateDPIExpressionOfRequiredPermission( + permissionResponse.Expression, + requiredPermission, + ); err != nil { + return err + } + } + + return nil +} + +func (l *ACPLocal) RegisterDocObject( + ctx context.Context, + actorID string, + policyID string, + resourceName string, + docID string, +) error { + registerDoc := types.MsgRegisterObject{ + Creator: actorID, + PolicyId: policyID, + Object: types.NewObject(resourceName, docID), + CreationTime: protoTypes.TimestampNow(), + } + + registerDocResponse, err := l.localACP.GetMsgService().RegisterObject( + l.localACP.GetCtx(), + ®isterDoc, + ) + + if err != nil { + return NewErrFailedToRegisterDocWithACP(err, "Local", policyID, actorID, resourceName, docID) + } + + switch registerDocResponse.Result { + case types.RegistrationResult_NoOp: + return ErrObjectDidNotRegister + + case types.RegistrationResult_Registered: + log.InfoContext( + ctx, + "Document registered with local acp", + corelog.Any("PolicyID", policyID), + corelog.Any("Creator", actorID), + corelog.Any("Resource", resourceName), + corelog.Any("DocID", docID), + ) + return nil + + case types.RegistrationResult_Unarchived: + log.InfoContext( + ctx, + "Document re-registered (unarchived object) with local acp", + corelog.Any("PolicyID", policyID), + corelog.Any("Creator", actorID), + corelog.Any("Resource", resourceName), + corelog.Any("DocID", docID), + ) + return nil + } + + return ErrObjectDidNotRegister +} + +func (l *ACPLocal) IsDocRegistered( + ctx context.Context, + policyID string, + resourceName string, + docID string, +) (bool, error) { + queryObjectOwner := types.QueryObjectOwnerRequest{ + PolicyId: policyID, + Object: types.NewObject(resourceName, docID), + } + + queryObjectOwnerResponse, err := l.localACP.GetQueryService().ObjectOwner( + l.localACP.GetCtx(), + &queryObjectOwner, + ) + if err != nil { + return false, NewErrFailedToCheckIfDocIsRegisteredWithACP(err, "Local", policyID, resourceName, docID) + } + + return queryObjectOwnerResponse.IsRegistered, nil +} + +func (l *ACPLocal) CheckDocAccess( + ctx context.Context, + permission DPIPermission, + actorID string, + policyID string, + resourceName string, + docID string, +) (bool, error) { + checkDoc := types.QueryVerifyAccessRequestRequest{ + PolicyId: policyID, + AccessRequest: &types.AccessRequest{ + Operations: []*types.Operation{ + { + Object: types.NewObject(resourceName, docID), + Permission: permission.String(), + }, + }, + Actor: &types.Actor{ + Id: actorID, + }, + }, + } + + checkDocResponse, err := l.localACP.GetQueryService().VerifyAccessRequest( + l.localACP.GetCtx(), + &checkDoc, + ) + if err != nil { + return false, NewErrFailedToVerifyDocAccessWithACP(err, "Local", policyID, actorID, resourceName, docID) + } + + if checkDocResponse.Valid { + log.InfoContext( + ctx, + "Document accessible", + corelog.Any("PolicyID", policyID), + corelog.Any("ActorID", actorID), + corelog.Any("Resource", resourceName), + corelog.Any("DocID", docID), + ) + return true, nil + } else { + log.InfoContext( + ctx, + "Document inaccessible", + corelog.Any("PolicyID", policyID), + corelog.Any("ActorID", actorID), + corelog.Any("Resource", resourceName), + corelog.Any("DocID", docID), + ) + return false, nil + } +} diff --git a/acp/acp_local_test.go b/acp/acp_local_test.go new file mode 100644 index 0000000000..9abdcb04d1 --- /dev/null +++ b/acp/acp_local_test.go @@ -0,0 +1,654 @@ +// 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" + "testing" + + "github.com/stretchr/testify/require" +) + +var identity1 = "cosmos1zzg43wdrhmmk89z3pmejwete2kkd4a3vn7w969" +var identity2 = "cosmos1x25hhksxhu86r45hqwk28dd70qzux3262hdrll" + +var validPolicyID string = "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" +var validPolicy string = ` +description: a policy + +actor: + name: actor + +resources: + users: + permissions: + write: + expr: owner + read: + expr: owner + reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + ` + +func Test_LocalACP_InMemory_StartAndClose_NoError(t *testing.T) { + ctx := context.Background() + var localACP ACPLocal + + localACP.Init(ctx, "") + err := localACP.Start(ctx) + + require.Nil(t, err) + + err = localACP.Close() + require.Nil(t, err) +} + +func Test_LocalACP_PersistentMemory_StartAndClose_NoError(t *testing.T) { + acpPath := t.TempDir() + require.NotEqual(t, "", acpPath) + + ctx := context.Background() + var localACP ACPLocal + + localACP.Init(ctx, acpPath) + err := localACP.Start(ctx) + require.Nil(t, err) + + err = localACP.Close() + require.Nil(t, err) +} + +func Test_LocalACP_InMemory_AddPolicy_CanCreateTwice(t *testing.T) { + ctx := context.Background() + var localACP ACPLocal + + localACP.Init(ctx, "") + errStart := localACP.Start(ctx) + require.Nil(t, errStart) + + policyID, errAddPolicy := localACP.AddPolicy( + ctx, + identity1, + validPolicy, + ) + require.Nil(t, errAddPolicy) + + require.Equal( + t, + validPolicyID, + policyID, + ) + + errClose := localACP.Close() + require.Nil(t, errClose) + + // Since nothing is persisted should allow adding same policy again. + + localACP.Init(ctx, "") + errStart = localACP.Start(ctx) + require.Nil(t, errStart) + + policyID, errAddPolicy = localACP.AddPolicy( + ctx, + identity1, + validPolicy, + ) + require.Nil(t, errAddPolicy) + require.Equal( + t, + validPolicyID, + policyID, + ) + + errClose = localACP.Close() + require.Nil(t, errClose) +} + +func Test_LocalACP_PersistentMemory_AddPolicy_CanNotCreateTwice(t *testing.T) { + acpPath := t.TempDir() + require.NotEqual(t, "", acpPath) + + ctx := context.Background() + var localACP ACPLocal + + localACP.Init(ctx, acpPath) + errStart := localACP.Start(ctx) + require.Nil(t, errStart) + + policyID, errAddPolicy := localACP.AddPolicy( + ctx, + identity1, + validPolicy, + ) + require.Nil(t, errAddPolicy) + require.Equal( + t, + validPolicyID, + policyID, + ) + + errClose := localACP.Close() + require.Nil(t, errClose) + + // The above policy should remain persisted on restarting ACP. + + localACP.Init(ctx, acpPath) + errStart = localACP.Start(ctx) + require.Nil(t, errStart) + + // Should not allow us to create the same policy again as it exists already. + _, errAddPolicy = localACP.AddPolicy( + ctx, + identity1, + validPolicy, + ) + require.Error(t, errAddPolicy) + require.ErrorIs(t, errAddPolicy, ErrFailedToAddPolicyWithACP) + + errClose = localACP.Close() + require.Nil(t, errClose) +} + +func Test_LocalACP_InMemory_ValidateResourseExistsOrNot_ErrIfDoesntExist(t *testing.T) { + ctx := context.Background() + var localACP ACPLocal + + localACP.Init(ctx, "") + errStart := localACP.Start(ctx) + require.Nil(t, errStart) + + policyID, errAddPolicy := localACP.AddPolicy( + ctx, + identity1, + validPolicy, + ) + require.Nil(t, errAddPolicy) + require.Equal( + t, + validPolicyID, + policyID, + ) + + errValidateResourceExists := localACP.ValidateResourceExistsOnValidDPI( + ctx, + validPolicyID, + "users", + ) + require.Nil(t, errValidateResourceExists) + + errValidateResourceExists = localACP.ValidateResourceExistsOnValidDPI( + ctx, + validPolicyID, + "resourceDoesNotExist", + ) + require.Error(t, errValidateResourceExists) + require.ErrorIs(t, errValidateResourceExists, ErrResourceDoesNotExistOnTargetPolicy) + + errValidateResourceExists = localACP.ValidateResourceExistsOnValidDPI( + ctx, + "invalidPolicyID", + "resourceDoesNotExist", + ) + require.Error(t, errValidateResourceExists) + require.ErrorIs(t, errValidateResourceExists, ErrPolicyDoesNotExistWithACP) + + errClose := localACP.Close() + require.Nil(t, errClose) +} + +func Test_LocalACP_PersistentMemory_ValidateResourseExistsOrNot_ErrIfDoesntExist(t *testing.T) { + acpPath := t.TempDir() + require.NotEqual(t, "", acpPath) + + ctx := context.Background() + var localACP ACPLocal + + localACP.Init(ctx, acpPath) + errStart := localACP.Start(ctx) + require.Nil(t, errStart) + + policyID, errAddPolicy := localACP.AddPolicy( + ctx, + identity1, + validPolicy, + ) + require.Nil(t, errAddPolicy) + require.Equal( + t, + validPolicyID, + policyID, + ) + + errValidateResourceExists := localACP.ValidateResourceExistsOnValidDPI( + ctx, + validPolicyID, + "users", + ) + require.Nil(t, errValidateResourceExists) + + // Resource should still exist even after a restart. + errClose := localACP.Close() + require.Nil(t, errClose) + + localACP.Init(ctx, acpPath) + errStart = localACP.Start(ctx) + require.Nil(t, errStart) + + // Do the same check after restart. + errValidateResourceExists = localACP.ValidateResourceExistsOnValidDPI( + ctx, + validPolicyID, + "users", + ) + require.Nil(t, errValidateResourceExists) + + errValidateResourceExists = localACP.ValidateResourceExistsOnValidDPI( + ctx, + validPolicyID, + "resourceDoesNotExist", + ) + require.Error(t, errValidateResourceExists) + require.ErrorIs(t, errValidateResourceExists, ErrResourceDoesNotExistOnTargetPolicy) + + errValidateResourceExists = localACP.ValidateResourceExistsOnValidDPI( + ctx, + "invalidPolicyID", + "resourceDoesNotExist", + ) + require.Error(t, errValidateResourceExists) + require.ErrorIs(t, errValidateResourceExists, ErrPolicyDoesNotExistWithACP) + + errClose = localACP.Close() + require.Nil(t, errClose) +} + +func Test_LocalACP_InMemory_IsDocRegistered_TrueIfRegisteredFalseIfNotAndErrorOtherwise(t *testing.T) { + ctx := context.Background() + var localACP ACPLocal + + localACP.Init(ctx, "") + errStart := localACP.Start(ctx) + require.Nil(t, errStart) + + policyID, errAddPolicy := localACP.AddPolicy( + ctx, + identity1, + validPolicy, + ) + require.Nil(t, errAddPolicy) + require.Equal( + t, + validPolicyID, + policyID, + ) + + // Invalid empty doc and empty resource can't be registered. + errRegisterDoc := localACP.RegisterDocObject( + ctx, + identity1, + validPolicyID, + "", + "", + ) + require.Error(t, errRegisterDoc) + require.ErrorIs(t, errRegisterDoc, ErrFailedToRegisterDocWithACP) + + // Check if an invalid empty doc and empty resource is registered. + isDocRegistered, errDocRegistered := localACP.IsDocRegistered( + ctx, + validPolicyID, + "", + "", + ) + require.Error(t, errDocRegistered) + require.ErrorIs(t, errDocRegistered, ErrFailedToCheckIfDocIsRegisteredWithACP) + require.False(t, isDocRegistered) + + // No documents are registered right now so return false. + isDocRegistered, errDocRegistered = localACP.IsDocRegistered( + ctx, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errDocRegistered) + require.False(t, isDocRegistered) + + // Register a document. + errRegisterDoc = localACP.RegisterDocObject( + ctx, + identity1, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errRegisterDoc) + + // Now it should be registered. + isDocRegistered, errDocRegistered = localACP.IsDocRegistered( + ctx, + validPolicyID, + "users", + "documentID_XYZ", + ) + + require.Nil(t, errDocRegistered) + require.True(t, isDocRegistered) + + errClose := localACP.Close() + require.Nil(t, errClose) +} + +func Test_LocalACP_PersistentMemory_IsDocRegistered_TrueIfRegisteredFalseIfNotAndErrorOtherwise(t *testing.T) { + acpPath := t.TempDir() + require.NotEqual(t, "", acpPath) + + ctx := context.Background() + var localACP ACPLocal + + localACP.Init(ctx, acpPath) + errStart := localACP.Start(ctx) + require.Nil(t, errStart) + + policyID, errAddPolicy := localACP.AddPolicy( + ctx, + identity1, + validPolicy, + ) + require.Nil(t, errAddPolicy) + require.Equal( + t, + validPolicyID, + policyID, + ) + + // Invalid empty doc and empty resource can't be registered. + errRegisterDoc := localACP.RegisterDocObject( + ctx, + identity1, + validPolicyID, + "", + "", + ) + require.Error(t, errRegisterDoc) + require.ErrorIs(t, errRegisterDoc, ErrFailedToRegisterDocWithACP) + + // Check if an invalid empty doc and empty resource is registered. + isDocRegistered, errDocRegistered := localACP.IsDocRegistered( + ctx, + validPolicyID, + "", + "", + ) + require.Error(t, errDocRegistered) + require.ErrorIs(t, errDocRegistered, ErrFailedToCheckIfDocIsRegisteredWithACP) + require.False(t, isDocRegistered) + + // No documents are registered right now so return false. + isDocRegistered, errDocRegistered = localACP.IsDocRegistered( + ctx, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errDocRegistered) + require.False(t, isDocRegistered) + + // Register a document. + errRegisterDoc = localACP.RegisterDocObject( + ctx, + identity1, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errRegisterDoc) + + // Now it should be registered. + isDocRegistered, errDocRegistered = localACP.IsDocRegistered( + ctx, + validPolicyID, + "users", + "documentID_XYZ", + ) + + require.Nil(t, errDocRegistered) + require.True(t, isDocRegistered) + + // Should stay registered even after a restart. + errClose := localACP.Close() + require.Nil(t, errClose) + + localACP.Init(ctx, acpPath) + errStart = localACP.Start(ctx) + require.Nil(t, errStart) + + // Check after restart if it is still registered. + isDocRegistered, errDocRegistered = localACP.IsDocRegistered( + ctx, + validPolicyID, + "users", + "documentID_XYZ", + ) + + require.Nil(t, errDocRegistered) + require.True(t, isDocRegistered) + + errClose = localACP.Close() + require.Nil(t, errClose) +} + +func Test_LocalACP_InMemory_CheckDocAccess_TrueIfHaveAccessFalseIfNotErrorOtherwise(t *testing.T) { + ctx := context.Background() + var localACP ACPLocal + + localACP.Init(ctx, "") + errStart := localACP.Start(ctx) + require.Nil(t, errStart) + + policyID, errAddPolicy := localACP.AddPolicy( + ctx, + identity1, + validPolicy, + ) + require.Nil(t, errAddPolicy) + require.Equal( + t, + validPolicyID, + policyID, + ) + + // Invalid empty arguments such that we can't check doc access. + hasAccess, errCheckDocAccess := localACP.CheckDocAccess( + ctx, + ReadPermission, + identity1, + validPolicyID, + "", + "", + ) + require.Error(t, errCheckDocAccess) + require.ErrorIs(t, errCheckDocAccess, ErrFailedToVerifyDocAccessWithACP) + require.False(t, hasAccess) + + // Check document accesss for a document that does not exist. + hasAccess, errCheckDocAccess = localACP.CheckDocAccess( + ctx, + ReadPermission, + identity1, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errCheckDocAccess) + require.False(t, hasAccess) + + // Register a document. + errRegisterDoc := localACP.RegisterDocObject( + ctx, + identity1, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errRegisterDoc) + + // Now check using correct identity if it has access. + hasAccess, errCheckDocAccess = localACP.CheckDocAccess( + ctx, + ReadPermission, + identity1, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errCheckDocAccess) + require.True(t, hasAccess) + + // Now check using wrong identity, it should not have access. + hasAccess, errCheckDocAccess = localACP.CheckDocAccess( + ctx, + ReadPermission, + identity2, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errCheckDocAccess) + require.False(t, hasAccess) + + errClose := localACP.Close() + require.Nil(t, errClose) +} + +func Test_LocalACP_PersistentMemory_CheckDocAccess_TrueIfHaveAccessFalseIfNotErrorOtherwise(t *testing.T) { + acpPath := t.TempDir() + require.NotEqual(t, "", acpPath) + + ctx := context.Background() + var localACP ACPLocal + + localACP.Init(ctx, acpPath) + errStart := localACP.Start(ctx) + require.Nil(t, errStart) + + policyID, errAddPolicy := localACP.AddPolicy( + ctx, + identity1, + validPolicy, + ) + require.Nil(t, errAddPolicy) + require.Equal( + t, + validPolicyID, + policyID, + ) + + // Invalid empty arguments such that we can't check doc access. + hasAccess, errCheckDocAccess := localACP.CheckDocAccess( + ctx, + ReadPermission, + identity1, + validPolicyID, + "", + "", + ) + require.Error(t, errCheckDocAccess) + require.ErrorIs(t, errCheckDocAccess, ErrFailedToVerifyDocAccessWithACP) + require.False(t, hasAccess) + + // Check document accesss for a document that does not exist. + hasAccess, errCheckDocAccess = localACP.CheckDocAccess( + ctx, + ReadPermission, + identity1, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errCheckDocAccess) + require.False(t, hasAccess) + + // Register a document. + errRegisterDoc := localACP.RegisterDocObject( + ctx, + identity1, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errRegisterDoc) + + // Now check using correct identity if it has access. + hasAccess, errCheckDocAccess = localACP.CheckDocAccess( + ctx, + ReadPermission, + identity1, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errCheckDocAccess) + require.True(t, hasAccess) + + // Now check using wrong identity, it should not have access. + hasAccess, errCheckDocAccess = localACP.CheckDocAccess( + ctx, + ReadPermission, + identity2, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errCheckDocAccess) + require.False(t, hasAccess) + + // identities should continue having their correct behaviour and access even after a restart. + errClose := localACP.Close() + require.Nil(t, errClose) + + localACP.Init(ctx, acpPath) + errStart = localACP.Start(ctx) + require.Nil(t, errStart) + + // Now check again after the restart using correct identity if it still has access. + hasAccess, errCheckDocAccess = localACP.CheckDocAccess( + ctx, + ReadPermission, + identity1, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errCheckDocAccess) + require.True(t, hasAccess) + + // Now check again after restart using wrong identity, it should continue to not have access. + hasAccess, errCheckDocAccess = localACP.CheckDocAccess( + ctx, + ReadPermission, + identity2, + validPolicyID, + "users", + "documentID_XYZ", + ) + require.Nil(t, errCheckDocAccess) + require.False(t, hasAccess) + + errClose = localACP.Close() + require.Nil(t, errClose) +} diff --git a/acp/doc.go b/acp/doc.go new file mode 100644 index 0000000000..3fd60dd147 --- /dev/null +++ b/acp/doc.go @@ -0,0 +1,17 @@ +// 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 utilizes the sourcehub acp module to bring the functionality +to defradb, this package also helps avoid the leakage of direct sourcehub +references through out the code base, and eases in swapping between local +use case and a more global on sourcehub use case. +*/ +package acp diff --git a/acp/dpi.go b/acp/dpi.go new file mode 100644 index 0000000000..85da972131 --- /dev/null +++ b/acp/dpi.go @@ -0,0 +1,73 @@ +// 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 ( + "strings" +) + +type DPIPermission int + +// Valid DefraDB Policy Interface Permission Type. +const ( + ReadPermission DPIPermission = iota + WritePermission +) + +// List of all valid DPI permissions, the order of permissions in this list must match +// the above defined ordering such that iota matches the index position within the list. +var dpiRequiredPermissions = []string{ + "read", + "write", +} + +func (dpiPermission DPIPermission) String() string { + return dpiRequiredPermissions[dpiPermission] +} + +const requiredRegistererRelationName string = "owner" + +// validateDPIExpressionOfRequiredPermission validates that the expression under the +// permission is valid. Moreover, DPI requires that for all required permissions, the +// expression start with "owner" then a space or symbol, and then follow-up expression. +// This is important because even if the policy has the required permissions under the +// resource, it's still possible that those permissions are not granted to the "owner" +// relation. This validation will help users not shoot themseleves in the foot. +// +// Learn more about the DefraDB Policy Interface [ACP](/acp/README.md), can find more +// detailed valid and invalid `expr` (expression) examples there. +func validateDPIExpressionOfRequiredPermission(expression string, requiredPermission string) error { + exprNoSpace := strings.ReplaceAll(expression, " ", "") + + if !strings.HasPrefix(exprNoSpace, requiredRegistererRelationName) { + return newErrExprOfRequiredPermissionMustStartWithRelation( + requiredPermission, + requiredRegistererRelationName, + ) + } + + restOfTheExpr := exprNoSpace[len(requiredRegistererRelationName):] + if len(restOfTheExpr) != 0 { + c := restOfTheExpr[0] + // First non-space character after the required relation name MUST be a `+`. + // The reason we are enforcing this here is because other set operations are + // not applied to the registerer relation anyways. + if c != '+' { + return newErrExprOfRequiredPermissionHasInvalidChar( + requiredPermission, + requiredRegistererRelationName, + c, + ) + } + } + + return nil +} diff --git a/acp/errors.go b/acp/errors.go new file mode 100644 index 0000000000..307b32f5ad --- /dev/null +++ b/acp/errors.go @@ -0,0 +1,207 @@ +// 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 ( + "github.com/sourcenetwork/defradb/errors" +) + +const ( + errInitializationOfACPFailed = "initialization of acp failed" + errStartingACPInEmptyPath = "starting acp in an empty path" + errFailedToAddPolicyWithACP = "failed to add policy with acp" + errFailedToRegisterDocWithACP = "failed to register document with acp" + errFailedToCheckIfDocIsRegisteredWithACP = "failed to check if doc is registered with acp" + errFailedToVerifyDocAccessWithACP = "failed to verify doc access with acp" + + errObjectDidNotRegister = "no-op while registering object (already exists or error) with acp" + errNoPolicyArgs = "missing policy arguments, must have both id and resource" + + errPolicyIDMustNotBeEmpty = "policyID must not be empty" + errPolicyDoesNotExistWithACP = "policyID specified does not exist with acp" + errPolicyValidationFailedWithACP = "policyID validation through acp failed" + + errResourceNameMustNotBeEmpty = "resource name must not be empty" + errResourceDoesNotExistOnTargetPolicy = "resource does not exist on the specified policy" + errResourceIsMissingRequiredPermission = "resource is missing required permission on policy" + + errExprOfRequiredPermMustStartWithRelation = "expr of required permission must start with required relation" + errExprOfRequiredPermHasInvalidChar = "expr of required permission has invalid character after relation" +) + +var ( + ErrInitializationOfACPFailed = errors.New(errInitializationOfACPFailed) + ErrFailedToAddPolicyWithACP = errors.New(errFailedToAddPolicyWithACP) + ErrFailedToRegisterDocWithACP = errors.New(errFailedToRegisterDocWithACP) + ErrFailedToCheckIfDocIsRegisteredWithACP = errors.New(errFailedToCheckIfDocIsRegisteredWithACP) + ErrFailedToVerifyDocAccessWithACP = errors.New(errFailedToVerifyDocAccessWithACP) + ErrPolicyDoesNotExistWithACP = errors.New(errPolicyDoesNotExistWithACP) + + ErrResourceDoesNotExistOnTargetPolicy = errors.New(errResourceDoesNotExistOnTargetPolicy) + + ErrPolicyDataMustNotBeEmpty = errors.New("policy data can not be empty") + ErrPolicyCreatorMustNotBeEmpty = errors.New("policy creator can not be empty") + ErrObjectDidNotRegister = errors.New(errObjectDidNotRegister) + ErrNoPolicyArgs = errors.New(errNoPolicyArgs) + ErrPolicyIDMustNotBeEmpty = errors.New(errPolicyIDMustNotBeEmpty) + ErrResourceNameMustNotBeEmpty = errors.New(errResourceNameMustNotBeEmpty) +) + +func NewErrInitializationOfACPFailed( + inner error, + Type string, + path string, +) error { + return errors.Wrap( + errInitializationOfACPFailed, + inner, + errors.NewKV("Type", Type), + errors.NewKV("Path", path), + ) +} + +func NewErrFailedToAddPolicyWithACP( + inner error, + Type string, + creatorID string, +) error { + return errors.Wrap( + errFailedToAddPolicyWithACP, + inner, + errors.NewKV("Type", Type), + errors.NewKV("CreatorID", creatorID), + ) +} + +func NewErrFailedToRegisterDocWithACP( + inner error, + Type string, + policyID string, + creatorID string, + resourceName string, + docID string, +) error { + return errors.Wrap( + errFailedToRegisterDocWithACP, + inner, + errors.NewKV("Type", Type), + errors.NewKV("PolicyID", policyID), + errors.NewKV("CreatorID", creatorID), + errors.NewKV("ResourceName", resourceName), + errors.NewKV("DocID", docID), + ) +} + +func NewErrFailedToCheckIfDocIsRegisteredWithACP( + inner error, + Type string, + policyID string, + resourceName string, + docID string, +) error { + return errors.Wrap( + errFailedToCheckIfDocIsRegisteredWithACP, + inner, + errors.NewKV("Type", Type), + errors.NewKV("PolicyID", policyID), + errors.NewKV("ResourceName", resourceName), + errors.NewKV("DocID", docID), + ) +} + +func NewErrFailedToVerifyDocAccessWithACP( + inner error, + Type string, + policyID string, + actorID string, + resourceName string, + docID string, +) error { + return errors.Wrap( + errFailedToVerifyDocAccessWithACP, + inner, + errors.NewKV("Type", Type), + errors.NewKV("PolicyID", policyID), + errors.NewKV("ActorID", actorID), + errors.NewKV("ResourceName", resourceName), + errors.NewKV("DocID", docID), + ) +} + +func newErrPolicyDoesNotExistWithACP( + inner error, + policyID string, +) error { + return errors.Wrap( + errPolicyDoesNotExistWithACP, + inner, + errors.NewKV("PolicyID", policyID), + ) +} + +func newErrPolicyValidationFailedWithACP( + inner error, + policyID string, +) error { + return errors.Wrap( + errPolicyValidationFailedWithACP, + inner, + errors.NewKV("PolicyID", policyID), + ) +} + +func newErrResourceDoesNotExistOnTargetPolicy( + resourceName string, + policyID string, +) error { + return errors.New( + errResourceDoesNotExistOnTargetPolicy, + errors.NewKV("PolicyID", policyID), + errors.NewKV("ResourceName", resourceName), + ) +} + +func newErrResourceIsMissingRequiredPermission( + resourceName string, + permission string, + policyID string, +) error { + return errors.New( + errResourceIsMissingRequiredPermission, + errors.NewKV("PolicyID", policyID), + errors.NewKV("ResourceName", resourceName), + errors.NewKV("Permission", permission), + ) +} + +func newErrExprOfRequiredPermissionMustStartWithRelation( + permission string, + relation string, +) error { + return errors.New( + errExprOfRequiredPermMustStartWithRelation, + errors.NewKV("Permission", permission), + errors.NewKV("Relation", relation), + ) +} + +func newErrExprOfRequiredPermissionHasInvalidChar( + permission string, + relation string, + char byte, +) error { + return errors.New( + errExprOfRequiredPermHasInvalidChar, + errors.NewKV("Permission", permission), + errors.NewKV("Relation", relation), + errors.NewKV("Character", string(char)), + ) +} diff --git a/acp/identity/identity.go b/acp/identity/identity.go new file mode 100644 index 0000000000..ba6efb71fa --- /dev/null +++ b/acp/identity/identity.go @@ -0,0 +1,35 @@ +// 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 identity provides defradb identity. +*/ + +package identity + +import ( + "github.com/sourcenetwork/immutable" +) + +var ( + // NoIdentity is an empty identity. + NoIdentity = immutable.None[string]() +) + +// NewIdentity makes a new identity if the input is not empty otherwise, returns an empty Option. +func NewIdentity(identity string) immutable.Option[string] { + // TODO-ACP: There will be more validation once sourcehub gets some utilities. + // Then a validation function would do the validation, will likely do outside this function. + // https://github.com/sourcenetwork/defradb/issues/2358 + if identity == "" { + return NoIdentity + } + return immutable.Some[string](identity) +} diff --git a/cli/acp.go b/cli/acp.go new file mode 100644 index 0000000000..30705ac908 --- /dev/null +++ b/cli/acp.go @@ -0,0 +1,29 @@ +// 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 cli + +import ( + "github.com/spf13/cobra" +) + +func MakeACPCommand() *cobra.Command { + var cmd = &cobra.Command{ + Use: "acp", + Short: "Interact with the access control system of a DefraDB node", + Long: `Interact with the access control system of a DefraDB node + +Learn more about [ACP](/acp/README.md) + + `, + } + + return cmd +} diff --git a/cli/acp_policy.go b/cli/acp_policy.go new file mode 100644 index 0000000000..92ae9321f0 --- /dev/null +++ b/cli/acp_policy.go @@ -0,0 +1,25 @@ +// 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 cli + +import ( + "github.com/spf13/cobra" +) + +func MakeACPPolicyCommand() *cobra.Command { + var cmd = &cobra.Command{ + Use: "policy", + Short: "Interact with the acp policy features of DefraDB instance", + Long: `Interact with the acp policy features of DefraDB instance`, + } + + return cmd +} diff --git a/cli/acp_policy_add.go b/cli/acp_policy_add.go new file mode 100644 index 0000000000..01914b37c6 --- /dev/null +++ b/cli/acp_policy_add.go @@ -0,0 +1,139 @@ +// 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 cli + +import ( + "io" + "os" + + "github.com/spf13/cobra" + + "github.com/sourcenetwork/defradb/acp" +) + +func MakeACPPolicyAddCommand() *cobra.Command { + const identityFlagLongRequired string = "identity" + const identityFlagShortRequired string = "i" + + const fileFlagLong string = "file" + const fileFlagShort string = "f" + + var identityValue string + var policyFile string + + var cmd = &cobra.Command{ + Use: "add [-i --identity] [policy]", + Short: "Add new policy", + Long: `Add new policy + +Notes: + - Can not add a policy without specifying an identity. + - ACP must be available (i.e. ACP can not be disabled). + - A non-DPI policy will be accepted (will be registered with acp system). + - But only a valid DPI policyID & resource can be specified on a schema. + - DPI validation happens when attempting to add a schema with '@policy'. + - Learn more about [ACP & DPI Rules](/acp/README.md) + +Example: add from an argument string: + defradb client acp policy add -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j ' +description: A Valid DefraDB Policy Interface + +actor: + name: actor + +resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor +' + +Example: add from file: + defradb client acp policy add -i cosmos17r39df0hdcrgnmmw4mvu7qgk5nu888c7uvv37y -f policy.yml + +Example: add from file, verbose flags: + defradb client acp policy add --identity cosmos1kpw734v54g0t0d8tcye8ee5jc3gld0tcr2q473 --file policy.yml + +Example: add from stdin: + cat policy.yml | defradb client acp policy add - + +`, + RunE: func(cmd *cobra.Command, args []string) error { + if identityValue == "" { + return acp.ErrPolicyCreatorMustNotBeEmpty + } + + // TODO-ACP: Ensure here (before going through acp system) if the required identity argument + // is valid, if it is valid then keep proceeding further, otherwise return this error: + // `NewErrRequiredFlagInvalid(identityFlagLongRequired, identityFlagShortRequired)` + // Issue: https://github.com/sourcenetwork/defradb/issues/2358 + + // Handle policy argument. + extraArgsProvided := len(args) + var policy string + switch { + case policyFile != "": + data, err := os.ReadFile(policyFile) + if err != nil { + return err + } + policy = string(data) + + case extraArgsProvided > 0 && args[extraArgsProvided-1] == "-": + data, err := io.ReadAll(cmd.InOrStdin()) + if err != nil { + return err + } + policy = string(data) + + case extraArgsProvided > 0: + policy = args[0] + + default: + return ErrPolicyFileArgCanNotBeEmpty + } + + db := mustGetContextDB(cmd) + policyResult, err := db.AddPolicy( + cmd.Context(), + identityValue, + policy, + ) + + if err != nil { + return err + } + + return writeJSON(cmd, policyResult) + }, + } + cmd.Flags().StringVarP(&policyFile, fileFlagLong, fileFlagShort, "", "File to load a policy from") + cmd.Flags().StringVarP( + &identityValue, + identityFlagLongRequired, + identityFlagShortRequired, + "", + "[Required] Identity of the creator", + ) + _ = cmd.MarkFlagRequired(identityFlagLongRequired) + + return cmd +} diff --git a/cli/cli.go b/cli/cli.go index 33f58ac8e9..38209a9f69 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -62,6 +62,16 @@ func NewDefraCommand() *cobra.Command { schema_migrate, ) + policy := MakeACPPolicyCommand() + policy.AddCommand( + MakeACPPolicyAddCommand(), + ) + + acp := MakeACPCommand() + acp.AddCommand( + policy, + ) + view := MakeViewCommand() view.AddCommand( MakeViewAddCommand(), @@ -103,6 +113,7 @@ func NewDefraCommand() *cobra.Command { MakeDumpCommand(), MakeRequestCommand(), schema, + acp, view, index, p2p, diff --git a/cli/collection_create.go b/cli/collection_create.go index efeee61494..0af57d77ed 100644 --- a/cli/collection_create.go +++ b/cli/collection_create.go @@ -16,34 +16,41 @@ import ( "github.com/spf13/cobra" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" ) func MakeCollectionCreateCommand() *cobra.Command { + const identityFlagLongRequired string = "identity" + const identityFlagShortRequired string = "i" + + var identityValue string var file string + var cmd = &cobra.Command{ - Use: "create ", + Use: "create [-i --identity] ", Short: "Create a new document.", Long: `Create a new document. -Example: create from string +Example: create from string: defradb client collection create --name User '{ "name": "Bob" }' -Example: create multiple from string +Example: create from string, with identity: + defradb client collection create -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name User '{ "name": "Bob" }' + +Example: create multiple from string: defradb client collection create --name User '[{ "name": "Alice" }, { "name": "Bob" }]' -Example: create from file +Example: create from file: defradb client collection create --name User -f document.json -Example: create from stdin +Example: create from stdin: cat document.json | defradb client collection create --name User - `, Args: cobra.RangeArgs(0, 1), RunE: func(cmd *cobra.Command, args []string) error { - col, ok := tryGetContextCollection(cmd) - if !ok { - return cmd.Usage() - } + // TODO-ACP: `https://github.com/sourcenetwork/defradb/issues/2358` do the validation here. + identity := acpIdentity.NewIdentity(identityValue) var docData []byte switch { @@ -65,21 +72,33 @@ Example: create from stdin return ErrNoDocOrFile } + col, ok := tryGetContextCollection(cmd) + if !ok { + return cmd.Usage() + } + if client.IsJSONArray(docData) { docs, err := client.NewDocsFromJSON(docData, col.Schema()) if err != nil { return err } - return col.CreateMany(cmd.Context(), docs) + return col.CreateMany(cmd.Context(), identity, docs) } doc, err := client.NewDocFromJSON(docData, col.Schema()) if err != nil { return err } - return col.Create(cmd.Context(), doc) + return col.Create(cmd.Context(), identity, doc) }, } cmd.Flags().StringVarP(&file, "file", "f", "", "File containing document(s)") + cmd.Flags().StringVarP( + &identityValue, + identityFlagLongRequired, + identityFlagShortRequired, + "", + "Identity of the actor", + ) return cmd } diff --git a/cli/collection_delete.go b/cli/collection_delete.go index d1f945d9ae..1d1c128948 100644 --- a/cli/collection_delete.go +++ b/cli/collection_delete.go @@ -13,24 +13,35 @@ package cli import ( "github.com/spf13/cobra" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" ) func MakeCollectionDeleteCommand() *cobra.Command { + const identityFlagLongRequired string = "identity" + const identityFlagShortRequired string = "i" + + var identityValue string var argDocIDs []string var filter string var cmd = &cobra.Command{ - Use: "delete [--filter --docID ]", + Use: "delete [-i --identity] [--filter --docID ]", Short: "Delete documents by docID or filter.", Long: `Delete documents by docID or filter and lists the number of documents deleted. -Example: delete by docID(s) - defradb client collection delete --name User --docID bae-123,bae-456 +Example: delete by docID(s): + defradb client collection delete --name User --docID bae-123,bae-456 + +Example: delete by docID(s) with identity: + defradb client collection delete -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name User --docID bae-123,bae-456 -Example: delete by filter +Example: delete by filter: defradb client collection delete --name User --filter '{ "_gte": { "points": 100 } }' `, RunE: func(cmd *cobra.Command, args []string) error { + // TODO-ACP: `https://github.com/sourcenetwork/defradb/issues/2358` do the validation here. + identity := acpIdentity.NewIdentity(identityValue) + col, ok := tryGetContextCollection(cmd) if !ok { return cmd.Usage() @@ -42,7 +53,7 @@ Example: delete by filter if err != nil { return err } - res, err := col.DeleteWithDocID(cmd.Context(), docID) + res, err := col.DeleteWithDocID(cmd.Context(), identity, docID) if err != nil { return err } @@ -56,13 +67,13 @@ Example: delete by filter } docIDs[i] = docID } - res, err := col.DeleteWithDocIDs(cmd.Context(), docIDs) + res, err := col.DeleteWithDocIDs(cmd.Context(), identity, docIDs) if err != nil { return err } return writeJSON(cmd, res) case filter != "": - res, err := col.DeleteWithFilter(cmd.Context(), filter) + res, err := col.DeleteWithFilter(cmd.Context(), identity, filter) if err != nil { return err } @@ -74,5 +85,12 @@ Example: delete by filter } cmd.Flags().StringSliceVar(&argDocIDs, "docID", nil, "Document ID") cmd.Flags().StringVar(&filter, "filter", "", "Document filter") + cmd.Flags().StringVarP( + &identityValue, + identityFlagLongRequired, + identityFlagShortRequired, + "", + "Identity of the actor", + ) return cmd } diff --git a/cli/collection_get.go b/cli/collection_get.go index 55c84d6289..1a924ea1aa 100644 --- a/cli/collection_get.go +++ b/cli/collection_get.go @@ -13,21 +13,32 @@ package cli import ( "github.com/spf13/cobra" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" ) func MakeCollectionGetCommand() *cobra.Command { + const identityFlagLongRequired string = "identity" + const identityFlagShortRequired string = "i" + + var identityValue string var showDeleted bool var cmd = &cobra.Command{ - Use: "get [--show-deleted]", + Use: "get [-i --identity] [--show-deleted] ", Short: "View document fields.", Long: `View document fields. Example: defradb client collection get --name User bae-123 + +Example to get a private document we must use an identity: + defradb client collection get -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name User bae-123 `, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + // TODO-ACP: `https://github.com/sourcenetwork/defradb/issues/2358` do the validation here. + identity := acpIdentity.NewIdentity(identityValue) + col, ok := tryGetContextCollection(cmd) if !ok { return cmd.Usage() @@ -37,7 +48,7 @@ Example: if err != nil { return err } - doc, err := col.Get(cmd.Context(), docID, showDeleted) + doc, err := col.Get(cmd.Context(), identity, docID, showDeleted) if err != nil { return err } @@ -49,5 +60,12 @@ Example: }, } cmd.Flags().BoolVar(&showDeleted, "show-deleted", false, "Show deleted documents") + cmd.Flags().StringVarP( + &identityValue, + identityFlagLongRequired, + identityFlagShortRequired, + "", + "Identity of the actor", + ) return cmd } diff --git a/cli/collection_list_doc_ids.go b/cli/collection_list_doc_ids.go index 7112a88817..10f6d879bf 100644 --- a/cli/collection_list_doc_ids.go +++ b/cli/collection_list_doc_ids.go @@ -13,25 +13,37 @@ package cli import ( "github.com/spf13/cobra" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/http" ) func MakeCollectionListDocIDsCommand() *cobra.Command { + const identityFlagLongRequired string = "identity" + const identityFlagShortRequired string = "i" + + var identityValue string + var cmd = &cobra.Command{ - Use: "docIDs", + Use: "docIDs [-i --identity]", Short: "List all document IDs (docIDs).", Long: `List all document IDs (docIDs). -Example: +Example: list all docID(s): defradb client collection docIDs --name User + +Example: list all docID(s), with an identity: + defradb client collection docIDs -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name User `, RunE: func(cmd *cobra.Command, args []string) error { + // TODO-ACP: `https://github.com/sourcenetwork/defradb/issues/2358` do the validation here. + identity := acpIdentity.NewIdentity(identityValue) + col, ok := tryGetContextCollection(cmd) if !ok { return cmd.Usage() } - docCh, err := col.GetAllDocIDs(cmd.Context()) + docCh, err := col.GetAllDocIDs(cmd.Context(), identity) if err != nil { return err } @@ -49,5 +61,12 @@ Example: return nil }, } + cmd.Flags().StringVarP( + &identityValue, + identityFlagLongRequired, + identityFlagShortRequired, + "", + "Identity of the actor", + ) return cmd } diff --git a/cli/collection_update.go b/cli/collection_update.go index 42354948a9..816cad8029 100644 --- a/cli/collection_update.go +++ b/cli/collection_update.go @@ -13,31 +13,43 @@ package cli import ( "github.com/spf13/cobra" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" ) func MakeCollectionUpdateCommand() *cobra.Command { + const identityFlagLongRequired string = "identity" + const identityFlagShortRequired string = "i" + + var identityValue string var argDocIDs []string var filter string var updater string var cmd = &cobra.Command{ - Use: "update [--filter --docID --updater ] ", + Use: "update [-i --identity] [--filter --docID --updater ] ", Short: "Update documents by docID or filter.", Long: `Update documents by docID or filter. -Example: update from string +Example: update from string: defradb client collection update --name User --docID bae-123 '{ "name": "Bob" }' -Example: update by filter +Example: update by filter: defradb client collection update --name User \ --filter '{ "_gte": { "points": 100 } }' --updater '{ "verified": true }' -Example: update by docIDs +Example: update by docIDs: defradb client collection update --name User \ + --docID bae-123,bae-456 --updater '{ "verified": true }' + +Example: update private docIDs, with identity: + defradb client collection update -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name User \ --docID bae-123,bae-456 --updater '{ "verified": true }' `, Args: cobra.RangeArgs(0, 1), RunE: func(cmd *cobra.Command, args []string) error { + // TODO-ACP: `https://github.com/sourcenetwork/defradb/issues/2358` do the validation here. + identity := acpIdentity.NewIdentity(identityValue) + col, ok := tryGetContextCollection(cmd) if !ok { return cmd.Usage() @@ -49,7 +61,7 @@ Example: update by docIDs if err != nil { return err } - res, err := col.UpdateWithDocID(cmd.Context(), docID, updater) + res, err := col.UpdateWithDocID(cmd.Context(), identity, docID, updater) if err != nil { return err } @@ -63,13 +75,13 @@ Example: update by docIDs } docIDs[i] = docID } - res, err := col.UpdateWithDocIDs(cmd.Context(), docIDs, updater) + res, err := col.UpdateWithDocIDs(cmd.Context(), identity, docIDs, updater) if err != nil { return err } return writeJSON(cmd, res) case filter != "" && updater != "": - res, err := col.UpdateWithFilter(cmd.Context(), filter, updater) + res, err := col.UpdateWithFilter(cmd.Context(), identity, filter, updater) if err != nil { return err } @@ -79,14 +91,14 @@ Example: update by docIDs if err != nil { return err } - doc, err := col.Get(cmd.Context(), docID, true) + doc, err := col.Get(cmd.Context(), identity, docID, true) if err != nil { return err } if err := doc.SetWithJSON([]byte(args[0])); err != nil { return err } - return col.Update(cmd.Context(), doc) + return col.Update(cmd.Context(), identity, doc) default: return ErrNoDocIDOrFilter } @@ -95,5 +107,12 @@ Example: update by docIDs cmd.Flags().StringSliceVar(&argDocIDs, "docID", nil, "Document ID") cmd.Flags().StringVar(&filter, "filter", "", "Document filter") cmd.Flags().StringVar(&updater, "updater", "", "Document updater") + cmd.Flags().StringVarP( + &identityValue, + identityFlagLongRequired, + identityFlagShortRequired, + "", + "Identity of the actor", + ) return cmd } diff --git a/cli/dump.go b/cli/dump.go index a3d155605b..76b36bab99 100644 --- a/cli/dump.go +++ b/cli/dump.go @@ -12,8 +12,6 @@ package cli import ( "github.com/spf13/cobra" - - "github.com/sourcenetwork/defradb/client" ) func MakeDumpCommand() *cobra.Command { @@ -21,7 +19,7 @@ func MakeDumpCommand() *cobra.Command { Use: "dump", Short: "Dump the contents of DefraDB node-side", RunE: func(cmd *cobra.Command, _ []string) (err error) { - db := cmd.Context().Value(dbContextKey).(client.DB) + db := mustGetContextDB(cmd) return db.PrintDump(cmd.Context()) }, } diff --git a/cli/errors.go b/cli/errors.go index bb124bc7f9..02cd252b59 100644 --- a/cli/errors.go +++ b/cli/errors.go @@ -11,25 +11,37 @@ package cli import ( + "fmt" + "github.com/sourcenetwork/defradb/errors" ) const ( errInvalidLensConfig string = "invalid lens configuration" errSchemaVersionNotOfSchema string = "the given schema version is from a different schema" + errRequiredFlag string = "the required flag [--%s|-%s] is %s" ) var ( - ErrNoDocOrFile = errors.New("document or file must be defined") - ErrInvalidDocument = errors.New("invalid document") - ErrNoDocIDOrFilter = errors.New("docID or filter must be defined") - ErrInvalidExportFormat = errors.New("invalid export format") - ErrNoLensConfig = errors.New("lens config cannot be empty") - ErrInvalidLensConfig = errors.New("invalid lens configuration") - ErrSchemaVersionNotOfSchema = errors.New(errSchemaVersionNotOfSchema) - ErrViewAddMissingArgs = errors.New("please provide a base query and output SDL for this view") + ErrNoDocOrFile = errors.New("document or file must be defined") + ErrInvalidDocument = errors.New("invalid document") + ErrNoDocIDOrFilter = errors.New("docID or filter must be defined") + ErrInvalidExportFormat = errors.New("invalid export format") + ErrNoLensConfig = errors.New("lens config cannot be empty") + ErrInvalidLensConfig = errors.New("invalid lens configuration") + ErrSchemaVersionNotOfSchema = errors.New(errSchemaVersionNotOfSchema) + ErrViewAddMissingArgs = errors.New("please provide a base query and output SDL for this view") + ErrPolicyFileArgCanNotBeEmpty = errors.New("policy file argument can not be empty") ) +func NewErrRequiredFlagEmpty(longName string, shortName string) error { + return errors.New(fmt.Sprintf(errRequiredFlag, longName, shortName, "empty")) +} + +func NewErrRequiredFlagInvalid(longName string, shortName string) error { + return errors.New(fmt.Sprintf(errRequiredFlag, longName, shortName, "invalid")) +} + func NewErrInvalidLensConfig(inner error) error { return errors.Wrap(errInvalidLensConfig, inner) } diff --git a/cli/request.go b/cli/request.go index d5e37e79a3..c583d51a28 100644 --- a/cli/request.go +++ b/cli/request.go @@ -16,6 +16,7 @@ import ( "github.com/spf13/cobra" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/errors" ) @@ -25,9 +26,13 @@ const ( ) func MakeRequestCommand() *cobra.Command { + const identityFlagLongRequired string = "identity" + const identityFlagShortRequired string = "i" + + var identityValue string var filePath string var cmd = &cobra.Command{ - Use: "query [query request]", + Use: "query [-i --identity] [request]", Short: "Send a DefraDB GraphQL query request", Long: `Send a DefraDB GraphQL query request to the database. @@ -37,6 +42,9 @@ A query request can be sent as a single argument. Example command: Do a query request from a file by using the '-f' flag. Example command: defradb client query -f request.graphql +Do a query request from a file and with an identity. Example command: + defradb client query -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j -f request.graphql + Or it can be sent via stdin by using the '-' special syntax. Example command: cat request.graphql | defradb client query - @@ -45,7 +53,8 @@ with the database more conveniently. To learn more about the DefraDB GraphQL Query Language, refer to https://docs.source.network.`, RunE: func(cmd *cobra.Command, args []string) error { - store := mustGetContextStore(cmd) + // TODO-ACP: `https://github.com/sourcenetwork/defradb/issues/2358` do the validation here. + identity := acpIdentity.NewIdentity(identityValue) var request string switch { @@ -68,7 +77,9 @@ To learn more about the DefraDB GraphQL Query Language, refer to https://docs.so if request == "" { return errors.New("request cannot be empty") } - result := store.ExecRequest(cmd.Context(), request) + + store := mustGetContextStore(cmd) + result := store.ExecRequest(cmd.Context(), identity, request) var errors []string for _, err := range result.GQL.Errors { @@ -87,5 +98,12 @@ To learn more about the DefraDB GraphQL Query Language, refer to https://docs.so } cmd.Flags().StringVarP(&filePath, "file", "f", "", "File containing the query request") + cmd.Flags().StringVarP( + &identityValue, + identityFlagLongRequired, + identityFlagShortRequired, + "", + "Identity of the actor", + ) return cmd } diff --git a/cli/schema_add.go b/cli/schema_add.go index f987d062df..e81896322d 100644 --- a/cli/schema_add.go +++ b/cli/schema_add.go @@ -25,6 +25,11 @@ func MakeSchemaAddCommand() *cobra.Command { Short: "Add new schema", Long: `Add new schema. +Schema Object with a '@policy(id:".." resource: "..")' linked will only be accepted if: + - ACP is available (i.e. ACP is not disabled). + - The specified resource adheres to the Document Access Control DPI Rules. + - Learn more about [ACP & DPI Rules](/acp/README.md) + Example: add from an argument string: defradb client schema add 'type Foo { ... }' diff --git a/cli/start.go b/cli/start.go index 90ca08d77a..ca9267e7e9 100644 --- a/cli/start.go +++ b/cli/start.go @@ -50,6 +50,10 @@ func MakeStartCommand() *cobra.Command { dbOpts := []db.Option{ db.WithUpdateEvents(), db.WithMaxRetries(cfg.GetInt("datastore.MaxTxnRetries")), + // TODO-ACP: Infuture when we add support for the --no-acp flag when admin signatures are in, + // we can allow starting of db without acp. Currently that can only be done programmatically. + // https://github.com/sourcenetwork/defradb/issues/2271 + db.WithACPInMemory(), } netOpts := []net.NodeOpt{ @@ -84,12 +88,17 @@ func MakeStartCommand() *cobra.Command { // Running with memory store mode will always generate a random key. // Adding support for an ephemeral mode and moving the key to the // config would solve both of these issues. - rootdir := mustGetContextRootDir(cmd) - key, err := loadOrGeneratePrivateKey(filepath.Join(rootdir, "data", "key")) + rootDir := mustGetContextRootDir(cmd) + key, err := loadOrGeneratePrivateKey(filepath.Join(rootDir, "data", "key")) if err != nil { return err } netOpts = append(netOpts, net.WithPrivateKey(key)) + + // TODO-ACP: Infuture when we add support for the --no-acp flag when admin signatures are in, + // we can allow starting of db without acp. Currently that can only be done programmatically. + // https://github.com/sourcenetwork/defradb/issues/2271 + dbOpts = append(dbOpts, db.WithACP(rootDir)) } opts := []node.NodeOpt{ diff --git a/cli/tx_create.go b/cli/tx_create.go index da239b6943..5190ba20f7 100644 --- a/cli/tx_create.go +++ b/cli/tx_create.go @@ -13,7 +13,6 @@ package cli import ( "github.com/spf13/cobra" - "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/datastore" ) @@ -25,7 +24,7 @@ func MakeTxCreateCommand() *cobra.Command { Short: "Create a new DefraDB transaction.", Long: `Create a new DefraDB transaction.`, RunE: func(cmd *cobra.Command, args []string) (err error) { - db := cmd.Context().Value(dbContextKey).(client.DB) + db := mustGetContextDB(cmd) var tx datastore.Txn if concurrent { diff --git a/cli/utils.go b/cli/utils.go index caeb282606..f923021fcf 100644 --- a/cli/utils.go +++ b/cli/utils.go @@ -50,6 +50,13 @@ var ( colContextKey = contextKey("col") ) +// mustGetContextDB returns the db for the current command context. +// +// If a db is not set in the current context this function panics. +func mustGetContextDB(cmd *cobra.Command) client.DB { + return cmd.Context().Value(dbContextKey).(client.DB) +} + // mustGetContextStore returns the store for the current command context. // // If a store is not set in the current context this function panics. diff --git a/client/collection.go b/client/collection.go index 58b53c3af0..aa219b3a74 100644 --- a/client/collection.go +++ b/client/collection.go @@ -46,12 +46,12 @@ type Collection interface { // Create a new document. // // Will verify the DocID/CID to ensure that the new document is correctly formatted. - Create(context.Context, *Document) error + Create(ctx context.Context, identity immutable.Option[string], doc *Document) error // CreateMany new documents. // // Will verify the DocIDs/CIDs to ensure that the new documents are correctly formatted. - CreateMany(context.Context, []*Document) error + CreateMany(ctx context.Context, identity immutable.Option[string], docs []*Document) error // Update an existing document with the new values. // @@ -59,25 +59,26 @@ type Collection interface { // Any field that is nil/empty that hasn't called Clear will be ignored. // // Will return a ErrDocumentNotFound error if the given document is not found. - Update(context.Context, *Document) error + Update(ctx context.Context, identity immutable.Option[string], docs *Document) error // Save the given document in the database. // // If a document exists with the given DocID it will update it. Otherwise a new document // will be created. - Save(context.Context, *Document) error + Save(ctx context.Context, identity immutable.Option[string], doc *Document) error // Delete will attempt to delete a document by DocID. // // Will return true if a deletion is successful, and return false along with an error // if it cannot. If the document doesn't exist, then it will return false and a ErrDocumentNotFound error. - // This operation will hard-delete all state relating to the given DocID. This includes data, block, and head storage. - Delete(context.Context, DocID) (bool, error) + // This operation will hard-delete all state relating to the given DocID. + // This includes data, block, and head storage. + Delete(ctx context.Context, identity immutable.Option[string], docID DocID) (bool, error) // Exists checks if a given document exists with supplied DocID. // // Will return true if a matching document exists, otherwise will return false. - Exists(context.Context, DocID) (bool, error) + Exists(ctx context.Context, identity immutable.Option[string], docID DocID) (bool, error) // UpdateWith updates a target document using the given updater type. // @@ -88,13 +89,23 @@ type Collection interface { // // Returns an ErrInvalidUpdateTarget error if the target type is not supported. // Returns an ErrInvalidUpdater error if the updater type is not supported. - UpdateWith(ctx context.Context, target any, updater string) (*UpdateResult, error) + UpdateWith( + ctx context.Context, + identity immutable.Option[string], + target any, + updater string, + ) (*UpdateResult, error) // UpdateWithFilter updates using a filter to target documents for update. // // The provided updater must be a string Patch, string Merge Patch, a parsed Patch, or parsed Merge Patch // else an ErrInvalidUpdater will be returned. - UpdateWithFilter(ctx context.Context, filter any, updater string) (*UpdateResult, error) + UpdateWithFilter( + ctx context.Context, + identity immutable.Option[string], + filter any, + updater string, + ) (*UpdateResult, error) // UpdateWithDocID updates using a DocID to target a single document for update. // @@ -102,7 +113,12 @@ type Collection interface { // else an ErrInvalidUpdater will be returned. // // Returns an ErrDocumentNotFound if a document matching the given DocID is not found. - UpdateWithDocID(ctx context.Context, docID DocID, updater string) (*UpdateResult, error) + UpdateWithDocID( + ctx context.Context, + identity immutable.Option[string], + docID DocID, + updater string, + ) (*UpdateResult, error) // UpdateWithDocIDs updates documents matching the given DocIDs. // @@ -110,7 +126,12 @@ type Collection interface { // else an ErrInvalidUpdater will be returned. // // Returns an ErrDocumentNotFound if a document is not found for any given DocID. - UpdateWithDocIDs(context.Context, []DocID, string) (*UpdateResult, error) + UpdateWithDocIDs( + ctx context.Context, + identity immutable.Option[string], + docIDs []DocID, + updater string, + ) (*UpdateResult, error) // DeleteWith deletes a target document. // @@ -121,13 +142,21 @@ type Collection interface { // with a status of `Deleted`. // // Returns an ErrInvalidDeleteTarget if the target type is not supported. - DeleteWith(ctx context.Context, target any) (*DeleteResult, error) + DeleteWith( + ctx context.Context, + identity immutable.Option[string], + target any, + ) (*DeleteResult, error) // DeleteWithFilter deletes documents matching the given filter. // // This operation will soft-delete documents related to the given filter and update the composite block // with a status of `Deleted`. - DeleteWithFilter(ctx context.Context, filter any) (*DeleteResult, error) + DeleteWithFilter( + ctx context.Context, + identity immutable.Option[string], + filter any, + ) (*DeleteResult, error) // DeleteWithDocID deletes using a DocID to target a single document for delete. // @@ -135,7 +164,11 @@ type Collection interface { // with a status of `Deleted`. // // Returns an ErrDocumentNotFound if a document matching the given DocID is not found. - DeleteWithDocID(context.Context, DocID) (*DeleteResult, error) + DeleteWithDocID( + ctx context.Context, + identity immutable.Option[string], + docID DocID, + ) (*DeleteResult, error) // DeleteWithDocIDs deletes documents matching the given DocIDs. // @@ -143,25 +176,35 @@ type Collection interface { // with a status of `Deleted`. // // Returns an ErrDocumentNotFound if a document is not found for any given DocID. - DeleteWithDocIDs(context.Context, []DocID) (*DeleteResult, error) + DeleteWithDocIDs( + ctx context.Context, + identity immutable.Option[string], + docIDs []DocID, + ) (*DeleteResult, error) // Get returns the document with the given DocID. // // Returns an ErrDocumentNotFound if a document matching the given DocID is not found. - Get(ctx context.Context, docID DocID, showDeleted bool) (*Document, error) + Get( + ctx context.Context, + identity immutable.Option[string], + docID DocID, + showDeleted bool, + ) (*Document, error) // WithTxn returns a new instance of the collection, with a transaction // handle instead of a raw DB handle. WithTxn(datastore.Txn) Collection // GetAllDocIDs returns all the document IDs that exist in the collection. - GetAllDocIDs(ctx context.Context) (<-chan DocIDResult, error) + GetAllDocIDs(ctx context.Context, identity immutable.Option[string]) (<-chan DocIDResult, error) // CreateIndex creates a new index on the collection. // `IndexDescription` contains the description of the index to be created. // `IndexDescription.Name` must start with a letter or an underscore and can // only contain letters, numbers, and underscores. // If the name of the index is not provided, it will be generated. + // WARNING: This method can not create index for a collection that has a policy. CreateIndex(context.Context, IndexDescription) (IndexDescription, error) // DropIndex drops an index from the collection. diff --git a/client/collection_description.go b/client/collection_description.go index 2e3e10aa36..2db34ddb8b 100644 --- a/client/collection_description.go +++ b/client/collection_description.go @@ -65,6 +65,16 @@ type CollectionDescription struct { // Indexes contains the secondary indexes that this Collection has. Indexes []IndexDescription + + // Policy contains the policy information on this collection. + // + // It is possible for a collection to not have a policy, a collection + // without a policy has no access control. + // + // Note: The policy information must be validated using acp right after + // parsing is done, to avoid storing an invalid policyID or policy resource + // that may not even exist on acp. + Policy immutable.Option[PolicyDescription] } // QuerySource represents a collection data source from a query. @@ -166,6 +176,7 @@ type collectionDescription struct { ID uint32 RootID uint32 SchemaVersionID string + Policy immutable.Option[PolicyDescription] Indexes []IndexDescription Fields []CollectionFieldDescription @@ -187,6 +198,7 @@ func (c *CollectionDescription) UnmarshalJSON(bytes []byte) error { c.Indexes = descMap.Indexes c.Fields = descMap.Fields c.Sources = make([]any, len(descMap.Sources)) + c.Policy = descMap.Policy for i, source := range descMap.Sources { sourceJson, err := json.Marshal(source) diff --git a/client/db.go b/client/db.go index 660c03998f..a5d855f137 100644 --- a/client/db.go +++ b/client/db.go @@ -85,6 +85,18 @@ type DB interface { // // It is likely unwise to call this on a large database instance. PrintDump(ctx context.Context) error + + // AddPolicy adds policy to acp, if acp is available. + // + // If policy was successfully added to acp then a policyID is returned, + // otherwise if acp was not available then returns the following error: + // [client.ErrPolicyAddFailureNoACP] + // + // Detects the format of the policy automatically by assuming YAML format if JSON + // validation fails. + // + // Note: A policy can not be added without the creatorID (identity). + AddPolicy(ctx context.Context, creatorID string, policy string) (AddPolicyResult, error) } // Store contains the core DefraDB read-write operations. @@ -226,8 +238,12 @@ type Store interface { // GetAllIndexes returns all the indexes that currently exist within this [Store]. GetAllIndexes(context.Context) (map[CollectionName][]IndexDescription, error) - // ExecRequest executes the given GQL request against the [Store]. - ExecRequest(context.Context, string) *RequestResult + // ExecRequest executes the given GQL request against the [Store], with the given identity. + ExecRequest( + ctx context.Context, + identity immutable.Option[string], + request string, + ) *RequestResult } // GQLResult represents the immediate results of a GQL request. diff --git a/client/errors.go b/client/errors.go index 7e18e9566c..dbc29ed78b 100644 --- a/client/errors.go +++ b/client/errors.go @@ -48,7 +48,8 @@ var ( ErrOperationNotPermittedOnNamelessCols = errors.New(errOperationNotPermittedOnNamelessCols) ErrFieldNotObject = errors.New("trying to access field on a non object type") ErrValueTypeMismatch = errors.New("value does not match indicated type") - ErrDocumentNotFound = errors.New("no document for the given ID exists") + ErrDocumentNotFoundOrNotAuthorized = errors.New("document not found or not authorized to access") + ErrPolicyAddFailureNoACP = errors.New("failure adding policy because ACP was not available") ErrInvalidUpdateTarget = errors.New("the target document to update is of invalid type") ErrInvalidUpdater = errors.New("the updater of a document is of invalid type") ErrInvalidDeleteTarget = errors.New("the target document to delete is of invalid type") diff --git a/client/mocks/collection.go b/client/mocks/collection.go index 6e6c7afae3..397bac7d1b 100644 --- a/client/mocks/collection.go +++ b/client/mocks/collection.go @@ -27,13 +27,13 @@ func (_m *Collection) EXPECT() *Collection_Expecter { return &Collection_Expecter{mock: &_m.Mock} } -// Create provides a mock function with given fields: _a0, _a1 -func (_m *Collection) Create(_a0 context.Context, _a1 *client.Document) error { - ret := _m.Called(_a0, _a1) +// Create provides a mock function with given fields: ctx, identity, doc +func (_m *Collection) Create(ctx context.Context, identity immutable.Option[string], doc *client.Document) error { + ret := _m.Called(ctx, identity, doc) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *client.Document) error); ok { - r0 = rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], *client.Document) error); ok { + r0 = rf(ctx, identity, doc) } else { r0 = ret.Error(0) } @@ -47,25 +47,69 @@ type Collection_Create_Call struct { } // Create is a helper method to define mock.On call +// - ctx context.Context +// - identity immutable.Option[string] +// - doc *client.Document +func (_e *Collection_Expecter) Create(ctx interface{}, identity interface{}, doc interface{}) *Collection_Create_Call { + return &Collection_Create_Call{Call: _e.mock.On("Create", ctx, identity, doc)} +} + +func (_c *Collection_Create_Call) Run(run func(ctx context.Context, identity immutable.Option[string], doc *client.Document)) *Collection_Create_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(*client.Document)) + }) + return _c +} + +func (_c *Collection_Create_Call) Return(_a0 error) *Collection_Create_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Collection_Create_Call) RunAndReturn(run func(context.Context, immutable.Option[string], *client.Document) error) *Collection_Create_Call { + _c.Call.Return(run) + return _c +} + +// CreateDocIndex provides a mock function with given fields: _a0, _a1 +func (_m *Collection) CreateDocIndex(_a0 context.Context, _a1 *client.Document) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *client.Document) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Collection_CreateDocIndex_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateDocIndex' +type Collection_CreateDocIndex_Call struct { + *mock.Call +} + +// CreateDocIndex is a helper method to define mock.On call // - _a0 context.Context // - _a1 *client.Document -func (_e *Collection_Expecter) Create(_a0 interface{}, _a1 interface{}) *Collection_Create_Call { - return &Collection_Create_Call{Call: _e.mock.On("Create", _a0, _a1)} +func (_e *Collection_Expecter) CreateDocIndex(_a0 interface{}, _a1 interface{}) *Collection_CreateDocIndex_Call { + return &Collection_CreateDocIndex_Call{Call: _e.mock.On("CreateDocIndex", _a0, _a1)} } -func (_c *Collection_Create_Call) Run(run func(_a0 context.Context, _a1 *client.Document)) *Collection_Create_Call { +func (_c *Collection_CreateDocIndex_Call) Run(run func(_a0 context.Context, _a1 *client.Document)) *Collection_CreateDocIndex_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(*client.Document)) }) return _c } -func (_c *Collection_Create_Call) Return(_a0 error) *Collection_Create_Call { +func (_c *Collection_CreateDocIndex_Call) Return(_a0 error) *Collection_CreateDocIndex_Call { _c.Call.Return(_a0) return _c } -func (_c *Collection_Create_Call) RunAndReturn(run func(context.Context, *client.Document) error) *Collection_Create_Call { +func (_c *Collection_CreateDocIndex_Call) RunAndReturn(run func(context.Context, *client.Document) error) *Collection_CreateDocIndex_Call { _c.Call.Return(run) return _c } @@ -123,13 +167,13 @@ func (_c *Collection_CreateIndex_Call) RunAndReturn(run func(context.Context, cl return _c } -// CreateMany provides a mock function with given fields: _a0, _a1 -func (_m *Collection) CreateMany(_a0 context.Context, _a1 []*client.Document) error { - ret := _m.Called(_a0, _a1) +// CreateMany provides a mock function with given fields: ctx, identity, docs +func (_m *Collection) CreateMany(ctx context.Context, identity immutable.Option[string], docs []*client.Document) error { + ret := _m.Called(ctx, identity, docs) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []*client.Document) error); ok { - r0 = rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], []*client.Document) error); ok { + r0 = rf(ctx, identity, docs) } else { r0 = ret.Error(0) } @@ -143,15 +187,16 @@ type Collection_CreateMany_Call struct { } // CreateMany is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 []*client.Document -func (_e *Collection_Expecter) CreateMany(_a0 interface{}, _a1 interface{}) *Collection_CreateMany_Call { - return &Collection_CreateMany_Call{Call: _e.mock.On("CreateMany", _a0, _a1)} +// - ctx context.Context +// - identity immutable.Option[string] +// - docs []*client.Document +func (_e *Collection_Expecter) CreateMany(ctx interface{}, identity interface{}, docs interface{}) *Collection_CreateMany_Call { + return &Collection_CreateMany_Call{Call: _e.mock.On("CreateMany", ctx, identity, docs)} } -func (_c *Collection_CreateMany_Call) Run(run func(_a0 context.Context, _a1 []*client.Document)) *Collection_CreateMany_Call { +func (_c *Collection_CreateMany_Call) Run(run func(ctx context.Context, identity immutable.Option[string], docs []*client.Document)) *Collection_CreateMany_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]*client.Document)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].([]*client.Document)) }) return _c } @@ -161,7 +206,7 @@ func (_c *Collection_CreateMany_Call) Return(_a0 error) *Collection_CreateMany_C return _c } -func (_c *Collection_CreateMany_Call) RunAndReturn(run func(context.Context, []*client.Document) error) *Collection_CreateMany_Call { +func (_c *Collection_CreateMany_Call) RunAndReturn(run func(context.Context, immutable.Option[string], []*client.Document) error) *Collection_CreateMany_Call { _c.Call.Return(run) return _c } @@ -207,23 +252,23 @@ func (_c *Collection_Definition_Call) RunAndReturn(run func() client.CollectionD return _c } -// Delete provides a mock function with given fields: _a0, _a1 -func (_m *Collection) Delete(_a0 context.Context, _a1 client.DocID) (bool, error) { - ret := _m.Called(_a0, _a1) +// Delete provides a mock function with given fields: ctx, identity, docID +func (_m *Collection) Delete(ctx context.Context, identity immutable.Option[string], docID client.DocID) (bool, error) { + ret := _m.Called(ctx, identity, docID) var r0 bool var r1 error - if rf, ok := ret.Get(0).(func(context.Context, client.DocID) (bool, error)); ok { - return rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], client.DocID) (bool, error)); ok { + return rf(ctx, identity, docID) } - if rf, ok := ret.Get(0).(func(context.Context, client.DocID) bool); ok { - r0 = rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], client.DocID) bool); ok { + r0 = rf(ctx, identity, docID) } else { r0 = ret.Get(0).(bool) } - if rf, ok := ret.Get(1).(func(context.Context, client.DocID) error); ok { - r1 = rf(_a0, _a1) + if rf, ok := ret.Get(1).(func(context.Context, immutable.Option[string], client.DocID) error); ok { + r1 = rf(ctx, identity, docID) } else { r1 = ret.Error(1) } @@ -237,15 +282,16 @@ type Collection_Delete_Call struct { } // Delete is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 client.DocID -func (_e *Collection_Expecter) Delete(_a0 interface{}, _a1 interface{}) *Collection_Delete_Call { - return &Collection_Delete_Call{Call: _e.mock.On("Delete", _a0, _a1)} +// - ctx context.Context +// - identity immutable.Option[string] +// - docID client.DocID +func (_e *Collection_Expecter) Delete(ctx interface{}, identity interface{}, docID interface{}) *Collection_Delete_Call { + return &Collection_Delete_Call{Call: _e.mock.On("Delete", ctx, identity, docID)} } -func (_c *Collection_Delete_Call) Run(run func(_a0 context.Context, _a1 client.DocID)) *Collection_Delete_Call { +func (_c *Collection_Delete_Call) Run(run func(ctx context.Context, identity immutable.Option[string], docID client.DocID)) *Collection_Delete_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(client.DocID)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(client.DocID)) }) return _c } @@ -255,30 +301,73 @@ func (_c *Collection_Delete_Call) Return(_a0 bool, _a1 error) *Collection_Delete return _c } -func (_c *Collection_Delete_Call) RunAndReturn(run func(context.Context, client.DocID) (bool, error)) *Collection_Delete_Call { +func (_c *Collection_Delete_Call) RunAndReturn(run func(context.Context, immutable.Option[string], client.DocID) (bool, error)) *Collection_Delete_Call { _c.Call.Return(run) return _c } -// DeleteWith provides a mock function with given fields: ctx, target -func (_m *Collection) DeleteWith(ctx context.Context, target interface{}) (*client.DeleteResult, error) { - ret := _m.Called(ctx, target) +// DeleteDocIndex provides a mock function with given fields: _a0, _a1 +func (_m *Collection) DeleteDocIndex(_a0 context.Context, _a1 *client.Document) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *client.Document) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Collection_DeleteDocIndex_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteDocIndex' +type Collection_DeleteDocIndex_Call struct { + *mock.Call +} + +// DeleteDocIndex is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *client.Document +func (_e *Collection_Expecter) DeleteDocIndex(_a0 interface{}, _a1 interface{}) *Collection_DeleteDocIndex_Call { + return &Collection_DeleteDocIndex_Call{Call: _e.mock.On("DeleteDocIndex", _a0, _a1)} +} + +func (_c *Collection_DeleteDocIndex_Call) Run(run func(_a0 context.Context, _a1 *client.Document)) *Collection_DeleteDocIndex_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*client.Document)) + }) + return _c +} + +func (_c *Collection_DeleteDocIndex_Call) Return(_a0 error) *Collection_DeleteDocIndex_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Collection_DeleteDocIndex_Call) RunAndReturn(run func(context.Context, *client.Document) error) *Collection_DeleteDocIndex_Call { + _c.Call.Return(run) + return _c +} + +// DeleteWith provides a mock function with given fields: ctx, identity, target +func (_m *Collection) DeleteWith(ctx context.Context, identity immutable.Option[string], target interface{}) (*client.DeleteResult, error) { + ret := _m.Called(ctx, identity, target) var r0 *client.DeleteResult var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}) (*client.DeleteResult, error)); ok { - return rf(ctx, target) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], interface{}) (*client.DeleteResult, error)); ok { + return rf(ctx, identity, target) } - if rf, ok := ret.Get(0).(func(context.Context, interface{}) *client.DeleteResult); ok { - r0 = rf(ctx, target) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], interface{}) *client.DeleteResult); ok { + r0 = rf(ctx, identity, target) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*client.DeleteResult) } } - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, target) + if rf, ok := ret.Get(1).(func(context.Context, immutable.Option[string], interface{}) error); ok { + r1 = rf(ctx, identity, target) } else { r1 = ret.Error(1) } @@ -293,14 +382,15 @@ type Collection_DeleteWith_Call struct { // DeleteWith is a helper method to define mock.On call // - ctx context.Context +// - identity immutable.Option[string] // - target interface{} -func (_e *Collection_Expecter) DeleteWith(ctx interface{}, target interface{}) *Collection_DeleteWith_Call { - return &Collection_DeleteWith_Call{Call: _e.mock.On("DeleteWith", ctx, target)} +func (_e *Collection_Expecter) DeleteWith(ctx interface{}, identity interface{}, target interface{}) *Collection_DeleteWith_Call { + return &Collection_DeleteWith_Call{Call: _e.mock.On("DeleteWith", ctx, identity, target)} } -func (_c *Collection_DeleteWith_Call) Run(run func(ctx context.Context, target interface{})) *Collection_DeleteWith_Call { +func (_c *Collection_DeleteWith_Call) Run(run func(ctx context.Context, identity immutable.Option[string], target interface{})) *Collection_DeleteWith_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{})) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(interface{})) }) return _c } @@ -310,30 +400,30 @@ func (_c *Collection_DeleteWith_Call) Return(_a0 *client.DeleteResult, _a1 error return _c } -func (_c *Collection_DeleteWith_Call) RunAndReturn(run func(context.Context, interface{}) (*client.DeleteResult, error)) *Collection_DeleteWith_Call { +func (_c *Collection_DeleteWith_Call) RunAndReturn(run func(context.Context, immutable.Option[string], interface{}) (*client.DeleteResult, error)) *Collection_DeleteWith_Call { _c.Call.Return(run) return _c } -// DeleteWithDocID provides a mock function with given fields: _a0, _a1 -func (_m *Collection) DeleteWithDocID(_a0 context.Context, _a1 client.DocID) (*client.DeleteResult, error) { - ret := _m.Called(_a0, _a1) +// DeleteWithDocID provides a mock function with given fields: ctx, identity, docID +func (_m *Collection) DeleteWithDocID(ctx context.Context, identity immutable.Option[string], docID client.DocID) (*client.DeleteResult, error) { + ret := _m.Called(ctx, identity, docID) var r0 *client.DeleteResult var r1 error - if rf, ok := ret.Get(0).(func(context.Context, client.DocID) (*client.DeleteResult, error)); ok { - return rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], client.DocID) (*client.DeleteResult, error)); ok { + return rf(ctx, identity, docID) } - if rf, ok := ret.Get(0).(func(context.Context, client.DocID) *client.DeleteResult); ok { - r0 = rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], client.DocID) *client.DeleteResult); ok { + r0 = rf(ctx, identity, docID) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*client.DeleteResult) } } - if rf, ok := ret.Get(1).(func(context.Context, client.DocID) error); ok { - r1 = rf(_a0, _a1) + if rf, ok := ret.Get(1).(func(context.Context, immutable.Option[string], client.DocID) error); ok { + r1 = rf(ctx, identity, docID) } else { r1 = ret.Error(1) } @@ -347,15 +437,16 @@ type Collection_DeleteWithDocID_Call struct { } // DeleteWithDocID is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 client.DocID -func (_e *Collection_Expecter) DeleteWithDocID(_a0 interface{}, _a1 interface{}) *Collection_DeleteWithDocID_Call { - return &Collection_DeleteWithDocID_Call{Call: _e.mock.On("DeleteWithDocID", _a0, _a1)} +// - ctx context.Context +// - identity immutable.Option[string] +// - docID client.DocID +func (_e *Collection_Expecter) DeleteWithDocID(ctx interface{}, identity interface{}, docID interface{}) *Collection_DeleteWithDocID_Call { + return &Collection_DeleteWithDocID_Call{Call: _e.mock.On("DeleteWithDocID", ctx, identity, docID)} } -func (_c *Collection_DeleteWithDocID_Call) Run(run func(_a0 context.Context, _a1 client.DocID)) *Collection_DeleteWithDocID_Call { +func (_c *Collection_DeleteWithDocID_Call) Run(run func(ctx context.Context, identity immutable.Option[string], docID client.DocID)) *Collection_DeleteWithDocID_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(client.DocID)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(client.DocID)) }) return _c } @@ -365,30 +456,30 @@ func (_c *Collection_DeleteWithDocID_Call) Return(_a0 *client.DeleteResult, _a1 return _c } -func (_c *Collection_DeleteWithDocID_Call) RunAndReturn(run func(context.Context, client.DocID) (*client.DeleteResult, error)) *Collection_DeleteWithDocID_Call { +func (_c *Collection_DeleteWithDocID_Call) RunAndReturn(run func(context.Context, immutable.Option[string], client.DocID) (*client.DeleteResult, error)) *Collection_DeleteWithDocID_Call { _c.Call.Return(run) return _c } -// DeleteWithDocIDs provides a mock function with given fields: _a0, _a1 -func (_m *Collection) DeleteWithDocIDs(_a0 context.Context, _a1 []client.DocID) (*client.DeleteResult, error) { - ret := _m.Called(_a0, _a1) +// DeleteWithDocIDs provides a mock function with given fields: ctx, identity, docIDs +func (_m *Collection) DeleteWithDocIDs(ctx context.Context, identity immutable.Option[string], docIDs []client.DocID) (*client.DeleteResult, error) { + ret := _m.Called(ctx, identity, docIDs) var r0 *client.DeleteResult var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []client.DocID) (*client.DeleteResult, error)); ok { - return rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], []client.DocID) (*client.DeleteResult, error)); ok { + return rf(ctx, identity, docIDs) } - if rf, ok := ret.Get(0).(func(context.Context, []client.DocID) *client.DeleteResult); ok { - r0 = rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], []client.DocID) *client.DeleteResult); ok { + r0 = rf(ctx, identity, docIDs) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*client.DeleteResult) } } - if rf, ok := ret.Get(1).(func(context.Context, []client.DocID) error); ok { - r1 = rf(_a0, _a1) + if rf, ok := ret.Get(1).(func(context.Context, immutable.Option[string], []client.DocID) error); ok { + r1 = rf(ctx, identity, docIDs) } else { r1 = ret.Error(1) } @@ -402,15 +493,16 @@ type Collection_DeleteWithDocIDs_Call struct { } // DeleteWithDocIDs is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 []client.DocID -func (_e *Collection_Expecter) DeleteWithDocIDs(_a0 interface{}, _a1 interface{}) *Collection_DeleteWithDocIDs_Call { - return &Collection_DeleteWithDocIDs_Call{Call: _e.mock.On("DeleteWithDocIDs", _a0, _a1)} +// - ctx context.Context +// - identity immutable.Option[string] +// - docIDs []client.DocID +func (_e *Collection_Expecter) DeleteWithDocIDs(ctx interface{}, identity interface{}, docIDs interface{}) *Collection_DeleteWithDocIDs_Call { + return &Collection_DeleteWithDocIDs_Call{Call: _e.mock.On("DeleteWithDocIDs", ctx, identity, docIDs)} } -func (_c *Collection_DeleteWithDocIDs_Call) Run(run func(_a0 context.Context, _a1 []client.DocID)) *Collection_DeleteWithDocIDs_Call { +func (_c *Collection_DeleteWithDocIDs_Call) Run(run func(ctx context.Context, identity immutable.Option[string], docIDs []client.DocID)) *Collection_DeleteWithDocIDs_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]client.DocID)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].([]client.DocID)) }) return _c } @@ -420,30 +512,30 @@ func (_c *Collection_DeleteWithDocIDs_Call) Return(_a0 *client.DeleteResult, _a1 return _c } -func (_c *Collection_DeleteWithDocIDs_Call) RunAndReturn(run func(context.Context, []client.DocID) (*client.DeleteResult, error)) *Collection_DeleteWithDocIDs_Call { +func (_c *Collection_DeleteWithDocIDs_Call) RunAndReturn(run func(context.Context, immutable.Option[string], []client.DocID) (*client.DeleteResult, error)) *Collection_DeleteWithDocIDs_Call { _c.Call.Return(run) return _c } -// DeleteWithFilter provides a mock function with given fields: ctx, filter -func (_m *Collection) DeleteWithFilter(ctx context.Context, filter interface{}) (*client.DeleteResult, error) { - ret := _m.Called(ctx, filter) +// DeleteWithFilter provides a mock function with given fields: ctx, identity, filter +func (_m *Collection) DeleteWithFilter(ctx context.Context, identity immutable.Option[string], filter interface{}) (*client.DeleteResult, error) { + ret := _m.Called(ctx, identity, filter) var r0 *client.DeleteResult var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}) (*client.DeleteResult, error)); ok { - return rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], interface{}) (*client.DeleteResult, error)); ok { + return rf(ctx, identity, filter) } - if rf, ok := ret.Get(0).(func(context.Context, interface{}) *client.DeleteResult); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], interface{}) *client.DeleteResult); ok { + r0 = rf(ctx, identity, filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*client.DeleteResult) } } - if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, immutable.Option[string], interface{}) error); ok { + r1 = rf(ctx, identity, filter) } else { r1 = ret.Error(1) } @@ -458,14 +550,15 @@ type Collection_DeleteWithFilter_Call struct { // DeleteWithFilter is a helper method to define mock.On call // - ctx context.Context +// - identity immutable.Option[string] // - filter interface{} -func (_e *Collection_Expecter) DeleteWithFilter(ctx interface{}, filter interface{}) *Collection_DeleteWithFilter_Call { - return &Collection_DeleteWithFilter_Call{Call: _e.mock.On("DeleteWithFilter", ctx, filter)} +func (_e *Collection_Expecter) DeleteWithFilter(ctx interface{}, identity interface{}, filter interface{}) *Collection_DeleteWithFilter_Call { + return &Collection_DeleteWithFilter_Call{Call: _e.mock.On("DeleteWithFilter", ctx, identity, filter)} } -func (_c *Collection_DeleteWithFilter_Call) Run(run func(ctx context.Context, filter interface{})) *Collection_DeleteWithFilter_Call { +func (_c *Collection_DeleteWithFilter_Call) Run(run func(ctx context.Context, identity immutable.Option[string], filter interface{})) *Collection_DeleteWithFilter_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{})) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(interface{})) }) return _c } @@ -475,7 +568,7 @@ func (_c *Collection_DeleteWithFilter_Call) Return(_a0 *client.DeleteResult, _a1 return _c } -func (_c *Collection_DeleteWithFilter_Call) RunAndReturn(run func(context.Context, interface{}) (*client.DeleteResult, error)) *Collection_DeleteWithFilter_Call { +func (_c *Collection_DeleteWithFilter_Call) RunAndReturn(run func(context.Context, immutable.Option[string], interface{}) (*client.DeleteResult, error)) *Collection_DeleteWithFilter_Call { _c.Call.Return(run) return _c } @@ -564,23 +657,23 @@ func (_c *Collection_DropIndex_Call) RunAndReturn(run func(context.Context, stri return _c } -// Exists provides a mock function with given fields: _a0, _a1 -func (_m *Collection) Exists(_a0 context.Context, _a1 client.DocID) (bool, error) { - ret := _m.Called(_a0, _a1) +// Exists provides a mock function with given fields: ctx, identity, docID +func (_m *Collection) Exists(ctx context.Context, identity immutable.Option[string], docID client.DocID) (bool, error) { + ret := _m.Called(ctx, identity, docID) var r0 bool var r1 error - if rf, ok := ret.Get(0).(func(context.Context, client.DocID) (bool, error)); ok { - return rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], client.DocID) (bool, error)); ok { + return rf(ctx, identity, docID) } - if rf, ok := ret.Get(0).(func(context.Context, client.DocID) bool); ok { - r0 = rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], client.DocID) bool); ok { + r0 = rf(ctx, identity, docID) } else { r0 = ret.Get(0).(bool) } - if rf, ok := ret.Get(1).(func(context.Context, client.DocID) error); ok { - r1 = rf(_a0, _a1) + if rf, ok := ret.Get(1).(func(context.Context, immutable.Option[string], client.DocID) error); ok { + r1 = rf(ctx, identity, docID) } else { r1 = ret.Error(1) } @@ -594,15 +687,16 @@ type Collection_Exists_Call struct { } // Exists is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 client.DocID -func (_e *Collection_Expecter) Exists(_a0 interface{}, _a1 interface{}) *Collection_Exists_Call { - return &Collection_Exists_Call{Call: _e.mock.On("Exists", _a0, _a1)} +// - ctx context.Context +// - identity immutable.Option[string] +// - docID client.DocID +func (_e *Collection_Expecter) Exists(ctx interface{}, identity interface{}, docID interface{}) *Collection_Exists_Call { + return &Collection_Exists_Call{Call: _e.mock.On("Exists", ctx, identity, docID)} } -func (_c *Collection_Exists_Call) Run(run func(_a0 context.Context, _a1 client.DocID)) *Collection_Exists_Call { +func (_c *Collection_Exists_Call) Run(run func(ctx context.Context, identity immutable.Option[string], docID client.DocID)) *Collection_Exists_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(client.DocID)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(client.DocID)) }) return _c } @@ -612,30 +706,30 @@ func (_c *Collection_Exists_Call) Return(_a0 bool, _a1 error) *Collection_Exists return _c } -func (_c *Collection_Exists_Call) RunAndReturn(run func(context.Context, client.DocID) (bool, error)) *Collection_Exists_Call { +func (_c *Collection_Exists_Call) RunAndReturn(run func(context.Context, immutable.Option[string], client.DocID) (bool, error)) *Collection_Exists_Call { _c.Call.Return(run) return _c } -// Get provides a mock function with given fields: ctx, docID, showDeleted -func (_m *Collection) Get(ctx context.Context, docID client.DocID, showDeleted bool) (*client.Document, error) { - ret := _m.Called(ctx, docID, showDeleted) +// Get provides a mock function with given fields: ctx, identity, docID, showDeleted +func (_m *Collection) Get(ctx context.Context, identity immutable.Option[string], docID client.DocID, showDeleted bool) (*client.Document, error) { + ret := _m.Called(ctx, identity, docID, showDeleted) var r0 *client.Document var r1 error - if rf, ok := ret.Get(0).(func(context.Context, client.DocID, bool) (*client.Document, error)); ok { - return rf(ctx, docID, showDeleted) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], client.DocID, bool) (*client.Document, error)); ok { + return rf(ctx, identity, docID, showDeleted) } - if rf, ok := ret.Get(0).(func(context.Context, client.DocID, bool) *client.Document); ok { - r0 = rf(ctx, docID, showDeleted) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], client.DocID, bool) *client.Document); ok { + r0 = rf(ctx, identity, docID, showDeleted) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*client.Document) } } - if rf, ok := ret.Get(1).(func(context.Context, client.DocID, bool) error); ok { - r1 = rf(ctx, docID, showDeleted) + if rf, ok := ret.Get(1).(func(context.Context, immutable.Option[string], client.DocID, bool) error); ok { + r1 = rf(ctx, identity, docID, showDeleted) } else { r1 = ret.Error(1) } @@ -650,15 +744,16 @@ type Collection_Get_Call struct { // Get is a helper method to define mock.On call // - ctx context.Context +// - identity immutable.Option[string] // - docID client.DocID // - showDeleted bool -func (_e *Collection_Expecter) Get(ctx interface{}, docID interface{}, showDeleted interface{}) *Collection_Get_Call { - return &Collection_Get_Call{Call: _e.mock.On("Get", ctx, docID, showDeleted)} +func (_e *Collection_Expecter) Get(ctx interface{}, identity interface{}, docID interface{}, showDeleted interface{}) *Collection_Get_Call { + return &Collection_Get_Call{Call: _e.mock.On("Get", ctx, identity, docID, showDeleted)} } -func (_c *Collection_Get_Call) Run(run func(ctx context.Context, docID client.DocID, showDeleted bool)) *Collection_Get_Call { +func (_c *Collection_Get_Call) Run(run func(ctx context.Context, identity immutable.Option[string], docID client.DocID, showDeleted bool)) *Collection_Get_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(client.DocID), args[2].(bool)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(client.DocID), args[3].(bool)) }) return _c } @@ -668,30 +763,30 @@ func (_c *Collection_Get_Call) Return(_a0 *client.Document, _a1 error) *Collecti return _c } -func (_c *Collection_Get_Call) RunAndReturn(run func(context.Context, client.DocID, bool) (*client.Document, error)) *Collection_Get_Call { +func (_c *Collection_Get_Call) RunAndReturn(run func(context.Context, immutable.Option[string], client.DocID, bool) (*client.Document, error)) *Collection_Get_Call { _c.Call.Return(run) return _c } -// GetAllDocIDs provides a mock function with given fields: ctx -func (_m *Collection) GetAllDocIDs(ctx context.Context) (<-chan client.DocIDResult, error) { - ret := _m.Called(ctx) +// GetAllDocIDs provides a mock function with given fields: ctx, identity +func (_m *Collection) GetAllDocIDs(ctx context.Context, identity immutable.Option[string]) (<-chan client.DocIDResult, error) { + ret := _m.Called(ctx, identity) var r0 <-chan client.DocIDResult var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (<-chan client.DocIDResult, error)); ok { - return rf(ctx) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string]) (<-chan client.DocIDResult, error)); ok { + return rf(ctx, identity) } - if rf, ok := ret.Get(0).(func(context.Context) <-chan client.DocIDResult); ok { - r0 = rf(ctx) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string]) <-chan client.DocIDResult); ok { + r0 = rf(ctx, identity) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(<-chan client.DocIDResult) } } - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) + if rf, ok := ret.Get(1).(func(context.Context, immutable.Option[string]) error); ok { + r1 = rf(ctx, identity) } else { r1 = ret.Error(1) } @@ -706,13 +801,14 @@ type Collection_GetAllDocIDs_Call struct { // GetAllDocIDs is a helper method to define mock.On call // - ctx context.Context -func (_e *Collection_Expecter) GetAllDocIDs(ctx interface{}) *Collection_GetAllDocIDs_Call { - return &Collection_GetAllDocIDs_Call{Call: _e.mock.On("GetAllDocIDs", ctx)} +// - identity immutable.Option[string] +func (_e *Collection_Expecter) GetAllDocIDs(ctx interface{}, identity interface{}) *Collection_GetAllDocIDs_Call { + return &Collection_GetAllDocIDs_Call{Call: _e.mock.On("GetAllDocIDs", ctx, identity)} } -func (_c *Collection_GetAllDocIDs_Call) Run(run func(ctx context.Context)) *Collection_GetAllDocIDs_Call { +func (_c *Collection_GetAllDocIDs_Call) Run(run func(ctx context.Context, identity immutable.Option[string])) *Collection_GetAllDocIDs_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) + run(args[0].(context.Context), args[1].(immutable.Option[string])) }) return _c } @@ -722,7 +818,7 @@ func (_c *Collection_GetAllDocIDs_Call) Return(_a0 <-chan client.DocIDResult, _a return _c } -func (_c *Collection_GetAllDocIDs_Call) RunAndReturn(run func(context.Context) (<-chan client.DocIDResult, error)) *Collection_GetAllDocIDs_Call { +func (_c *Collection_GetAllDocIDs_Call) RunAndReturn(run func(context.Context, immutable.Option[string]) (<-chan client.DocIDResult, error)) *Collection_GetAllDocIDs_Call { _c.Call.Return(run) return _c } @@ -863,13 +959,13 @@ func (_c *Collection_Name_Call) RunAndReturn(run func() immutable.Option[string] return _c } -// Save provides a mock function with given fields: _a0, _a1 -func (_m *Collection) Save(_a0 context.Context, _a1 *client.Document) error { - ret := _m.Called(_a0, _a1) +// Save provides a mock function with given fields: ctx, identity, doc +func (_m *Collection) Save(ctx context.Context, identity immutable.Option[string], doc *client.Document) error { + ret := _m.Called(ctx, identity, doc) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *client.Document) error); ok { - r0 = rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], *client.Document) error); ok { + r0 = rf(ctx, identity, doc) } else { r0 = ret.Error(0) } @@ -883,15 +979,16 @@ type Collection_Save_Call struct { } // Save is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *client.Document -func (_e *Collection_Expecter) Save(_a0 interface{}, _a1 interface{}) *Collection_Save_Call { - return &Collection_Save_Call{Call: _e.mock.On("Save", _a0, _a1)} +// - ctx context.Context +// - identity immutable.Option[string] +// - doc *client.Document +func (_e *Collection_Expecter) Save(ctx interface{}, identity interface{}, doc interface{}) *Collection_Save_Call { + return &Collection_Save_Call{Call: _e.mock.On("Save", ctx, identity, doc)} } -func (_c *Collection_Save_Call) Run(run func(_a0 context.Context, _a1 *client.Document)) *Collection_Save_Call { +func (_c *Collection_Save_Call) Run(run func(ctx context.Context, identity immutable.Option[string], doc *client.Document)) *Collection_Save_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*client.Document)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(*client.Document)) }) return _c } @@ -901,7 +998,7 @@ func (_c *Collection_Save_Call) Return(_a0 error) *Collection_Save_Call { return _c } -func (_c *Collection_Save_Call) RunAndReturn(run func(context.Context, *client.Document) error) *Collection_Save_Call { +func (_c *Collection_Save_Call) RunAndReturn(run func(context.Context, immutable.Option[string], *client.Document) error) *Collection_Save_Call { _c.Call.Return(run) return _c } @@ -988,13 +1085,13 @@ func (_c *Collection_SchemaRoot_Call) RunAndReturn(run func() string) *Collectio return _c } -// Update provides a mock function with given fields: _a0, _a1 -func (_m *Collection) Update(_a0 context.Context, _a1 *client.Document) error { - ret := _m.Called(_a0, _a1) +// Update provides a mock function with given fields: ctx, identity, docs +func (_m *Collection) Update(ctx context.Context, identity immutable.Option[string], docs *client.Document) error { + ret := _m.Called(ctx, identity, docs) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *client.Document) error); ok { - r0 = rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], *client.Document) error); ok { + r0 = rf(ctx, identity, docs) } else { r0 = ret.Error(0) } @@ -1008,15 +1105,16 @@ type Collection_Update_Call struct { } // Update is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *client.Document -func (_e *Collection_Expecter) Update(_a0 interface{}, _a1 interface{}) *Collection_Update_Call { - return &Collection_Update_Call{Call: _e.mock.On("Update", _a0, _a1)} +// - ctx context.Context +// - identity immutable.Option[string] +// - docs *client.Document +func (_e *Collection_Expecter) Update(ctx interface{}, identity interface{}, docs interface{}) *Collection_Update_Call { + return &Collection_Update_Call{Call: _e.mock.On("Update", ctx, identity, docs)} } -func (_c *Collection_Update_Call) Run(run func(_a0 context.Context, _a1 *client.Document)) *Collection_Update_Call { +func (_c *Collection_Update_Call) Run(run func(ctx context.Context, identity immutable.Option[string], docs *client.Document)) *Collection_Update_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*client.Document)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(*client.Document)) }) return _c } @@ -1026,30 +1124,74 @@ func (_c *Collection_Update_Call) Return(_a0 error) *Collection_Update_Call { return _c } -func (_c *Collection_Update_Call) RunAndReturn(run func(context.Context, *client.Document) error) *Collection_Update_Call { +func (_c *Collection_Update_Call) RunAndReturn(run func(context.Context, immutable.Option[string], *client.Document) error) *Collection_Update_Call { _c.Call.Return(run) return _c } -// UpdateWith provides a mock function with given fields: ctx, target, updater -func (_m *Collection) UpdateWith(ctx context.Context, target interface{}, updater string) (*client.UpdateResult, error) { - ret := _m.Called(ctx, target, updater) +// UpdateDocIndex provides a mock function with given fields: ctx, oldDoc, newDoc +func (_m *Collection) UpdateDocIndex(ctx context.Context, oldDoc *client.Document, newDoc *client.Document) error { + ret := _m.Called(ctx, oldDoc, newDoc) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *client.Document, *client.Document) error); ok { + r0 = rf(ctx, oldDoc, newDoc) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Collection_UpdateDocIndex_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateDocIndex' +type Collection_UpdateDocIndex_Call struct { + *mock.Call +} + +// UpdateDocIndex is a helper method to define mock.On call +// - ctx context.Context +// - oldDoc *client.Document +// - newDoc *client.Document +func (_e *Collection_Expecter) UpdateDocIndex(ctx interface{}, oldDoc interface{}, newDoc interface{}) *Collection_UpdateDocIndex_Call { + return &Collection_UpdateDocIndex_Call{Call: _e.mock.On("UpdateDocIndex", ctx, oldDoc, newDoc)} +} + +func (_c *Collection_UpdateDocIndex_Call) Run(run func(ctx context.Context, oldDoc *client.Document, newDoc *client.Document)) *Collection_UpdateDocIndex_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*client.Document), args[2].(*client.Document)) + }) + return _c +} + +func (_c *Collection_UpdateDocIndex_Call) Return(_a0 error) *Collection_UpdateDocIndex_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Collection_UpdateDocIndex_Call) RunAndReturn(run func(context.Context, *client.Document, *client.Document) error) *Collection_UpdateDocIndex_Call { + _c.Call.Return(run) + return _c +} + +// UpdateWith provides a mock function with given fields: ctx, identity, target, updater +func (_m *Collection) UpdateWith(ctx context.Context, identity immutable.Option[string], target interface{}, updater string) (*client.UpdateResult, error) { + ret := _m.Called(ctx, identity, target, updater) var r0 *client.UpdateResult var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}, string) (*client.UpdateResult, error)); ok { - return rf(ctx, target, updater) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], interface{}, string) (*client.UpdateResult, error)); ok { + return rf(ctx, identity, target, updater) } - if rf, ok := ret.Get(0).(func(context.Context, interface{}, string) *client.UpdateResult); ok { - r0 = rf(ctx, target, updater) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], interface{}, string) *client.UpdateResult); ok { + r0 = rf(ctx, identity, target, updater) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*client.UpdateResult) } } - if rf, ok := ret.Get(1).(func(context.Context, interface{}, string) error); ok { - r1 = rf(ctx, target, updater) + if rf, ok := ret.Get(1).(func(context.Context, immutable.Option[string], interface{}, string) error); ok { + r1 = rf(ctx, identity, target, updater) } else { r1 = ret.Error(1) } @@ -1064,15 +1206,16 @@ type Collection_UpdateWith_Call struct { // UpdateWith is a helper method to define mock.On call // - ctx context.Context +// - identity immutable.Option[string] // - target interface{} // - updater string -func (_e *Collection_Expecter) UpdateWith(ctx interface{}, target interface{}, updater interface{}) *Collection_UpdateWith_Call { - return &Collection_UpdateWith_Call{Call: _e.mock.On("UpdateWith", ctx, target, updater)} +func (_e *Collection_Expecter) UpdateWith(ctx interface{}, identity interface{}, target interface{}, updater interface{}) *Collection_UpdateWith_Call { + return &Collection_UpdateWith_Call{Call: _e.mock.On("UpdateWith", ctx, identity, target, updater)} } -func (_c *Collection_UpdateWith_Call) Run(run func(ctx context.Context, target interface{}, updater string)) *Collection_UpdateWith_Call { +func (_c *Collection_UpdateWith_Call) Run(run func(ctx context.Context, identity immutable.Option[string], target interface{}, updater string)) *Collection_UpdateWith_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{}), args[2].(string)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(interface{}), args[3].(string)) }) return _c } @@ -1082,30 +1225,30 @@ func (_c *Collection_UpdateWith_Call) Return(_a0 *client.UpdateResult, _a1 error return _c } -func (_c *Collection_UpdateWith_Call) RunAndReturn(run func(context.Context, interface{}, string) (*client.UpdateResult, error)) *Collection_UpdateWith_Call { +func (_c *Collection_UpdateWith_Call) RunAndReturn(run func(context.Context, immutable.Option[string], interface{}, string) (*client.UpdateResult, error)) *Collection_UpdateWith_Call { _c.Call.Return(run) return _c } -// UpdateWithDocID provides a mock function with given fields: ctx, docID, updater -func (_m *Collection) UpdateWithDocID(ctx context.Context, docID client.DocID, updater string) (*client.UpdateResult, error) { - ret := _m.Called(ctx, docID, updater) +// UpdateWithDocID provides a mock function with given fields: ctx, identity, docID, updater +func (_m *Collection) UpdateWithDocID(ctx context.Context, identity immutable.Option[string], docID client.DocID, updater string) (*client.UpdateResult, error) { + ret := _m.Called(ctx, identity, docID, updater) var r0 *client.UpdateResult var r1 error - if rf, ok := ret.Get(0).(func(context.Context, client.DocID, string) (*client.UpdateResult, error)); ok { - return rf(ctx, docID, updater) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], client.DocID, string) (*client.UpdateResult, error)); ok { + return rf(ctx, identity, docID, updater) } - if rf, ok := ret.Get(0).(func(context.Context, client.DocID, string) *client.UpdateResult); ok { - r0 = rf(ctx, docID, updater) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], client.DocID, string) *client.UpdateResult); ok { + r0 = rf(ctx, identity, docID, updater) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*client.UpdateResult) } } - if rf, ok := ret.Get(1).(func(context.Context, client.DocID, string) error); ok { - r1 = rf(ctx, docID, updater) + if rf, ok := ret.Get(1).(func(context.Context, immutable.Option[string], client.DocID, string) error); ok { + r1 = rf(ctx, identity, docID, updater) } else { r1 = ret.Error(1) } @@ -1120,15 +1263,16 @@ type Collection_UpdateWithDocID_Call struct { // UpdateWithDocID is a helper method to define mock.On call // - ctx context.Context +// - identity immutable.Option[string] // - docID client.DocID // - updater string -func (_e *Collection_Expecter) UpdateWithDocID(ctx interface{}, docID interface{}, updater interface{}) *Collection_UpdateWithDocID_Call { - return &Collection_UpdateWithDocID_Call{Call: _e.mock.On("UpdateWithDocID", ctx, docID, updater)} +func (_e *Collection_Expecter) UpdateWithDocID(ctx interface{}, identity interface{}, docID interface{}, updater interface{}) *Collection_UpdateWithDocID_Call { + return &Collection_UpdateWithDocID_Call{Call: _e.mock.On("UpdateWithDocID", ctx, identity, docID, updater)} } -func (_c *Collection_UpdateWithDocID_Call) Run(run func(ctx context.Context, docID client.DocID, updater string)) *Collection_UpdateWithDocID_Call { +func (_c *Collection_UpdateWithDocID_Call) Run(run func(ctx context.Context, identity immutable.Option[string], docID client.DocID, updater string)) *Collection_UpdateWithDocID_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(client.DocID), args[2].(string)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(client.DocID), args[3].(string)) }) return _c } @@ -1138,30 +1282,30 @@ func (_c *Collection_UpdateWithDocID_Call) Return(_a0 *client.UpdateResult, _a1 return _c } -func (_c *Collection_UpdateWithDocID_Call) RunAndReturn(run func(context.Context, client.DocID, string) (*client.UpdateResult, error)) *Collection_UpdateWithDocID_Call { +func (_c *Collection_UpdateWithDocID_Call) RunAndReturn(run func(context.Context, immutable.Option[string], client.DocID, string) (*client.UpdateResult, error)) *Collection_UpdateWithDocID_Call { _c.Call.Return(run) return _c } -// UpdateWithDocIDs provides a mock function with given fields: _a0, _a1, _a2 -func (_m *Collection) UpdateWithDocIDs(_a0 context.Context, _a1 []client.DocID, _a2 string) (*client.UpdateResult, error) { - ret := _m.Called(_a0, _a1, _a2) +// UpdateWithDocIDs provides a mock function with given fields: ctx, identity, docIDs, updater +func (_m *Collection) UpdateWithDocIDs(ctx context.Context, identity immutable.Option[string], docIDs []client.DocID, updater string) (*client.UpdateResult, error) { + ret := _m.Called(ctx, identity, docIDs, updater) var r0 *client.UpdateResult var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []client.DocID, string) (*client.UpdateResult, error)); ok { - return rf(_a0, _a1, _a2) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], []client.DocID, string) (*client.UpdateResult, error)); ok { + return rf(ctx, identity, docIDs, updater) } - if rf, ok := ret.Get(0).(func(context.Context, []client.DocID, string) *client.UpdateResult); ok { - r0 = rf(_a0, _a1, _a2) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], []client.DocID, string) *client.UpdateResult); ok { + r0 = rf(ctx, identity, docIDs, updater) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*client.UpdateResult) } } - if rf, ok := ret.Get(1).(func(context.Context, []client.DocID, string) error); ok { - r1 = rf(_a0, _a1, _a2) + if rf, ok := ret.Get(1).(func(context.Context, immutable.Option[string], []client.DocID, string) error); ok { + r1 = rf(ctx, identity, docIDs, updater) } else { r1 = ret.Error(1) } @@ -1175,16 +1319,17 @@ type Collection_UpdateWithDocIDs_Call struct { } // UpdateWithDocIDs is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 []client.DocID -// - _a2 string -func (_e *Collection_Expecter) UpdateWithDocIDs(_a0 interface{}, _a1 interface{}, _a2 interface{}) *Collection_UpdateWithDocIDs_Call { - return &Collection_UpdateWithDocIDs_Call{Call: _e.mock.On("UpdateWithDocIDs", _a0, _a1, _a2)} +// - ctx context.Context +// - identity immutable.Option[string] +// - docIDs []client.DocID +// - updater string +func (_e *Collection_Expecter) UpdateWithDocIDs(ctx interface{}, identity interface{}, docIDs interface{}, updater interface{}) *Collection_UpdateWithDocIDs_Call { + return &Collection_UpdateWithDocIDs_Call{Call: _e.mock.On("UpdateWithDocIDs", ctx, identity, docIDs, updater)} } -func (_c *Collection_UpdateWithDocIDs_Call) Run(run func(_a0 context.Context, _a1 []client.DocID, _a2 string)) *Collection_UpdateWithDocIDs_Call { +func (_c *Collection_UpdateWithDocIDs_Call) Run(run func(ctx context.Context, identity immutable.Option[string], docIDs []client.DocID, updater string)) *Collection_UpdateWithDocIDs_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]client.DocID), args[2].(string)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].([]client.DocID), args[3].(string)) }) return _c } @@ -1194,30 +1339,30 @@ func (_c *Collection_UpdateWithDocIDs_Call) Return(_a0 *client.UpdateResult, _a1 return _c } -func (_c *Collection_UpdateWithDocIDs_Call) RunAndReturn(run func(context.Context, []client.DocID, string) (*client.UpdateResult, error)) *Collection_UpdateWithDocIDs_Call { +func (_c *Collection_UpdateWithDocIDs_Call) RunAndReturn(run func(context.Context, immutable.Option[string], []client.DocID, string) (*client.UpdateResult, error)) *Collection_UpdateWithDocIDs_Call { _c.Call.Return(run) return _c } -// UpdateWithFilter provides a mock function with given fields: ctx, filter, updater -func (_m *Collection) UpdateWithFilter(ctx context.Context, filter interface{}, updater string) (*client.UpdateResult, error) { - ret := _m.Called(ctx, filter, updater) +// UpdateWithFilter provides a mock function with given fields: ctx, identity, filter, updater +func (_m *Collection) UpdateWithFilter(ctx context.Context, identity immutable.Option[string], filter interface{}, updater string) (*client.UpdateResult, error) { + ret := _m.Called(ctx, identity, filter, updater) var r0 *client.UpdateResult var r1 error - if rf, ok := ret.Get(0).(func(context.Context, interface{}, string) (*client.UpdateResult, error)); ok { - return rf(ctx, filter, updater) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], interface{}, string) (*client.UpdateResult, error)); ok { + return rf(ctx, identity, filter, updater) } - if rf, ok := ret.Get(0).(func(context.Context, interface{}, string) *client.UpdateResult); ok { - r0 = rf(ctx, filter, updater) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], interface{}, string) *client.UpdateResult); ok { + r0 = rf(ctx, identity, filter, updater) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*client.UpdateResult) } } - if rf, ok := ret.Get(1).(func(context.Context, interface{}, string) error); ok { - r1 = rf(ctx, filter, updater) + if rf, ok := ret.Get(1).(func(context.Context, immutable.Option[string], interface{}, string) error); ok { + r1 = rf(ctx, identity, filter, updater) } else { r1 = ret.Error(1) } @@ -1232,15 +1377,16 @@ type Collection_UpdateWithFilter_Call struct { // UpdateWithFilter is a helper method to define mock.On call // - ctx context.Context +// - identity immutable.Option[string] // - filter interface{} // - updater string -func (_e *Collection_Expecter) UpdateWithFilter(ctx interface{}, filter interface{}, updater interface{}) *Collection_UpdateWithFilter_Call { - return &Collection_UpdateWithFilter_Call{Call: _e.mock.On("UpdateWithFilter", ctx, filter, updater)} +func (_e *Collection_Expecter) UpdateWithFilter(ctx interface{}, identity interface{}, filter interface{}, updater interface{}) *Collection_UpdateWithFilter_Call { + return &Collection_UpdateWithFilter_Call{Call: _e.mock.On("UpdateWithFilter", ctx, identity, filter, updater)} } -func (_c *Collection_UpdateWithFilter_Call) Run(run func(ctx context.Context, filter interface{}, updater string)) *Collection_UpdateWithFilter_Call { +func (_c *Collection_UpdateWithFilter_Call) Run(run func(ctx context.Context, identity immutable.Option[string], filter interface{}, updater string)) *Collection_UpdateWithFilter_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(interface{}), args[2].(string)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(interface{}), args[3].(string)) }) return _c } @@ -1250,7 +1396,7 @@ func (_c *Collection_UpdateWithFilter_Call) Return(_a0 *client.UpdateResult, _a1 return _c } -func (_c *Collection_UpdateWithFilter_Call) RunAndReturn(run func(context.Context, interface{}, string) (*client.UpdateResult, error)) *Collection_UpdateWithFilter_Call { +func (_c *Collection_UpdateWithFilter_Call) RunAndReturn(run func(context.Context, immutable.Option[string], interface{}, string) (*client.UpdateResult, error)) *Collection_UpdateWithFilter_Call { _c.Call.Return(run) return _c } diff --git a/client/mocks/db.go b/client/mocks/db.go index c6f6711a59..c31578e190 100644 --- a/client/mocks/db.go +++ b/client/mocks/db.go @@ -32,6 +32,60 @@ func (_m *DB) EXPECT() *DB_Expecter { return &DB_Expecter{mock: &_m.Mock} } +// AddPolicy provides a mock function with given fields: ctx, creatorID, policy +func (_m *DB) AddPolicy(ctx context.Context, creatorID string, policy string) (client.AddPolicyResult, error) { + ret := _m.Called(ctx, creatorID, policy) + + var r0 client.AddPolicyResult + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (client.AddPolicyResult, error)); ok { + return rf(ctx, creatorID, policy) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) client.AddPolicyResult); ok { + r0 = rf(ctx, creatorID, policy) + } else { + r0 = ret.Get(0).(client.AddPolicyResult) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, creatorID, policy) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DB_AddPolicy_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddPolicy' +type DB_AddPolicy_Call struct { + *mock.Call +} + +// AddPolicy is a helper method to define mock.On call +// - ctx context.Context +// - creatorID string +// - policy string +func (_e *DB_Expecter) AddPolicy(ctx interface{}, creatorID interface{}, policy interface{}) *DB_AddPolicy_Call { + return &DB_AddPolicy_Call{Call: _e.mock.On("AddPolicy", ctx, creatorID, policy)} +} + +func (_c *DB_AddPolicy_Call) Run(run func(ctx context.Context, creatorID string, policy string)) *DB_AddPolicy_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string)) + }) + return _c +} + +func (_c *DB_AddPolicy_Call) Return(_a0 client.AddPolicyResult, _a1 error) *DB_AddPolicy_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *DB_AddPolicy_Call) RunAndReturn(run func(context.Context, string, string) (client.AddPolicyResult, error)) *DB_AddPolicy_Call { + _c.Call.Return(run) + return _c +} + // AddSchema provides a mock function with given fields: _a0, _a1 func (_m *DB) AddSchema(_a0 context.Context, _a1 string) ([]client.CollectionDescription, error) { ret := _m.Called(_a0, _a1) @@ -346,13 +400,13 @@ func (_c *DB_Events_Call) RunAndReturn(run func() events.Events) *DB_Events_Call return _c } -// ExecRequest provides a mock function with given fields: _a0, _a1 -func (_m *DB) ExecRequest(_a0 context.Context, _a1 string) *client.RequestResult { - ret := _m.Called(_a0, _a1) +// ExecRequest provides a mock function with given fields: ctx, identity, request +func (_m *DB) ExecRequest(ctx context.Context, identity immutable.Option[string], request string) *client.RequestResult { + ret := _m.Called(ctx, identity, request) var r0 *client.RequestResult - if rf, ok := ret.Get(0).(func(context.Context, string) *client.RequestResult); ok { - r0 = rf(_a0, _a1) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], string) *client.RequestResult); ok { + r0 = rf(ctx, identity, request) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*client.RequestResult) @@ -368,15 +422,16 @@ type DB_ExecRequest_Call struct { } // ExecRequest is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string -func (_e *DB_Expecter) ExecRequest(_a0 interface{}, _a1 interface{}) *DB_ExecRequest_Call { - return &DB_ExecRequest_Call{Call: _e.mock.On("ExecRequest", _a0, _a1)} +// - ctx context.Context +// - identity immutable.Option[string] +// - request string +func (_e *DB_Expecter) ExecRequest(ctx interface{}, identity interface{}, request interface{}) *DB_ExecRequest_Call { + return &DB_ExecRequest_Call{Call: _e.mock.On("ExecRequest", ctx, identity, request)} } -func (_c *DB_ExecRequest_Call) Run(run func(_a0 context.Context, _a1 string)) *DB_ExecRequest_Call { +func (_c *DB_ExecRequest_Call) Run(run func(ctx context.Context, identity immutable.Option[string], request string)) *DB_ExecRequest_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(string)) }) return _c } @@ -386,7 +441,7 @@ func (_c *DB_ExecRequest_Call) Return(_a0 *client.RequestResult) *DB_ExecRequest return _c } -func (_c *DB_ExecRequest_Call) RunAndReturn(run func(context.Context, string) *client.RequestResult) *DB_ExecRequest_Call { +func (_c *DB_ExecRequest_Call) RunAndReturn(run func(context.Context, immutable.Option[string], string) *client.RequestResult) *DB_ExecRequest_Call { _c.Call.Return(run) return _c } diff --git a/client/policy.go b/client/policy.go new file mode 100644 index 0000000000..5b877696c2 --- /dev/null +++ b/client/policy.go @@ -0,0 +1,31 @@ +// 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 client + +// PolicyDescription describes a policy which is made up of a valid policyID that is +// registered with acp and has a valid DPI compliant resource name that also +// exists on that policy, the description is already validated. +type PolicyDescription struct { + // ID is the local policyID when using local acp, and global policyID when + // using remote acp with sourcehub. This identifier is externally managed + // by the acp system. + ID string + + // ResourceName is the name of the corresponding resource within the policy. + ResourceName string +} + +// AddPolicyResult wraps the result of successfully adding/registering a Policy. +type AddPolicyResult struct { + // PolicyID is the unique identifier returned by the acp system, + // upon successful creation of a policy. + PolicyID string +} diff --git a/core/key.go b/core/key.go index 5c569f310d..69b19efb6e 100644 --- a/core/key.go +++ b/core/key.go @@ -43,7 +43,8 @@ const ( ) const ( - COLLECTION = "/collection/id" + COLLECTION = "collection" + COLLECTION_ID = "/collection/id" COLLECTION_NAME = "/collection/name" COLLECTION_SCHEMA_VERSION = "/collection/version" COLLECTION_INDEX = "/collection/index" @@ -326,7 +327,7 @@ func NewCollectionIndexKey(colID immutable.Option[uint32], indexName string) Col // Where [IndexName] might be omitted. Anything else will return an error. func NewCollectionIndexKeyFromString(key string) (CollectionIndexKey, error) { keyArr := strings.Split(key, "/") - if len(keyArr) < 4 || len(keyArr) > 5 || keyArr[1] != "collection" || keyArr[2] != "index" { + if len(keyArr) < 4 || len(keyArr) > 5 || keyArr[1] != COLLECTION || keyArr[2] != "index" { return CollectionIndexKey{}, ErrInvalidKey } @@ -564,7 +565,7 @@ func (k PrimaryDataStoreKey) ToString() string { } func (k CollectionKey) ToString() string { - return fmt.Sprintf("%s/%s", COLLECTION, strconv.Itoa(int(k.CollectionID))) + return fmt.Sprintf("%s/%s", COLLECTION_ID, strconv.Itoa(int(k.CollectionID))) } func (k CollectionKey) Bytes() []byte { diff --git a/core/parser.go b/core/parser.go index 05a90d0526..619f3fd1c2 100644 --- a/core/parser.go +++ b/core/parser.go @@ -51,6 +51,10 @@ type Parser interface { NewFilterFromString(collectionType string, body string) (immutable.Option[request.Filter], error) // ParseSDL parses an SDL string into a set of collection descriptions. + // + // The parsing should validate the syntax, but not validate what that syntax expresses + // is valid or not, i.e. we don't want the parser to make remote calls to verify the + // policy description is valid or not (that is the callers responsiblity). ParseSDL(ctx context.Context, schemaString string) ([]client.CollectionDefinition, error) // Adds the given schema to this parser's model. diff --git a/db/backup.go b/db/backup.go index 81d62f78b9..2d3b824be1 100644 --- a/db/backup.go +++ b/db/backup.go @@ -17,6 +17,7 @@ import ( "fmt" "os" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/client/request" "github.com/sourcenetwork/defradb/datastore" @@ -90,7 +91,8 @@ func (db *db) basicImport(ctx context.Context, txn datastore.Txn, filepath strin return NewErrDocFromMap(err) } - err = col.WithTxn(txn).Create(ctx, doc) + // TODO-ACP: https://github.com/sourcenetwork/defradb/issues/2430 - Add identity ability to backup + err = col.WithTxn(txn).Create(ctx, acpIdentity.NoIdentity, doc) if err != nil { return NewErrDocCreate(err) } @@ -101,7 +103,8 @@ func (db *db) basicImport(ctx context.Context, txn datastore.Txn, filepath strin if err != nil { return NewErrDocUpdate(err) } - err = col.WithTxn(txn).Update(ctx, doc) + // TODO-ACP: https://github.com/sourcenetwork/defradb/issues/2430 - Add identity ability to backup + err = col.WithTxn(txn).Update(ctx, acpIdentity.NoIdentity, doc) if err != nil { return NewErrDocUpdate(err) } @@ -189,7 +192,8 @@ func (db *db) basicExport(ctx context.Context, txn datastore.Txn, config *client return err } colTxn := col.WithTxn(txn) - docIDsCh, err := colTxn.GetAllDocIDs(ctx) + // TODO-ACP: https://github.com/sourcenetwork/defradb/issues/2430 - Add identity ability to export + docIDsCh, err := colTxn.GetAllDocIDs(ctx, acpIdentity.NoIdentity) if err != nil { return err } @@ -205,7 +209,8 @@ func (db *db) basicExport(ctx context.Context, txn datastore.Txn, config *client return err } } - doc, err := colTxn.Get(ctx, docResultWithID.ID, false) + // TODO-ACP: https://github.com/sourcenetwork/defradb/issues/2430 - Add identity ability to export + doc, err := colTxn.Get(ctx, acpIdentity.NoIdentity, docResultWithID.ID, false) if err != nil { return err } @@ -237,7 +242,8 @@ func (db *db) basicExport(ctx context.Context, txn datastore.Txn, config *client if err != nil { return err } - foreignDoc, err := foreignCol.Get(ctx, foreignDocID, false) + // TODO-ACP: https://github.com/sourcenetwork/defradb/issues/2430 + foreignDoc, err := foreignCol.Get(ctx, acpIdentity.NoIdentity, foreignDocID, false) if err != nil { err := doc.Set(field.Name+request.RelatedObjectID, nil) if err != nil { diff --git a/db/backup_test.go b/db/backup_test.go index 093b1a1a3f..6a9eab3cc9 100644 --- a/db/backup_test.go +++ b/db/backup_test.go @@ -18,6 +18,7 @@ import ( "github.com/stretchr/testify/require" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" ) @@ -46,10 +47,10 @@ func TestBasicExport_WithNormalFormatting_NoError(t *testing.T) { doc2, err := client.NewDocFromJSON([]byte(`{"name": "Bob", "age": 40}`), col1.Schema()) require.NoError(t, err) - err = col1.Create(ctx, doc1) + err = col1.Create(ctx, acpIdentity.NoIdentity, doc1) require.NoError(t, err) - err = col1.Create(ctx, doc2) + err = col1.Create(ctx, acpIdentity.NoIdentity, doc2) require.NoError(t, err) col2, err := db.GetCollectionByName(ctx, "Address") @@ -58,7 +59,7 @@ func TestBasicExport_WithNormalFormatting_NoError(t *testing.T) { doc3, err := client.NewDocFromJSON([]byte(`{"street": "101 Maple St", "city": "Toronto"}`), col2.Schema()) require.NoError(t, err) - err = col2.Create(ctx, doc3) + err = col2.Create(ctx, acpIdentity.NoIdentity, doc3) require.NoError(t, err) txn, err := db.NewTxn(ctx, true) @@ -108,10 +109,10 @@ func TestBasicExport_WithPrettyFormatting_NoError(t *testing.T) { doc2, err := client.NewDocFromJSON([]byte(`{"name": "Bob", "age": 40}`), col1.Schema()) require.NoError(t, err) - err = col1.Create(ctx, doc1) + err = col1.Create(ctx, acpIdentity.NoIdentity, doc1) require.NoError(t, err) - err = col1.Create(ctx, doc2) + err = col1.Create(ctx, acpIdentity.NoIdentity, doc2) require.NoError(t, err) col2, err := db.GetCollectionByName(ctx, "Address") @@ -120,7 +121,7 @@ func TestBasicExport_WithPrettyFormatting_NoError(t *testing.T) { doc3, err := client.NewDocFromJSON([]byte(`{"street": "101 Maple St", "city": "Toronto"}`), col2.Schema()) require.NoError(t, err) - err = col2.Create(ctx, doc3) + err = col2.Create(ctx, acpIdentity.NoIdentity, doc3) require.NoError(t, err) txn, err := db.NewTxn(ctx, true) @@ -170,10 +171,10 @@ func TestBasicExport_WithSingleCollection_NoError(t *testing.T) { doc2, err := client.NewDocFromJSON([]byte(`{"name": "Bob", "age": 40}`), col1.Schema()) require.NoError(t, err) - err = col1.Create(ctx, doc1) + err = col1.Create(ctx, acpIdentity.NoIdentity, doc1) require.NoError(t, err) - err = col1.Create(ctx, doc2) + err = col1.Create(ctx, acpIdentity.NoIdentity, doc2) require.NoError(t, err) col2, err := db.GetCollectionByName(ctx, "Address") @@ -182,7 +183,7 @@ func TestBasicExport_WithSingleCollection_NoError(t *testing.T) { doc3, err := client.NewDocFromJSON([]byte(`{"street": "101 Maple St", "city": "Toronto"}`), col2.Schema()) require.NoError(t, err) - err = col2.Create(ctx, doc3) + err = col2.Create(ctx, acpIdentity.NoIdentity, doc3) require.NoError(t, err) txn, err := db.NewTxn(ctx, true) @@ -233,10 +234,10 @@ func TestBasicExport_WithMultipleCollectionsAndUpdate_NoError(t *testing.T) { doc2, err := client.NewDocFromJSON([]byte(`{"name": "Bob", "age": 31}`), col1.Schema()) require.NoError(t, err) - err = col1.Create(ctx, doc1) + err = col1.Create(ctx, acpIdentity.NoIdentity, doc1) require.NoError(t, err) - err = col1.Create(ctx, doc2) + err = col1.Create(ctx, acpIdentity.NoIdentity, doc2) require.NoError(t, err) col2, err := db.GetCollectionByName(ctx, "Book") @@ -248,15 +249,15 @@ func TestBasicExport_WithMultipleCollectionsAndUpdate_NoError(t *testing.T) { doc4, err := client.NewDocFromJSON([]byte(`{"name": "Game of chains", "author": "bae-e933420a-988a-56f8-8952-6c245aebd519"}`), col2.Schema()) require.NoError(t, err) - err = col2.Create(ctx, doc3) + err = col2.Create(ctx, acpIdentity.NoIdentity, doc3) require.NoError(t, err) - err = col2.Create(ctx, doc4) + err = col2.Create(ctx, acpIdentity.NoIdentity, doc4) require.NoError(t, err) err = doc1.Set("age", 31) require.NoError(t, err) - err = col1.Update(ctx, doc1) + err = col1.Update(ctx, acpIdentity.NoIdentity, doc1) require.NoError(t, err) txn, err := db.NewTxn(ctx, true) @@ -306,10 +307,10 @@ func TestBasicExport_EnsureFileOverwrite_NoError(t *testing.T) { doc2, err := client.NewDocFromJSON([]byte(`{"name": "Bob", "age": 40}`), col1.Schema()) require.NoError(t, err) - err = col1.Create(ctx, doc1) + err = col1.Create(ctx, acpIdentity.NoIdentity, doc1) require.NoError(t, err) - err = col1.Create(ctx, doc2) + err = col1.Create(ctx, acpIdentity.NoIdentity, doc2) require.NoError(t, err) col2, err := db.GetCollectionByName(ctx, "Address") @@ -318,7 +319,7 @@ func TestBasicExport_EnsureFileOverwrite_NoError(t *testing.T) { doc3, err := client.NewDocFromJSON([]byte(`{"street": "101 Maple St", "city": "Toronto"}`), col2.Schema()) require.NoError(t, err) - err = col2.Create(ctx, doc3) + err = col2.Create(ctx, acpIdentity.NoIdentity, doc3) require.NoError(t, err) txn, err := db.NewTxn(ctx, true) @@ -392,7 +393,7 @@ func TestBasicImport_WithMultipleCollectionsAndObjects_NoError(t *testing.T) { key1, err := client.NewDocIDFromString("bae-8096f2c1-ea4c-5226-8ba5-17fc4b68ac1f") require.NoError(t, err) - _, err = col1.Get(ctx, key1, false) + _, err = col1.Get(ctx, acpIdentity.NoIdentity, key1, false) require.NoError(t, err) col2, err := db.getCollectionByName(ctx, txn, "User") @@ -400,12 +401,12 @@ func TestBasicImport_WithMultipleCollectionsAndObjects_NoError(t *testing.T) { key2, err := client.NewDocIDFromString("bae-b94880d1-e6d2-542f-b9e0-5a369fafd0df") require.NoError(t, err) - _, err = col2.Get(ctx, key2, false) + _, err = col2.Get(ctx, acpIdentity.NoIdentity, key2, false) require.NoError(t, err) key3, err := client.NewDocIDFromString("bae-e933420a-988a-56f8-8952-6c245aebd519") require.NoError(t, err) - _, err = col2.Get(ctx, key3, false) + _, err = col2.Get(ctx, acpIdentity.NoIdentity, key3, false) require.NoError(t, err) } diff --git a/db/collection.go b/db/collection.go index 2ad2cf2ca5..d7364df3b2 100644 --- a/db/collection.go +++ b/db/collection.go @@ -27,6 +27,7 @@ import ( "github.com/lens-vm/lens/host-go/config/model" "github.com/sourcenetwork/immutable" + "github.com/sourcenetwork/defradb/acp" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/client/request" "github.com/sourcenetwork/defradb/core" @@ -161,6 +162,7 @@ func (db *db) createCollection( } col := db.newCollection(desc, schema) + for _, index := range desc.Indexes { if _, err := col.createIndex(ctx, txn, index); err != nil { return nil, err @@ -170,6 +172,37 @@ func (db *db) createCollection( return db.getCollectionByID(ctx, txn, desc.ID) } +// validateCollectionDefinitionPolicyDesc validates that the policy definition is valid, beyond syntax. +// +// Ensures that the information within the policy definition makes sense, +// this function might also make relevant remote calls using the acp system. +func (db *db) validateCollectionDefinitionPolicyDesc( + ctx context.Context, + policyDesc immutable.Option[client.PolicyDescription], +) error { + if !policyDesc.HasValue() { + // No policy validation needed, whether acp exists or not doesn't matter. + return nil + } + + // If there is a policy specified, but the database does not have + // acp enabled/available return an error, database must have an acp available + // to enable access control (inorder to adhere to the policy specified). + if !db.acp.HasValue() { + return ErrCanNotHavePolicyWithoutACP + } + + // If we have the policy specified on the collection, and acp is available/enabled, + // then using the acp system we need to ensure the policy id specified + // actually exists as a policy, and the resource name exists on that policy + // and that the resource is a valid DPI. + return db.acp.Value().ValidateResourceExistsOnValidDPI( + ctx, + policyDesc.Value().ID, + policyDesc.Value().ResourceName, + ) +} + // updateSchema updates the persisted schema description matching the name of the given // description, to the values in the given description. // @@ -627,6 +660,7 @@ var patchCollectionValidators = []func( validateSourcesNotRedefined, validateIndexesNotModified, validateFieldsNotModified, + validatePolicyNotModified, validateIDNotZero, validateIDUnique, validateIDExists, @@ -768,6 +802,25 @@ func validateFieldsNotModified( return nil } +func validatePolicyNotModified( + oldColsByID map[uint32]client.CollectionDescription, + newColsByID map[uint32]client.CollectionDescription, +) error { + for _, newCol := range newColsByID { + oldCol, ok := oldColsByID[newCol.ID] + if !ok { + continue + } + + // DeepEqual is temporary, as this validation is temporary + if !reflect.DeepEqual(oldCol.Policy, newCol.Policy) { + return NewErrCollectionPolicyCannotBeMutated(newCol.ID) + } + } + + return nil +} + func validateIDNotZero( oldColsByID map[uint32]client.CollectionDescription, newColsByID map[uint32]client.CollectionDescription, @@ -1025,6 +1078,7 @@ func (db *db) getCollectionByID(ctx context.Context, txn datastore.Txn, id uint3 } collection := db.newCollection(col, schema) + err = collection.loadIndexes(ctx, txn) if err != nil { return nil, err @@ -1182,17 +1236,21 @@ func (db *db) getAllActiveDefinitions(ctx context.Context, txn datastore.Txn) ([ // // @todo: We probably need a lock on the collection for this kind of op since // it hits every key and will cause Tx conflicts for concurrent Txs -func (c *collection) GetAllDocIDs(ctx context.Context) (<-chan client.DocIDResult, error) { +func (c *collection) GetAllDocIDs( + ctx context.Context, + identity immutable.Option[string], +) (<-chan client.DocIDResult, error) { txn, err := c.getTxn(ctx, true) if err != nil { return nil, err } - return c.getAllDocIDsChan(ctx, txn) + return c.getAllDocIDsChan(ctx, identity, txn) } func (c *collection) getAllDocIDsChan( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, ) (<-chan client.DocIDResult, error) { prefix := core.PrimaryDataStoreKey{ // empty path for all keys prefix @@ -1235,12 +1293,29 @@ func (c *collection) getAllDocIDsChan( docID, err := client.NewDocIDFromString(rawDocID) if err != nil { resCh <- client.DocIDResult{ - Err: res.Error, + Err: err, + } + return + } + + canRead, err := c.checkAccessOfDocWithACP( + ctx, + identity, + acp.ReadPermission, + docID.String(), + ) + + if err != nil { + resCh <- client.DocIDResult{ + Err: err, } return } - resCh <- client.DocIDResult{ - ID: docID, + + if canRead { + resCh <- client.DocIDResult{ + ID: docID, + } } } }() @@ -1290,23 +1365,32 @@ func (c *collection) WithTxn(txn datastore.Txn) client.Collection { // Create a new document. // Will verify the DocID/CID to ensure that the new document is correctly formatted. -func (c *collection) Create(ctx context.Context, doc *client.Document) error { +func (c *collection) Create( + ctx context.Context, + identity immutable.Option[string], + doc *client.Document, +) error { txn, err := c.getTxn(ctx, false) if err != nil { return err } defer c.discardImplicitTxn(ctx, txn) - err = c.create(ctx, txn, doc) + err = c.create(ctx, identity, txn, doc) if err != nil { return err } + return c.commitImplicitTxn(ctx, txn) } // CreateMany creates a collection of documents at once. // Will verify the DocID/CID to ensure that the new documents are correctly formatted. -func (c *collection) CreateMany(ctx context.Context, docs []*client.Document) error { +func (c *collection) CreateMany( + ctx context.Context, + identity immutable.Option[string], + docs []*client.Document, +) error { txn, err := c.getTxn(ctx, false) if err != nil { return err @@ -1314,7 +1398,7 @@ func (c *collection) CreateMany(ctx context.Context, docs []*client.Document) er defer c.discardImplicitTxn(ctx, txn) for _, doc := range docs { - err = c.create(ctx, txn, doc) + err = c.create(ctx, identity, txn, doc) if err != nil { return err } @@ -1338,14 +1422,19 @@ func (c *collection) getDocIDAndPrimaryKeyFromDoc( return docID, primaryKey, nil } -func (c *collection) create(ctx context.Context, txn datastore.Txn, doc *client.Document) error { +func (c *collection) create( + ctx context.Context, + identity immutable.Option[string], + txn datastore.Txn, + doc *client.Document, +) error { docID, primaryKey, err := c.getDocIDAndPrimaryKeyFromDoc(doc) if err != nil { return err } // check if doc already exists - exists, isDeleted, err := c.exists(ctx, txn, primaryKey) + exists, isDeleted, err := c.exists(ctx, identity, txn, primaryKey) if err != nil { return err } @@ -1366,18 +1455,27 @@ func (c *collection) create(ctx context.Context, txn datastore.Txn, doc *client. } // write data to DB via MerkleClock/CRDT - _, err = c.save(ctx, txn, doc, true) + _, err = c.save(ctx, identity, txn, doc, true) if err != nil { return err } - return c.indexNewDoc(ctx, txn, doc) + err = c.indexNewDoc(ctx, txn, doc) + if err != nil { + return err + } + + return c.registerDocWithACP(ctx, identity, doc.ID().String()) } // Update an existing document with the new values. // Any field that needs to be removed or cleared should call doc.Clear(field) before. // Any field that is nil/empty that hasn't called Clear will be ignored. -func (c *collection) Update(ctx context.Context, doc *client.Document) error { +func (c *collection) Update( + ctx context.Context, + identity immutable.Option[string], + doc *client.Document, +) error { txn, err := c.getTxn(ctx, false) if err != nil { return err @@ -1385,18 +1483,18 @@ func (c *collection) Update(ctx context.Context, doc *client.Document) error { defer c.discardImplicitTxn(ctx, txn) primaryKey := c.getPrimaryKeyFromDocID(doc.ID()) - exists, isDeleted, err := c.exists(ctx, txn, primaryKey) + exists, isDeleted, err := c.exists(ctx, identity, txn, primaryKey) if err != nil { return err } if !exists { - return client.ErrDocumentNotFound + return client.ErrDocumentNotFoundOrNotAuthorized } if isDeleted { return NewErrDocumentDeleted(primaryKey.DocID) } - err = c.update(ctx, txn, doc) + err = c.update(ctx, identity, txn, doc) if err != nil { return err } @@ -1409,8 +1507,27 @@ func (c *collection) Update(ctx context.Context, doc *client.Document) error { // or, just update everything regardless. // Should probably be smart about the update due to the MerkleCRDT overhead, shouldn't // add to the bloat. -func (c *collection) update(ctx context.Context, txn datastore.Txn, doc *client.Document) error { - _, err := c.save(ctx, txn, doc, false) +func (c *collection) update( + ctx context.Context, + identity immutable.Option[string], + txn datastore.Txn, + doc *client.Document, +) error { + // Stop the update if the correct permissions aren't there. + canUpdate, err := c.checkAccessOfDocWithACP( + ctx, + identity, + acp.WritePermission, + doc.ID().String(), + ) + if err != nil { + return err + } + if !canUpdate { + return client.ErrDocumentNotFoundOrNotAuthorized + } + + _, err = c.save(ctx, identity, txn, doc, false) if err != nil { return err } @@ -1419,7 +1536,11 @@ func (c *collection) update(ctx context.Context, txn datastore.Txn, doc *client. // Save a document into the db. // Either by creating a new document or by updating an existing one -func (c *collection) Save(ctx context.Context, doc *client.Document) error { +func (c *collection) Save( + ctx context.Context, + identity immutable.Option[string], + doc *client.Document, +) error { txn, err := c.getTxn(ctx, false) if err != nil { return err @@ -1428,7 +1549,7 @@ func (c *collection) Save(ctx context.Context, doc *client.Document) error { // Check if document already exists with primary DS key. primaryKey := c.getPrimaryKeyFromDocID(doc.ID()) - exists, isDeleted, err := c.exists(ctx, txn, primaryKey) + exists, isDeleted, err := c.exists(ctx, identity, txn, primaryKey) if err != nil { return err } @@ -1438,9 +1559,9 @@ func (c *collection) Save(ctx context.Context, doc *client.Document) error { } if exists { - err = c.update(ctx, txn, doc) + err = c.update(ctx, identity, txn, doc) } else { - err = c.create(ctx, txn, doc) + err = c.create(ctx, identity, txn, doc) } if err != nil { return err @@ -1449,8 +1570,12 @@ func (c *collection) Save(ctx context.Context, doc *client.Document) error { return c.commitImplicitTxn(ctx, txn) } +// save saves the document state. save MUST not be called outside the `c.create` +// and `c.update` methods as we wrap the acp logic within those methods. Calling +// save elsewhere could cause the omission of acp checks. func (c *collection) save( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, doc *client.Document, isCreate bool, @@ -1503,7 +1628,15 @@ func (c *collection) save( if isSecondaryRelationID { primaryId := val.Value().(string) - err = c.patchPrimaryDoc(ctx, txn, c.Name().Value(), relationFieldDescription, primaryKey.DocID, primaryId) + err = c.patchPrimaryDoc( + ctx, + identity, + txn, + c.Name().Value(), + relationFieldDescription, + primaryKey.DocID, + primaryId, + ) if err != nil { return cid.Undef, err } @@ -1513,7 +1646,14 @@ func (c *collection) save( continue } - err = c.validateOneToOneLinkDoesntAlreadyExist(ctx, txn, doc.ID().String(), fieldDescription, val.Value()) + err = c.validateOneToOneLinkDoesntAlreadyExist( + ctx, + identity, + txn, + doc.ID().String(), + fieldDescription, + val.Value(), + ) if err != nil { return cid.Undef, err } @@ -1579,6 +1719,7 @@ func (c *collection) save( func (c *collection) validateOneToOneLinkDoesntAlreadyExist( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, docID string, fieldDescription client.FieldDefinition, @@ -1625,7 +1766,7 @@ func (c *collection) validateOneToOneLinkDoesntAlreadyExist( fieldDescription.Name, value, ) - selectionPlan, err := c.makeSelectionPlan(ctx, txn, filter) + selectionPlan, err := c.makeSelectionPlan(ctx, identity, txn, filter) if err != nil { return err } @@ -1677,7 +1818,11 @@ func (c *collection) validateOneToOneLinkDoesntAlreadyExist( // otherwise will return false, along with an error, if it cannot. // If the document doesn't exist, then it will return false, and a ErrDocumentNotFound error. // This operation will all state relating to the given DocID. This includes data, block, and head storage. -func (c *collection) Delete(ctx context.Context, docID client.DocID) (bool, error) { +func (c *collection) Delete( + ctx context.Context, + identity immutable.Option[string], + docID client.DocID, +) (bool, error) { txn, err := c.getTxn(ctx, false) if err != nil { return false, err @@ -1685,18 +1830,8 @@ func (c *collection) Delete(ctx context.Context, docID client.DocID) (bool, erro defer c.discardImplicitTxn(ctx, txn) primaryKey := c.getPrimaryKeyFromDocID(docID) - exists, isDeleted, err := c.exists(ctx, txn, primaryKey) - if err != nil { - return false, err - } - if !exists || isDeleted { - return false, client.ErrDocumentNotFound - } - if isDeleted { - return false, NewErrDocumentDeleted(primaryKey.DocID) - } - err = c.applyDelete(ctx, txn, primaryKey) + err = c.applyDelete(ctx, identity, txn, primaryKey) if err != nil { return false, err } @@ -1704,7 +1839,11 @@ func (c *collection) Delete(ctx context.Context, docID client.DocID) (bool, erro } // Exists checks if a given document exists with supplied DocID. -func (c *collection) Exists(ctx context.Context, docID client.DocID) (bool, error) { +func (c *collection) Exists( + ctx context.Context, + identity immutable.Option[string], + docID client.DocID, +) (bool, error) { txn, err := c.getTxn(ctx, false) if err != nil { return false, err @@ -1712,7 +1851,7 @@ func (c *collection) Exists(ctx context.Context, docID client.DocID) (bool, erro defer c.discardImplicitTxn(ctx, txn) primaryKey := c.getPrimaryKeyFromDocID(docID) - exists, isDeleted, err := c.exists(ctx, txn, primaryKey) + exists, isDeleted, err := c.exists(ctx, identity, txn, primaryKey) if err != nil && !errors.Is(err, ds.ErrNotFound) { return false, err } @@ -1722,9 +1861,22 @@ func (c *collection) Exists(ctx context.Context, docID client.DocID) (bool, erro // check if a document exists with the given primary key func (c *collection) exists( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, primaryKey core.PrimaryDataStoreKey, ) (exists bool, isDeleted bool, err error) { + canRead, err := c.checkAccessOfDocWithACP( + ctx, + identity, + acp.ReadPermission, + primaryKey.DocID, + ) + if err != nil { + return false, false, err + } else if !canRead { + return false, false, nil + } + val, err := txn.Datastore().Get(ctx, primaryKey.ToDS()) if err != nil && errors.Is(err, ds.ErrNotFound) { return false, false, nil @@ -1738,6 +1890,10 @@ func (c *collection) exists( return true, false, nil } +// saveCompositeToMerkleCRDT saves the composite to the merkle CRDT. +// saveCompositeToMerkleCRDT MUST not be called outside the `c.save` +// and `c.applyDelete` methods as we wrap the acp logic around those methods. +// Calling it elsewhere could cause the omission of acp checks. func (c *collection) saveCompositeToMerkleCRDT( ctx context.Context, txn datastore.Txn, diff --git a/db/collection_acp.go b/db/collection_acp.go new file mode 100644 index 0000000000..ccb4f3ae32 --- /dev/null +++ b/db/collection_acp.go @@ -0,0 +1,71 @@ +// 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 db + +import ( + "context" + + "github.com/sourcenetwork/immutable" + + "github.com/sourcenetwork/defradb/acp" + "github.com/sourcenetwork/defradb/db/permission" +) + +// registerDocWithACP handles the registration of the document with acp. +// The registering is done at document creation on the collection. +// +// According to our access logic we have these components to worry about: +// (1) the request is permissioned (has an identity signature), +// (2) the collection is permissioned (has a policy), +// (3) acp is available (acp is enabled). +// +// The document is only registered if all (1) (2) and (3) are true. +// +// Otherwise, nothing is registered with the acp system. +func (c *collection) registerDocWithACP( + ctx context.Context, + identity immutable.Option[string], + docID string, +) error { + // If acp is not available, then no document is registered. + if !c.db.acp.HasValue() { + return nil + } + + return permission.RegisterDocOnCollectionWithACP( + ctx, + identity, + c.db.acp.Value(), + c, + docID, + ) +} + +func (c *collection) checkAccessOfDocWithACP( + ctx context.Context, + identity immutable.Option[string], + dpiPermission acp.DPIPermission, + docID string, +) (bool, error) { + // If acp is not available, then we have unrestricted access. + if !c.db.acp.HasValue() { + return true, nil + } + + return permission.CheckAccessOfDocOnCollectionWithACP( + ctx, + identity, + c.db.acp.Value(), + c, + dpiPermission, + docID, + ) +} diff --git a/db/collection_delete.go b/db/collection_delete.go index 371c454532..984cd27a21 100644 --- a/db/collection_delete.go +++ b/db/collection_delete.go @@ -13,6 +13,9 @@ package db import ( "context" + "github.com/sourcenetwork/immutable" + + "github.com/sourcenetwork/defradb/acp" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/client/request" "github.com/sourcenetwork/defradb/core" @@ -30,15 +33,16 @@ import ( // Eg: DeleteWithFilter or DeleteWithDocID func (c *collection) DeleteWith( ctx context.Context, + identity immutable.Option[string], target any, ) (*client.DeleteResult, error) { switch t := target.(type) { case string, map[string]any, *request.Filter: - return c.DeleteWithFilter(ctx, t) + return c.DeleteWithFilter(ctx, identity, t) case client.DocID: - return c.DeleteWithDocID(ctx, t) + return c.DeleteWithDocID(ctx, identity, t) case []client.DocID: - return c.DeleteWithDocIDs(ctx, t) + return c.DeleteWithDocIDs(ctx, identity, t) default: return nil, client.ErrInvalidDeleteTarget } @@ -47,6 +51,7 @@ func (c *collection) DeleteWith( // DeleteWithDocID deletes using a DocID to target a single document for delete. func (c *collection) DeleteWithDocID( ctx context.Context, + identity immutable.Option[string], docID client.DocID, ) (*client.DeleteResult, error) { txn, err := c.getTxn(ctx, false) @@ -57,7 +62,7 @@ func (c *collection) DeleteWithDocID( defer c.discardImplicitTxn(ctx, txn) dsKey := c.getPrimaryKeyFromDocID(docID) - res, err := c.deleteWithKey(ctx, txn, dsKey) + res, err := c.deleteWithKey(ctx, identity, txn, dsKey) if err != nil { return nil, err } @@ -68,6 +73,7 @@ func (c *collection) DeleteWithDocID( // DeleteWithDocIDs is the same as DeleteWithDocID but accepts multiple DocIDs as a slice. func (c *collection) DeleteWithDocIDs( ctx context.Context, + identity immutable.Option[string], docIDs []client.DocID, ) (*client.DeleteResult, error) { txn, err := c.getTxn(ctx, false) @@ -77,7 +83,7 @@ func (c *collection) DeleteWithDocIDs( defer c.discardImplicitTxn(ctx, txn) - res, err := c.deleteWithIDs(ctx, txn, docIDs, client.Deleted) + res, err := c.deleteWithIDs(ctx, identity, txn, docIDs, client.Deleted) if err != nil { return nil, err } @@ -88,6 +94,7 @@ func (c *collection) DeleteWithDocIDs( // DeleteWithFilter deletes using a filter to target documents for delete. func (c *collection) DeleteWithFilter( ctx context.Context, + identity immutable.Option[string], filter any, ) (*client.DeleteResult, error) { txn, err := c.getTxn(ctx, false) @@ -97,7 +104,7 @@ func (c *collection) DeleteWithFilter( defer c.discardImplicitTxn(ctx, txn) - res, err := c.deleteWithFilter(ctx, txn, filter, client.Deleted) + res, err := c.deleteWithFilter(ctx, identity, txn, filter, client.Deleted) if err != nil { return nil, err } @@ -107,12 +114,13 @@ func (c *collection) DeleteWithFilter( func (c *collection) deleteWithKey( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, key core.PrimaryDataStoreKey, ) (*client.DeleteResult, error) { // Check the key we have been given to delete with actually has a corresponding // document (i.e. document actually exists in the collection). - err := c.applyDelete(ctx, txn, key) + err := c.applyDelete(ctx, identity, txn, key) if err != nil { return nil, err } @@ -128,6 +136,7 @@ func (c *collection) deleteWithKey( func (c *collection) deleteWithIDs( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, docIDs []client.DocID, _ client.DocumentStatus, @@ -140,7 +149,7 @@ func (c *collection) deleteWithIDs( primaryKey := c.getPrimaryKeyFromDocID(docID) // Apply the function that will perform the full deletion of this document. - err := c.applyDelete(ctx, txn, primaryKey) + err := c.applyDelete(ctx, identity, txn, primaryKey) if err != nil { return nil, err } @@ -157,12 +166,13 @@ func (c *collection) deleteWithIDs( func (c *collection) deleteWithFilter( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, filter any, _ client.DocumentStatus, ) (*client.DeleteResult, error) { // Make a selection plan that will scan through only the documents with matching filter. - selectionPlan, err := c.makeSelectionPlan(ctx, txn, filter) + selectionPlan, err := c.makeSelectionPlan(ctx, identity, txn, filter) if err != nil { return nil, err } @@ -210,7 +220,7 @@ func (c *collection) deleteWithFilter( } // Delete the document that is associated with this DS key we got from the filter. - err = c.applyDelete(ctx, txn, primaryKey) + err = c.applyDelete(ctx, identity, txn, primaryKey) if err != nil { return nil, err } @@ -226,20 +236,37 @@ func (c *collection) deleteWithFilter( func (c *collection) applyDelete( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, primaryKey core.PrimaryDataStoreKey, ) error { - found, isDeleted, err := c.exists(ctx, txn, primaryKey) + // Must also have read permission to delete, inorder to check if document exists. + found, isDeleted, err := c.exists(ctx, identity, txn, primaryKey) if err != nil { return err } if !found { - return client.ErrDocumentNotFound + return client.ErrDocumentNotFoundOrNotAuthorized } if isDeleted { return NewErrDocumentDeleted(primaryKey.DocID) } + // Stop deletion of document if the correct permissions aren't there. + canDelete, err := c.checkAccessOfDocWithACP( + ctx, + identity, + acp.WritePermission, + primaryKey.DocID, + ) + + if err != nil { + return err + } + if !canDelete { + return client.ErrDocumentNotFoundOrNotAuthorized + } + dsKey := primaryKey.ToDataStoreKey() headset := clock.NewHeadSet( diff --git a/db/collection_get.go b/db/collection_get.go index cf245fc678..16d5bd4711 100644 --- a/db/collection_get.go +++ b/db/collection_get.go @@ -13,6 +13,8 @@ package db import ( "context" + "github.com/sourcenetwork/immutable" + "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/core" "github.com/sourcenetwork/defradb/datastore" @@ -20,7 +22,12 @@ import ( "github.com/sourcenetwork/defradb/db/fetcher" ) -func (c *collection) Get(ctx context.Context, docID client.DocID, showDeleted bool) (*client.Document, error) { +func (c *collection) Get( + ctx context.Context, + identity immutable.Option[string], + docID client.DocID, + showDeleted bool, +) (*client.Document, error) { // create txn txn, err := c.getTxn(ctx, true) if err != nil { @@ -29,23 +36,29 @@ func (c *collection) Get(ctx context.Context, docID client.DocID, showDeleted bo defer c.discardImplicitTxn(ctx, txn) primaryKey := c.getPrimaryKeyFromDocID(docID) - found, isDeleted, err := c.exists(ctx, txn, primaryKey) + found, isDeleted, err := c.exists(ctx, identity, txn, primaryKey) if err != nil { return nil, err } if !found || (isDeleted && !showDeleted) { - return nil, client.ErrDocumentNotFound + return nil, client.ErrDocumentNotFoundOrNotAuthorized } - doc, err := c.get(ctx, txn, primaryKey, nil, showDeleted) + doc, err := c.get(ctx, identity, txn, primaryKey, nil, showDeleted) if err != nil { return nil, err } + + if doc == nil { + return nil, client.ErrDocumentNotFoundOrNotAuthorized + } + return doc, c.commitImplicitTxn(ctx, txn) } func (c *collection) get( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, primaryKey core.PrimaryDataStoreKey, fields []client.FieldDefinition, @@ -54,7 +67,7 @@ func (c *collection) get( // create a new document fetcher df := c.newFetcher() // initialize it with the primary index - err := df.Init(ctx, txn, c, fields, nil, nil, false, showDeleted) + err := df.Init(ctx, identity, txn, c.db.acp, c, fields, nil, nil, false, showDeleted) if err != nil { _ = df.Close() return nil, err diff --git a/db/collection_index.go b/db/collection_index.go index 7fb036498a..1a7af8cc25 100644 --- a/db/collection_index.go +++ b/db/collection_index.go @@ -20,6 +20,7 @@ import ( "github.com/sourcenetwork/immutable" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/core" "github.com/sourcenetwork/defradb/datastore" @@ -99,8 +100,11 @@ func (db *db) fetchCollectionIndexDescriptions( colID uint32, ) ([]client.IndexDescription, error) { prefix := core.NewCollectionIndexKey(immutable.Some(colID), "") - _, indexDescriptions, err := datastore.DeserializePrefix[client.IndexDescription](ctx, - prefix.ToString(), txn.Systemstore()) + _, indexDescriptions, err := datastore.DeserializePrefix[client.IndexDescription]( + ctx, + prefix.ToString(), + txn.Systemstore(), + ) if err != nil { return nil, err } @@ -179,8 +183,11 @@ func (c *collection) updateIndexedDoc( if err != nil { return err } + // TODO-ACP: https://github.com/sourcenetwork/defradb/issues/2365 - ACP <> Indexing, possibly also check + // and handle the case of when oldDoc == nil (will be nil if inaccessible document). oldDoc, err := c.get( ctx, + acpIdentity.NoIdentity, txn, c.getPrimaryKeyFromDocID(doc.ID()), c.Definition().CollectIndexedFields(), @@ -253,6 +260,12 @@ func (c *collection) createIndex( txn datastore.Txn, desc client.IndexDescription, ) (CollectionIndex, error) { + // Don't allow creating index on a permissioned collection, until following is implemented. + // TODO-ACP: ACP <> INDEX https://github.com/sourcenetwork/defradb/issues/2365 + if c.Description().Policy.HasValue() { + return nil, ErrCanNotCreateIndexOnCollectionWithPolicy + } + if desc.Name != "" && !schema.IsValidIndexName(desc.Name) { return nil, schema.NewErrIndexWithInvalidName("!") } @@ -315,7 +328,18 @@ func (c *collection) iterateAllDocs( exec func(doc *client.Document) error, ) error { df := c.newFetcher() - err := df.Init(ctx, txn, c, fields, nil, nil, false, false) + err := df.Init( + ctx, + acpIdentity.NoIdentity, // TODO-ACP: https://github.com/sourcenetwork/defradb/issues/2365 - ACP <> Indexing + txn, + c.db.acp, + c, + fields, + nil, + nil, + false, + false, + ) if err != nil { return errors.Join(err, df.Close()) } diff --git a/db/collection_update.go b/db/collection_update.go index a4a4dc3f4d..dcc3ba6cba 100644 --- a/db/collection_update.go +++ b/db/collection_update.go @@ -32,16 +32,17 @@ import ( // Eg: UpdateWithFilter or UpdateWithDocID func (c *collection) UpdateWith( ctx context.Context, + identity immutable.Option[string], target any, updater string, ) (*client.UpdateResult, error) { switch t := target.(type) { case string, map[string]any, *request.Filter: - return c.UpdateWithFilter(ctx, t, updater) + return c.UpdateWithFilter(ctx, identity, t, updater) case client.DocID: - return c.UpdateWithDocID(ctx, t, updater) + return c.UpdateWithDocID(ctx, identity, t, updater) case []client.DocID: - return c.UpdateWithDocIDs(ctx, t, updater) + return c.UpdateWithDocIDs(ctx, identity, t, updater) default: return nil, client.ErrInvalidUpdateTarget } @@ -52,6 +53,7 @@ func (c *collection) UpdateWith( // or a parsed Patch, or parsed Merge Patch. func (c *collection) UpdateWithFilter( ctx context.Context, + identity immutable.Option[string], filter any, updater string, ) (*client.UpdateResult, error) { @@ -60,7 +62,7 @@ func (c *collection) UpdateWithFilter( return nil, err } defer c.discardImplicitTxn(ctx, txn) - res, err := c.updateWithFilter(ctx, txn, filter, updater) + res, err := c.updateWithFilter(ctx, identity, txn, filter, updater) if err != nil { return nil, err } @@ -72,6 +74,7 @@ func (c *collection) UpdateWithFilter( // or a parsed Patch, or parsed Merge Patch. func (c *collection) UpdateWithDocID( ctx context.Context, + identity immutable.Option[string], docID client.DocID, updater string, ) (*client.UpdateResult, error) { @@ -80,7 +83,7 @@ func (c *collection) UpdateWithDocID( return nil, err } defer c.discardImplicitTxn(ctx, txn) - res, err := c.updateWithDocID(ctx, txn, docID, updater) + res, err := c.updateWithDocID(ctx, identity, txn, docID, updater) if err != nil { return nil, err } @@ -93,6 +96,7 @@ func (c *collection) UpdateWithDocID( // or a parsed Patch, or parsed Merge Patch. func (c *collection) UpdateWithDocIDs( ctx context.Context, + identity immutable.Option[string], docIDs []client.DocID, updater string, ) (*client.UpdateResult, error) { @@ -101,7 +105,7 @@ func (c *collection) UpdateWithDocIDs( return nil, err } defer c.discardImplicitTxn(ctx, txn) - res, err := c.updateWithIDs(ctx, txn, docIDs, updater) + res, err := c.updateWithIDs(ctx, identity, txn, docIDs, updater) if err != nil { return nil, err } @@ -111,6 +115,7 @@ func (c *collection) UpdateWithDocIDs( func (c *collection) updateWithDocID( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, docID client.DocID, updater string, @@ -127,7 +132,7 @@ func (c *collection) updateWithDocID( return nil, client.ErrInvalidUpdater } - doc, err := c.Get(ctx, docID, false) + doc, err := c.Get(ctx, identity, docID, false) if err != nil { return nil, err } @@ -141,7 +146,7 @@ func (c *collection) updateWithDocID( return nil, err } - _, err = c.save(ctx, txn, doc, false) + err = c.update(ctx, identity, txn, doc) if err != nil { return nil, err } @@ -155,6 +160,7 @@ func (c *collection) updateWithDocID( func (c *collection) updateWithIDs( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, docIDs []client.DocID, updater string, @@ -175,7 +181,7 @@ func (c *collection) updateWithIDs( DocIDs: make([]string, len(docIDs)), } for i, docIDs := range docIDs { - doc, err := c.Get(ctx, docIDs, false) + doc, err := c.Get(ctx, identity, docIDs, false) if err != nil { return nil, err } @@ -189,7 +195,7 @@ func (c *collection) updateWithIDs( return nil, err } - _, err = c.save(ctx, txn, doc, false) + err = c.update(ctx, identity, txn, doc) if err != nil { return nil, err } @@ -202,6 +208,7 @@ func (c *collection) updateWithIDs( func (c *collection) updateWithFilter( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, filter any, updater string, @@ -223,7 +230,7 @@ func (c *collection) updateWithFilter( } // Make a selection plan that will scan through only the documents with matching filter. - selectionPlan, err := c.makeSelectionPlan(ctx, txn, filter) + selectionPlan, err := c.makeSelectionPlan(ctx, identity, txn, filter) if err != nil { return nil, err } @@ -277,7 +284,7 @@ func (c *collection) updateWithFilter( } } - _, err = c.save(ctx, txn, doc, false) + err = c.update(ctx, identity, txn, doc) if err != nil { return nil, err } @@ -310,6 +317,7 @@ func (c *collection) isSecondaryIDField(fieldDesc client.FieldDefinition) (clien // patched. func (c *collection) patchPrimaryDoc( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, secondaryCollectionName string, relationFieldDescription client.FieldDefinition, @@ -345,9 +353,11 @@ func (c *collection) patchPrimaryDoc( doc, err := primaryCol.Get( ctx, + identity, primaryDocID, false, ) + if err != nil && !errors.Is(err, ds.ErrNotFound) { return err } @@ -358,7 +368,14 @@ func (c *collection) patchPrimaryDoc( } pc := c.db.newCollection(primaryCol.Description(), primarySchema) - err = pc.validateOneToOneLinkDoesntAlreadyExist(ctx, txn, primaryDocID.String(), primaryIDField, docID) + err = pc.validateOneToOneLinkDoesntAlreadyExist( + ctx, + identity, + txn, + primaryDocID.String(), + primaryIDField, + docID, + ) if err != nil { return err } @@ -377,7 +394,7 @@ func (c *collection) patchPrimaryDoc( return err } - err = primaryCol.Update(ctx, doc) + err = primaryCol.Update(ctx, identity, doc) if err != nil { return err } @@ -391,6 +408,7 @@ func (c *collection) patchPrimaryDoc( // Additionally it only requests for the root scalar fields of the object func (c *collection) makeSelectionPlan( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, filter any, ) (planner.RequestPlan, error) { @@ -417,7 +435,14 @@ func (c *collection) makeSelectionPlan( return nil, err } - planner := planner.New(ctx, c.db.WithTxn(txn), txn) + planner := planner.New( + ctx, + identity, + c.db.acp, + c.db.WithTxn(txn), + txn, + ) + return planner.MakePlan(&request.Request{ Queries: []*request.OperationDefinition{ { diff --git a/db/db.go b/db/db.go index 30036fc55d..1b02e1d0c1 100644 --- a/db/db.go +++ b/db/db.go @@ -25,6 +25,7 @@ import ( "github.com/sourcenetwork/corelog" "github.com/sourcenetwork/immutable" + "github.com/sourcenetwork/defradb/acp" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/core" "github.com/sourcenetwork/defradb/datastore" @@ -44,7 +45,8 @@ var ( ) const ( - defaultMaxTxnRetries = 5 + defaultMaxTxnRetries = 5 + updateEventBufferSize = 100 ) // DB is the main interface for interacting with the @@ -71,12 +73,25 @@ type db struct { // The ID of the last transaction created. previousTxnID atomic.Uint64 + + // Contains ACP if it exists + acp immutable.Option[acp.ACP] } // Functional option type. type Option func(*db) -const updateEventBufferSize = 100 +// WithACP enables access control. If path is empty then acp runs in-memory. +func WithACP(path string) Option { + return func(db *db) { + var acpLocal acp.ACPLocal + acpLocal.Init(context.Background(), path) + db.acp = immutable.Some[acp.ACP](&acpLocal) + } +} + +// WithACPInMemory enables access control in-memory. +func WithACPInMemory() Option { return WithACP("") } // WithUpdateEvents enables the update events channel. func WithUpdateEvents() Option { @@ -104,11 +119,19 @@ func WithLensPoolSize(num int) Option { } // NewDB creates a new instance of the DB using the given options. -func NewDB(ctx context.Context, rootstore datastore.RootStore, options ...Option) (client.DB, error) { +func NewDB( + ctx context.Context, + rootstore datastore.RootStore, + options ...Option, +) (client.DB, error) { return newDB(ctx, rootstore, options...) } -func newDB(ctx context.Context, rootstore datastore.RootStore, options ...Option) (*implicitTxnDB, error) { +func newDB( + ctx context.Context, + rootstore datastore.RootStore, + options ...Option, +) (*implicitTxnDB, error) { multistore := datastore.MultiStoreFrom(rootstore) parser, err := graphql.NewParser() @@ -119,9 +142,9 @@ func newDB(ctx context.Context, rootstore datastore.RootStore, options ...Option db := &db{ rootstore: rootstore, multistore: multistore, - - parser: parser, - options: options, + acp: acp.NoACP, + parser: parser, + options: options, } // apply options @@ -184,6 +207,28 @@ func (db *db) LensRegistry() client.LensRegistry { return db.lensRegistry } +func (db *db) AddPolicy( + ctx context.Context, + creator string, + policy string, +) (client.AddPolicyResult, error) { + if !db.acp.HasValue() { + return client.AddPolicyResult{}, client.ErrPolicyAddFailureNoACP + } + + policyID, err := db.acp.Value().AddPolicy( + ctx, + creator, + policy, + ) + + if err != nil { + return client.AddPolicyResult{}, err + } + + return client.AddPolicyResult{PolicyID: policyID}, nil +} + // Initialize is called when a database is first run and creates all the db global meta data // like Collection ID counters. func (db *db) initialize(ctx context.Context) error { @@ -196,6 +241,14 @@ func (db *db) initialize(ctx context.Context) error { } defer txn.Discard(ctx) + // Start acp if enabled, this will recover previous state if there is any. + if db.acp.HasValue() { + // db is responsible to call db.acp.Close() to free acp resources while closing. + if err = db.acp.Value().Start(ctx); err != nil { + return err + } + } + exists, err := txn.Systemstore().Has(ctx, ds.NewKey("init")) if err != nil && !errors.Is(err, ds.ErrNotFound) { return err @@ -265,6 +318,13 @@ func (db *db) Close() { if err != nil { log.ErrorE("Failure closing running process", err) } + + if db.acp.HasValue() { + if err := db.acp.Value().Close(); err != nil { + log.ErrorE("Failure closing acp", err) + } + } + log.Info("Successfully closed running process") } diff --git a/db/description/collection.go b/db/description/collection.go index a6e9cd8b57..3658d3d318 100644 --- a/db/description/collection.go +++ b/db/description/collection.go @@ -237,7 +237,7 @@ func GetCollections( txn datastore.Txn, ) ([]client.CollectionDescription, error) { q, err := txn.Systemstore().Query(ctx, query.Query{ - Prefix: core.COLLECTION, + Prefix: core.COLLECTION_ID, }) if err != nil { return nil, NewErrFailedToCreateCollectionQuery(err) diff --git a/db/errors.go b/db/errors.go index b854ad2d3d..da82fcb941 100644 --- a/db/errors.go +++ b/db/errors.go @@ -88,14 +88,17 @@ const ( errCollectionSourceIDMutated string = "collection source ID cannot be mutated" errCollectionIndexesCannotBeMutated string = "collection indexes cannot be mutated" errCollectionFieldsCannotBeMutated string = "collection fields cannot be mutated" + errCollectionPolicyCannotBeMutated string = "collection policy cannot be mutated" errCollectionRootIDCannotBeMutated string = "collection root ID cannot be mutated" errCollectionSchemaVersionIDCannotBeMutated string = "collection schema version ID cannot be mutated" errCollectionIDCannotBeZero string = "collection ID cannot be zero" errCollectionsCannotBeDeleted string = "collections cannot be deleted" + errCanNotHavePolicyWithoutACP string = "can not specify policy on collection, without acp" ) var ( ErrFailedToGetCollection = errors.New(errFailedToGetCollection) + ErrCanNotCreateIndexOnCollectionWithPolicy = errors.New("can not create index on a collection with a policy") ErrSubscriptionsNotAllowed = errors.New("server does not accept subscriptions") ErrInvalidFilter = errors.New("invalid filter") ErrCollectionAlreadyExists = errors.New(errCollectionAlreadyExists) @@ -121,6 +124,7 @@ var ( ErrCollectionSchemaVersionIDCannotBeMutated = errors.New(errCollectionSchemaVersionIDCannotBeMutated) ErrCollectionIDCannotBeZero = errors.New(errCollectionIDCannotBeZero) ErrCollectionsCannotBeDeleted = errors.New(errCollectionsCannotBeDeleted) + ErrCanNotHavePolicyWithoutACP = errors.New(errCanNotHavePolicyWithoutACP) ) // NewErrFailedToGetHeads returns a new error indicating that the heads of a document @@ -597,6 +601,13 @@ func NewErrCollectionFieldsCannotBeMutated(colID uint32) error { ) } +func NewErrCollectionPolicyCannotBeMutated(colID uint32) error { + return errors.New( + errCollectionPolicyCannotBeMutated, + errors.NewKV("CollectionID", colID), + ) +} + func NewErrCollectionRootIDCannotBeMutated(colID uint32) error { return errors.New( errCollectionRootIDCannotBeMutated, diff --git a/db/fetcher/fetcher.go b/db/fetcher/fetcher.go index e4bb08cee4..db20310768 100644 --- a/db/fetcher/fetcher.go +++ b/db/fetcher/fetcher.go @@ -18,11 +18,15 @@ import ( "github.com/bits-and-blooms/bitset" dsq "github.com/ipfs/go-datastore/query" + "github.com/sourcenetwork/immutable" + + "github.com/sourcenetwork/defradb/acp" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/core" "github.com/sourcenetwork/defradb/datastore" "github.com/sourcenetwork/defradb/datastore/iterable" "github.com/sourcenetwork/defradb/db/base" + "github.com/sourcenetwork/defradb/db/permission" "github.com/sourcenetwork/defradb/planner/mapper" "github.com/sourcenetwork/defradb/request/graphql/parser" ) @@ -56,7 +60,9 @@ func (s *ExecInfo) Reset() { type Fetcher interface { Init( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, + acp immutable.Option[acp.ACP], col client.Collection, fields []client.FieldDefinition, filter *mapper.Filter, @@ -81,6 +87,10 @@ var ( // DocumentFetcher is a utility to incrementally fetch all the documents. type DocumentFetcher struct { + identity immutable.Option[string] + acp immutable.Option[acp.ACP] + passedPermissionCheck bool // have valid permission to access + col client.Collection reverse bool deletedDocs bool @@ -136,7 +146,9 @@ type DocumentFetcher struct { // Init implements DocumentFetcher. func (df *DocumentFetcher) Init( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, + acp immutable.Option[acp.ACP], col client.Collection, fields []client.FieldDefinition, filter *mapper.Filter, @@ -146,7 +158,7 @@ func (df *DocumentFetcher) Init( ) error { df.txn = txn - err := df.init(col, fields, filter, docmapper, reverse) + err := df.init(identity, acp, col, fields, filter, docmapper, reverse) if err != nil { return err } @@ -156,19 +168,23 @@ func (df *DocumentFetcher) Init( df.deletedDocFetcher = new(DocumentFetcher) df.deletedDocFetcher.txn = txn } - return df.deletedDocFetcher.init(col, fields, filter, docmapper, reverse) + return df.deletedDocFetcher.init(identity, acp, col, fields, filter, docmapper, reverse) } return nil } func (df *DocumentFetcher) init( + identity immutable.Option[string], + acp immutable.Option[acp.ACP], col client.Collection, fields []client.FieldDefinition, filter *mapper.Filter, docMapper *core.DocumentMapping, reverse bool, ) error { + df.identity = identity + df.acp = acp df.col = col df.reverse = reverse df.initialized = true @@ -476,6 +492,7 @@ func (df *DocumentFetcher) processKV(kv *keyValue) error { } } df.doc.id = []byte(kv.Key.DocID) + df.passedPermissionCheck = false df.passedFilter = false df.ranFilter = false @@ -544,24 +561,26 @@ func (df *DocumentFetcher) FetchNext(ctx context.Context) (EncodedDocument, Exec (df.reverse && ddf.kv.Key.DocID > df.kv.Key.DocID) || (!df.reverse && ddf.kv.Key.DocID < df.kv.Key.DocID) { encdoc, execInfo, err := ddf.FetchNext(ctx) + if err != nil { return nil, ExecInfo{}, err } - if encdoc != nil { - return encdoc, execInfo, err - } resultExecInfo.Add(execInfo) + if encdoc != nil { + return encdoc, resultExecInfo, nil + } } } } encdoc, execInfo, err := df.fetchNext(ctx) + if err != nil { return nil, ExecInfo{}, err } - resultExecInfo.Add(execInfo) + resultExecInfo.Add(execInfo) return encdoc, resultExecInfo, err } @@ -573,9 +592,6 @@ func (df *DocumentFetcher) fetchNext(ctx context.Context) (EncodedDocument, Exec if df.kv == nil { return nil, ExecInfo{}, client.NewErrUninitializeProperty("DocumentFetcher", "kv") } - // save the DocID of the current kv pair so we can track when we cross the doc pair boundries - // keyparts := df.kv.Key.List() - // key := keyparts[len(keyparts)-2] prevExecInfo := df.execInfo defer func() { df.execInfo.Add(prevExecInfo) }() @@ -584,8 +600,7 @@ func (df *DocumentFetcher) fetchNext(ctx context.Context) (EncodedDocument, Exec // we'll know when were done when either // A) Reach the end of the iterator for { - err := df.processKV(df.kv) - if err != nil { + if err := df.processKV(df.kv); err != nil { return nil, ExecInfo{}, err } @@ -606,16 +621,45 @@ func (df *DocumentFetcher) fetchNext(ctx context.Context) (EncodedDocument, Exec } } - // if we don't pass the filter (ran and pass) - // theres no point in collecting other select fields - // so we seek to the next doc - spansDone, docDone, err := df.nextKey(ctx, !df.passedFilter && df.ranFilter) + // Check if we have read access, for document on this collection, with the given identity. + if !df.passedPermissionCheck { + if !df.acp.HasValue() { + // If no acp is available, then we have unrestricted access. + df.passedPermissionCheck = true + } else { + hasPermission, err := permission.CheckAccessOfDocOnCollectionWithACP( + ctx, + df.identity, + df.acp.Value(), + df.col, + acp.ReadPermission, + df.kv.Key.DocID, + ) + + if err != nil { + df.passedPermissionCheck = false + return nil, ExecInfo{}, err + } + + df.passedPermissionCheck = hasPermission + } + } + + // if we don't pass the filter (ran and pass) or if we don't have access to document then + // there is no point in collecting other select fields, so we seek to the next doc. + spansDone, docDone, err := df.nextKey(ctx, !df.passedPermissionCheck || !df.passedFilter && df.ranFilter) + if err != nil { return nil, ExecInfo{}, err } - if docDone { - df.execInfo.DocsFetched++ + if !docDone { + continue + } + + df.execInfo.DocsFetched++ + + if df.passedPermissionCheck { if df.filter != nil { // if we passed, return if df.passedFilter { @@ -636,21 +680,11 @@ func (df *DocumentFetcher) fetchNext(ctx context.Context) (EncodedDocument, Exec } else { return df.doc, df.execInfo, nil } + } - if !spansDone { - continue - } - + if spansDone { return nil, df.execInfo, nil } - - // // crossed document kv boundary? - // // if so, return document - // newkeyparts := df.kv.Key.List() - // newKey := newkeyparts[len(newkeyparts)-2] - // if newKey != key { - // return df.doc, nil - // } } } diff --git a/db/fetcher/indexer.go b/db/fetcher/indexer.go index 0d09a2fc9a..84b1cba103 100644 --- a/db/fetcher/indexer.go +++ b/db/fetcher/indexer.go @@ -13,6 +13,9 @@ package fetcher import ( "context" + "github.com/sourcenetwork/immutable" + + "github.com/sourcenetwork/defradb/acp" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/core" "github.com/sourcenetwork/defradb/datastore" @@ -54,7 +57,9 @@ func NewIndexFetcher( func (f *IndexFetcher) Init( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, + acp immutable.Option[acp.ACP], col client.Collection, fields []client.FieldDefinition, filter *mapper.Filter, @@ -93,7 +98,18 @@ outer: f.indexIter = iter if f.docFetcher != nil && len(f.docFields) > 0 { - err = f.docFetcher.Init(ctx, f.txn, f.col, f.docFields, f.docFilter, f.mapping, false, false) + err = f.docFetcher.Init( + ctx, + identity, + f.txn, + acp, + f.col, + f.docFields, + f.docFilter, + f.mapping, + false, + false, + ) } return err diff --git a/db/fetcher/mocks/fetcher.go b/db/fetcher/mocks/fetcher.go index 044425c70b..e789032e47 100644 --- a/db/fetcher/mocks/fetcher.go +++ b/db/fetcher/mocks/fetcher.go @@ -3,16 +3,19 @@ package mocks import ( - context "context" - + acp "github.com/sourcenetwork/defradb/acp" client "github.com/sourcenetwork/defradb/client" + context "context" + core "github.com/sourcenetwork/defradb/core" datastore "github.com/sourcenetwork/defradb/datastore" fetcher "github.com/sourcenetwork/defradb/db/fetcher" + immutable "github.com/sourcenetwork/immutable" + mapper "github.com/sourcenetwork/defradb/planner/mapper" mock "github.com/stretchr/testify/mock" @@ -133,13 +136,13 @@ func (_c *Fetcher_FetchNext_Call) RunAndReturn(run func(context.Context) (fetche return _c } -// Init provides a mock function with given fields: ctx, txn, col, fields, filter, docmapper, reverse, showDeleted -func (_m *Fetcher) Init(ctx context.Context, txn datastore.Txn, col client.Collection, fields []client.FieldDefinition, filter *mapper.Filter, docmapper *core.DocumentMapping, reverse bool, showDeleted bool) error { - ret := _m.Called(ctx, txn, col, fields, filter, docmapper, reverse, showDeleted) +// Init provides a mock function with given fields: ctx, identity, txn, _a3, col, fields, filter, docmapper, reverse, showDeleted +func (_m *Fetcher) Init(ctx context.Context, identity immutable.Option[string], txn datastore.Txn, _a3 immutable.Option[acp.ACP], col client.Collection, fields []client.FieldDefinition, filter *mapper.Filter, docmapper *core.DocumentMapping, reverse bool, showDeleted bool) error { + ret := _m.Called(ctx, identity, txn, _a3, col, fields, filter, docmapper, reverse, showDeleted) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, datastore.Txn, client.Collection, []client.FieldDefinition, *mapper.Filter, *core.DocumentMapping, bool, bool) error); ok { - r0 = rf(ctx, txn, col, fields, filter, docmapper, reverse, showDeleted) + if rf, ok := ret.Get(0).(func(context.Context, immutable.Option[string], datastore.Txn, immutable.Option[acp.ACP], client.Collection, []client.FieldDefinition, *mapper.Filter, *core.DocumentMapping, bool, bool) error); ok { + r0 = rf(ctx, identity, txn, _a3, col, fields, filter, docmapper, reverse, showDeleted) } else { r0 = ret.Error(0) } @@ -154,20 +157,22 @@ type Fetcher_Init_Call struct { // Init is a helper method to define mock.On call // - ctx context.Context +// - identity immutable.Option[string] // - txn datastore.Txn +// - _a3 immutable.Option[acp.ACP] // - col client.Collection // - fields []client.FieldDefinition // - filter *mapper.Filter // - docmapper *core.DocumentMapping // - reverse bool // - showDeleted bool -func (_e *Fetcher_Expecter) Init(ctx interface{}, txn interface{}, col interface{}, fields interface{}, filter interface{}, docmapper interface{}, reverse interface{}, showDeleted interface{}) *Fetcher_Init_Call { - return &Fetcher_Init_Call{Call: _e.mock.On("Init", ctx, txn, col, fields, filter, docmapper, reverse, showDeleted)} +func (_e *Fetcher_Expecter) Init(ctx interface{}, identity interface{}, txn interface{}, _a3 interface{}, col interface{}, fields interface{}, filter interface{}, docmapper interface{}, reverse interface{}, showDeleted interface{}) *Fetcher_Init_Call { + return &Fetcher_Init_Call{Call: _e.mock.On("Init", ctx, identity, txn, _a3, col, fields, filter, docmapper, reverse, showDeleted)} } -func (_c *Fetcher_Init_Call) Run(run func(ctx context.Context, txn datastore.Txn, col client.Collection, fields []client.FieldDefinition, filter *mapper.Filter, docmapper *core.DocumentMapping, reverse bool, showDeleted bool)) *Fetcher_Init_Call { +func (_c *Fetcher_Init_Call) Run(run func(ctx context.Context, identity immutable.Option[string], txn datastore.Txn, _a3 immutable.Option[acp.ACP], col client.Collection, fields []client.FieldDefinition, filter *mapper.Filter, docmapper *core.DocumentMapping, reverse bool, showDeleted bool)) *Fetcher_Init_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(datastore.Txn), args[2].(client.Collection), args[3].([]client.FieldDefinition), args[4].(*mapper.Filter), args[5].(*core.DocumentMapping), args[6].(bool), args[7].(bool)) + run(args[0].(context.Context), args[1].(immutable.Option[string]), args[2].(datastore.Txn), args[3].(immutable.Option[acp.ACP]), args[4].(client.Collection), args[5].([]client.FieldDefinition), args[6].(*mapper.Filter), args[7].(*core.DocumentMapping), args[8].(bool), args[9].(bool)) }) return _c } @@ -177,7 +182,7 @@ func (_c *Fetcher_Init_Call) Return(_a0 error) *Fetcher_Init_Call { return _c } -func (_c *Fetcher_Init_Call) RunAndReturn(run func(context.Context, datastore.Txn, client.Collection, []client.FieldDefinition, *mapper.Filter, *core.DocumentMapping, bool, bool) error) *Fetcher_Init_Call { +func (_c *Fetcher_Init_Call) RunAndReturn(run func(context.Context, immutable.Option[string], datastore.Txn, immutable.Option[acp.ACP], client.Collection, []client.FieldDefinition, *mapper.Filter, *core.DocumentMapping, bool, bool) error) *Fetcher_Init_Call { _c.Call.Return(run) return _c } diff --git a/db/fetcher/mocks/utils.go b/db/fetcher/mocks/utils.go index 298d5b2ad6..524c46fc9e 100644 --- a/db/fetcher/mocks/utils.go +++ b/db/fetcher/mocks/utils.go @@ -27,6 +27,8 @@ func NewStubbedFetcher(t *testing.T) *Fetcher { mock.Anything, mock.Anything, mock.Anything, + mock.Anything, + mock.Anything, ).Maybe().Return(nil) f.EXPECT().Start(mock.Anything, mock.Anything).Maybe().Return(nil) f.EXPECT().FetchNext(mock.Anything).Maybe().Return(nil, nil) diff --git a/db/fetcher/versioned.go b/db/fetcher/versioned.go index 3f05f2c29a..16a4515939 100644 --- a/db/fetcher/versioned.go +++ b/db/fetcher/versioned.go @@ -19,6 +19,9 @@ import ( ds "github.com/ipfs/go-datastore" format "github.com/ipfs/go-ipld-format" + "github.com/sourcenetwork/immutable" + + "github.com/sourcenetwork/defradb/acp" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/core" "github.com/sourcenetwork/defradb/datastore" @@ -91,6 +94,8 @@ type VersionedFetcher struct { queuedCids *list.List + acp immutable.Option[acp.ACP] + col client.Collection // @todo index *client.IndexDescription mCRDTs map[uint32]merklecrdt.MerkleCRDT @@ -99,7 +104,9 @@ type VersionedFetcher struct { // Init initializes the VersionedFetcher. func (vf *VersionedFetcher) Init( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, + acp immutable.Option[acp.ACP], col client.Collection, fields []client.FieldDefinition, filter *mapper.Filter, @@ -107,6 +114,7 @@ func (vf *VersionedFetcher) Init( reverse bool, showDeleted bool, ) error { + vf.acp = acp vf.col = col vf.queuedCids = list.New() vf.mCRDTs = make(map[uint32]merklecrdt.MerkleCRDT) @@ -130,7 +138,18 @@ func (vf *VersionedFetcher) Init( // run the DF init, VersionedFetchers only supports the Primary (0) index vf.DocumentFetcher = new(DocumentFetcher) - return vf.DocumentFetcher.Init(ctx, vf.store, col, fields, filter, docmapper, reverse, showDeleted) + return vf.DocumentFetcher.Init( + ctx, + identity, + vf.store, + acp, + col, + fields, + filter, + docmapper, + reverse, + showDeleted, + ) } // Start serializes the correct state according to the Key and CID. diff --git a/db/indexed_docs_test.go b/db/indexed_docs_test.go index aeca3341b6..c11eb2617f 100644 --- a/db/indexed_docs_test.go +++ b/db/indexed_docs_test.go @@ -23,6 +23,8 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/sourcenetwork/defradb/acp" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/core" "github.com/sourcenetwork/defradb/datastore" @@ -46,7 +48,7 @@ type productDoc struct { } func (f *indexTestFixture) saveDocToCollection(doc *client.Document, col client.Collection) { - err := col.Create(f.ctx, doc) + err := col.Create(f.ctx, acpIdentity.NoIdentity, doc) require.NoError(f.t, err) f.commitTxn() f.txn, err = f.db.NewTxn(f.ctx, false) @@ -320,7 +322,7 @@ func TestNonUnique_IfFailsToStoredIndexedDoc_Error(t *testing.T) { dataStoreOn.Put(mock.Anything, key.ToDS(), mock.Anything).Return(errors.New("error")) dataStoreOn.Put(mock.Anything, mock.Anything, mock.Anything).Return(nil) - err := f.users.WithTxn(mockTxn).Create(f.ctx, doc) + err := f.users.WithTxn(mockTxn).Create(f.ctx, acpIdentity.NoIdentity, doc) require.ErrorIs(f.t, err, NewErrFailedToStoreIndexedField("name", nil)) } @@ -338,7 +340,7 @@ func TestNonUnique_IfDocDoesNotHaveIndexedField_SkipIndex(t *testing.T) { doc, err := client.NewDocFromJSON(data, f.users.Schema()) require.NoError(f.t, err) - err = f.users.Create(f.ctx, doc) + err = f.users.Create(f.ctx, acpIdentity.NoIdentity, doc) require.NoError(f.t, err) key := newIndexKeyBuilder(f).Col(usersColName).Build() @@ -358,7 +360,7 @@ func TestNonUnique_IfSystemStorageHasInvalidIndexDescription_Error(t *testing.T) systemStoreOn.Query(mock.Anything, mock.Anything). Return(mocks.NewQueryResultsWithValues(t, []byte("invalid")), nil) - err := f.users.WithTxn(mockTxn).Create(f.ctx, doc) + err := f.users.WithTxn(mockTxn).Create(f.ctx, acpIdentity.NoIdentity, doc) assert.ErrorIs(t, err, datastore.NewErrInvalidStoredValue(nil)) } @@ -376,7 +378,7 @@ func TestNonUnique_IfSystemStorageFailsToReadIndexDesc_Error(t *testing.T) { systemStoreOn.Query(mock.Anything, mock.Anything). Return(nil, testErr) - err := f.users.WithTxn(mockTxn).Create(f.ctx, doc) + err := f.users.WithTxn(mockTxn).Create(f.ctx, acpIdentity.NoIdentity, doc) require.ErrorIs(t, err, testErr) } @@ -409,9 +411,9 @@ func TestNonUnique_IfMultipleCollectionsWithIndexes_StoreIndexWithCollectionID(t userDoc := f.newUserDoc("John", 21, users) prodDoc := f.newProdDoc(1, 3, "games", products) - err = users.Create(f.ctx, userDoc) + err = users.Create(f.ctx, acpIdentity.NoIdentity, userDoc) require.NoError(f.t, err) - err = products.Create(f.ctx, prodDoc) + err = products.Create(f.ctx, acpIdentity.NoIdentity, prodDoc) require.NoError(f.t, err) f.commitTxn() @@ -588,8 +590,30 @@ func TestNonUniqueCreate_IfUponIndexingExistingDocsFetcherFails_ReturnError(t *t Name: "Fails to init", PrepareFetcher: func() fetcher.Fetcher { f := fetcherMocks.NewStubbedFetcher(t) - f.EXPECT().Init(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Unset() - f.EXPECT().Init(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testError) + f.EXPECT().Init( + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + ).Unset() + f.EXPECT().Init( + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + ).Return(testError) f.EXPECT().Close().Unset() f.EXPECT().Close().Return(nil) return f @@ -721,14 +745,14 @@ func TestNonUniqueUpdate_ShouldDeleteOldValueAndStoreNewOne(t *testing.T) { Name: "update", NewValue: "Islam", Exec: func(doc *client.Document) error { - return f.users.Update(f.ctx, doc) + return f.users.Update(f.ctx, acpIdentity.NoIdentity, doc) }, }, { Name: "save", NewValue: "Andy", Exec: func(doc *client.Document) error { - return f.users.Save(f.ctx, doc) + return f.users.Save(f.ctx, acpIdentity.NoIdentity, doc) }, }, } @@ -782,7 +806,7 @@ func TestNonUniqueUpdate_IfFailsToReadIndexDescription_ReturnError(t *testing.T) usersCol.(*collection).fetcherFactory = func() fetcher.Fetcher { return fetcherMocks.NewStubbedFetcher(t) } - err = usersCol.WithTxn(mockedTxn).Update(f.ctx, doc) + err = usersCol.WithTxn(mockedTxn).Update(f.ctx, acpIdentity.NoIdentity, doc) require.ErrorIs(t, err, testErr) } @@ -797,8 +821,30 @@ func TestNonUniqueUpdate_IfFetcherFails_ReturnError(t *testing.T) { Name: "Fails to init", PrepareFetcher: func() fetcher.Fetcher { f := fetcherMocks.NewStubbedFetcher(t) - f.EXPECT().Init(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Unset() - f.EXPECT().Init(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testError) + f.EXPECT().Init( + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + ).Unset() + f.EXPECT().Init( + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + ).Return(testError) f.EXPECT().Close().Unset() f.EXPECT().Close().Return(nil) return f @@ -856,7 +902,7 @@ func TestNonUniqueUpdate_IfFetcherFails_ReturnError(t *testing.T) { err := doc.Set(usersNameFieldName, "Islam") require.NoError(t, err, tc.Name) - err = f.users.Update(f.ctx, doc) + err = f.users.Update(f.ctx, acpIdentity.NoIdentity, doc) require.Error(t, err, tc.Name) newKey := newIndexKeyBuilder(f).Col(usersColName).Fields(usersNameFieldName).Doc(doc).Build() @@ -884,7 +930,7 @@ func TestNonUniqueUpdate_IfFailsToUpdateIndex_ReturnError(t *testing.T) { err = doc.Set(usersAgeFieldName, 23) require.NoError(t, err) - err = f.users.Update(f.ctx, doc) + err = f.users.Update(f.ctx, acpIdentity.NoIdentity, doc) require.ErrorIs(t, err, ErrCorruptedIndex) } @@ -896,11 +942,35 @@ func TestNonUniqueUpdate_ShouldPassToFetcherOnlyRelevantFields(t *testing.T) { f.users.(*collection).fetcherFactory = func() fetcher.Fetcher { f := fetcherMocks.NewStubbedFetcher(t) - f.EXPECT().Init(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Unset() - f.EXPECT().Init(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + f.EXPECT().Init( + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + ).Unset() + f.EXPECT().Init( + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + ). RunAndReturn(func( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, + acp immutable.Option[acp.ACP], col client.Collection, fields []client.FieldDefinition, filter *mapper.Filter, @@ -920,7 +990,7 @@ func TestNonUniqueUpdate_ShouldPassToFetcherOnlyRelevantFields(t *testing.T) { err := doc.Set(usersNameFieldName, "Islam") require.NoError(t, err) - _ = f.users.Update(f.ctx, doc) + _ = f.users.Update(f.ctx, acpIdentity.NoIdentity, doc) } func TestNonUniqueUpdate_IfDatastoreFails_ReturnError(t *testing.T) { @@ -978,7 +1048,7 @@ func TestNonUniqueUpdate_IfDatastoreFails_ReturnError(t *testing.T) { mockedTxn.EXPECT().Datastore().Unset() mockedTxn.EXPECT().Datastore().Return(mockedTxn.MockDatastore).Maybe() - err = f.users.WithTxn(mockedTxn).Update(f.ctx, doc) + err = f.users.WithTxn(mockedTxn).Update(f.ctx, acpIdentity.NoIdentity, doc) require.ErrorIs(t, err, testErr) } } @@ -1003,7 +1073,7 @@ func TestNonUpdate_IfIndexedFieldWasNil_ShouldDeleteIt(t *testing.T) { err = doc.Set(usersNameFieldName, "John") require.NoError(f.t, err) - err = f.users.Update(f.ctx, doc) + err = f.users.Update(f.ctx, acpIdentity.NoIdentity, doc) require.NoError(f.t, err) f.commitTxn() @@ -1127,14 +1197,14 @@ func TestUniqueUpdate_ShouldDeleteOldValueAndStoreNewOne(t *testing.T) { Name: "update", NewValue: "Islam", Exec: func(doc *client.Document) error { - return f.users.Update(f.ctx, doc) + return f.users.Update(f.ctx, acpIdentity.NoIdentity, doc) }, }, { Name: "save", NewValue: "Andy", Exec: func(doc *client.Document) error { - return f.users.Save(f.ctx, doc) + return f.users.Save(f.ctx, acpIdentity.NoIdentity, doc) }, }, } @@ -1270,7 +1340,7 @@ func TestUniqueComposite_IfNilUpdateToValue_ShouldUpdateIndexStored(t *testing.T newKey := newIndexKeyBuilder(f).Col(usersColName).Fields(usersNameFieldName, usersAgeFieldName). Doc(doc).Unique().Build() - require.NoError(t, f.users.Update(f.ctx, doc), tc.Name) + require.NoError(t, f.users.Update(f.ctx, acpIdentity.NoIdentity, doc), tc.Name) f.commitTxn() _, err = f.txn.Datastore().Get(f.ctx, oldKey.ToDS()) @@ -1319,7 +1389,7 @@ func TestCompositeUpdate_ShouldDeleteOldValueAndStoreNewOne(t *testing.T) { NewValue: "Islam", Field: usersNameFieldName, Exec: func(doc *client.Document) error { - return f.users.Update(f.ctx, doc) + return f.users.Update(f.ctx, acpIdentity.NoIdentity, doc) }, }, { @@ -1327,7 +1397,7 @@ func TestCompositeUpdate_ShouldDeleteOldValueAndStoreNewOne(t *testing.T) { NewValue: "Andy", Field: usersNameFieldName, Exec: func(doc *client.Document) error { - return f.users.Save(f.ctx, doc) + return f.users.Save(f.ctx, acpIdentity.NoIdentity, doc) }, }, { @@ -1335,7 +1405,7 @@ func TestCompositeUpdate_ShouldDeleteOldValueAndStoreNewOne(t *testing.T) { NewValue: 33, Field: usersAgeFieldName, Exec: func(doc *client.Document) error { - return f.users.Update(f.ctx, doc) + return f.users.Update(f.ctx, acpIdentity.NoIdentity, doc) }, }, { @@ -1343,7 +1413,7 @@ func TestCompositeUpdate_ShouldDeleteOldValueAndStoreNewOne(t *testing.T) { NewValue: 36, Field: usersAgeFieldName, Exec: func(doc *client.Document) error { - return f.users.Save(f.ctx, doc) + return f.users.Save(f.ctx, acpIdentity.NoIdentity, doc) }, }, } diff --git a/db/permission/check.go b/db/permission/check.go new file mode 100644 index 0000000000..b62b9c384b --- /dev/null +++ b/db/permission/check.go @@ -0,0 +1,91 @@ +// 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 permission + +import ( + "context" + + "github.com/sourcenetwork/immutable" + + "github.com/sourcenetwork/defradb/acp" + "github.com/sourcenetwork/defradb/client" +) + +// CheckAccessOfDocOnCollectionWithACP handles the check, which tells us if access to the target +// document is valid, with respect to the permission type, and the specified collection. +// +// This function should only be called if acp is available. As we have unrestricted +// access when acp is not available (acp turned off). +// +// Since we know acp is enabled we have these components to check in this function: +// (1) the request is permissioned (has an identity), +// (2) the collection is permissioned (has a policy), +// +// Unrestricted Access to document if: +// - (2) is false. +// - Document is public (unregistered), whether signatured request or not doesn't matter. +func CheckAccessOfDocOnCollectionWithACP( + ctx context.Context, + identityOptional immutable.Option[string], + acpSystem acp.ACP, + collection client.Collection, + permission acp.DPIPermission, + docID string, +) (bool, error) { + // Even if acp exists, but there is no policy on the collection (unpermissioned collection) + // then we still have unrestricted access. + policyID, resourceName, hasPolicy := isPermissioned(collection) + if !hasPolicy { + return true, nil + } + + // Now that we know acp is available and the collection is permissioned, before checking access with + // acp directly we need to make sure that the document is not public, as public documents will not + // be regestered with acp. We give unrestricted access to public documents, so it does not matter + // whether the request has a signature identity or not at this stage of the check. + isRegistered, err := acpSystem.IsDocRegistered( + ctx, + policyID, + resourceName, + docID, + ) + if err != nil { + return false, err + } + + if !isRegistered { + // Unrestricted access as it is a public document. + return true, nil + } + + // At this point if the request is not signatured, then it has no access, because: + // the collection has a policy on it, and the acp is enabled/available, + // and the document is not public (is regestered with acp). + if !identityOptional.HasValue() { + return false, nil + } + + // Now actually check using the signature if this identity has access or not. + hasAccess, err := acpSystem.CheckDocAccess( + ctx, + permission, + identityOptional.Value(), + policyID, + resourceName, + docID, + ) + + if err != nil { + return false, err + } + + return hasAccess, nil +} diff --git a/db/permission/permission.go b/db/permission/permission.go new file mode 100644 index 0000000000..3b365cba75 --- /dev/null +++ b/db/permission/permission.go @@ -0,0 +1,32 @@ +// 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 permission + +import ( + "github.com/sourcenetwork/defradb/client" +) + +// isPermissioned returns true if the collection has a policy, otherwise returns false. +// +// This tells us if access control is enabled for this collection or not. +// +// When there is a policy, in addition to returning true in the last return value, the +// first returned value is policyID, second is the resource name. +func isPermissioned(collection client.Collection) (string, string, bool) { + policy := collection.Definition().Description.Policy + if policy.HasValue() && + policy.Value().ID != "" && + policy.Value().ResourceName != "" { + return policy.Value().ID, policy.Value().ResourceName, true + } + + return "", "", false +} diff --git a/db/permission/register.go b/db/permission/register.go new file mode 100644 index 0000000000..b638a015db --- /dev/null +++ b/db/permission/register.go @@ -0,0 +1,50 @@ +// 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 permission + +import ( + "context" + + "github.com/sourcenetwork/immutable" + + "github.com/sourcenetwork/defradb/acp" + "github.com/sourcenetwork/defradb/client" +) + +// RegisterDocOnCollectionWithACP handles the registration of the document with acp. +// +// Since acp will always exist when this is called we have these components to worry about: +// (1) the request is permissioned (has an identity signature), +// (2) the collection is permissioned (has a policy), +// +// The document is only registered if all (1) (2) are true. +// +// Otherwise, nothing is registered with acp. +func RegisterDocOnCollectionWithACP( + ctx context.Context, + identity immutable.Option[string], + acpSystem acp.ACP, + collection client.Collection, + docID string, +) error { + // An identity exists and the collection has a policy. + if policyID, resourceName, hasPolicy := isPermissioned(collection); hasPolicy && identity.HasValue() { + return acpSystem.RegisterDocObject( + ctx, + identity.Value(), + policyID, + resourceName, + docID, + ) + } + + return nil +} diff --git a/db/request.go b/db/request.go index 69eabebd34..2905ee4de2 100644 --- a/db/request.go +++ b/db/request.go @@ -13,13 +13,20 @@ package db import ( "context" + "github.com/sourcenetwork/immutable" + "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/datastore" "github.com/sourcenetwork/defradb/planner" ) // execRequest executes a request against the database. -func (db *db) execRequest(ctx context.Context, request string, txn datastore.Txn) *client.RequestResult { +func (db *db) execRequest( + ctx context.Context, + identity immutable.Option[string], + request string, + txn datastore.Txn, +) *client.RequestResult { res := &client.RequestResult{} ast, err := db.parser.BuildRequestAST(request) if err != nil { @@ -44,11 +51,17 @@ func (db *db) execRequest(ctx context.Context, request string, txn datastore.Txn if pub != nil { res.Pub = pub - go db.handleSubscription(ctx, pub, subRequest) + go db.handleSubscription(ctx, identity, pub, subRequest) return res } - planner := planner.New(ctx, db.WithTxn(txn), txn) + planner := planner.New( + ctx, + identity, + db.acp, + db.WithTxn(txn), + txn, + ) results, err := planner.RunRequest(ctx, parsedRequest) if err != nil { diff --git a/db/schema.go b/db/schema.go index 676d983983..5b10df9906 100644 --- a/db/schema.go +++ b/db/schema.go @@ -47,6 +47,12 @@ func (db *db) addSchema( returnDescriptions := make([]client.CollectionDescription, len(newDefinitions)) for i, definition := range newDefinitions { + // Only accept the schema if policy description is valid, otherwise reject the schema. + err := db.validateCollectionDefinitionPolicyDesc(ctx, definition.Description.Policy) + if err != nil { + return nil, err + } + col, err := db.createCollection(ctx, txn, definition) if err != nil { return nil, err diff --git a/db/subscriptions.go b/db/subscriptions.go index bc013ae587..f6f187c54f 100644 --- a/db/subscriptions.go +++ b/db/subscriptions.go @@ -13,6 +13,8 @@ package db import ( "context" + "github.com/sourcenetwork/immutable" + "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/client/request" "github.com/sourcenetwork/defradb/datastore" @@ -49,6 +51,7 @@ func (db *db) checkForClientSubscriptions(r *request.Request) ( func (db *db) handleSubscription( ctx context.Context, + identity immutable.Option[string], pub *events.Publisher[events.Update], r *request.ObjectSubscription, ) { @@ -59,7 +62,7 @@ func (db *db) handleSubscription( continue } - db.handleEvent(ctx, txn, pub, evt, r) + db.handleEvent(ctx, identity, txn, pub, evt, r) txn.Discard(ctx) } @@ -67,12 +70,19 @@ func (db *db) handleSubscription( func (db *db) handleEvent( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, pub *events.Publisher[events.Update], evt events.Update, r *request.ObjectSubscription, ) { - p := planner.New(ctx, db.WithTxn(txn), txn) + p := planner.New( + ctx, + identity, + db.acp, + db.WithTxn(txn), + txn, + ) s := r.ToSelect(evt.DocID, evt.Cid.String()) diff --git a/db/txn_db.go b/db/txn_db.go index 09a7002033..e77176b433 100644 --- a/db/txn_db.go +++ b/db/txn_db.go @@ -14,6 +14,7 @@ import ( "context" "github.com/lens-vm/lens/host-go/config/model" + "github.com/sourcenetwork/immutable" "github.com/sourcenetwork/defradb/client" @@ -36,7 +37,11 @@ type explicitTxnDB struct { } // ExecRequest executes a request against the database. -func (db *implicitTxnDB) ExecRequest(ctx context.Context, request string) *client.RequestResult { +func (db *implicitTxnDB) ExecRequest( + ctx context.Context, + identity immutable.Option[string], + request string, +) *client.RequestResult { txn, err := db.NewTxn(ctx, false) if err != nil { res := &client.RequestResult{} @@ -45,7 +50,7 @@ func (db *implicitTxnDB) ExecRequest(ctx context.Context, request string) *clien } defer txn.Discard(ctx) - res := db.execRequest(ctx, request, txn) + res := db.execRequest(ctx, identity, request, txn) if len(res.GQL.Errors) > 0 { return res } @@ -61,9 +66,10 @@ func (db *implicitTxnDB) ExecRequest(ctx context.Context, request string) *clien // ExecRequest executes a transaction request against the database. func (db *explicitTxnDB) ExecRequest( ctx context.Context, + identity immutable.Option[string], request string, ) *client.RequestResult { - return db.execRequest(ctx, request, db.txn) + return db.execRequest(ctx, identity, request, db.txn) } // GetCollectionByName returns an existing collection within the database. diff --git a/docs/cli/defradb.md b/docs/cli/defradb.md index c89ce0f1aa..00b29d7392 100644 --- a/docs/cli/defradb.md +++ b/docs/cli/defradb.md @@ -14,11 +14,12 @@ Start a DefraDB node, interact with a local or remote node, and much more. ``` --allowed-origins stringArray List of origins to allow for CORS requests -h, --help help for defradb - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client.md b/docs/cli/defradb_client.md index 30e8c804ee..1b5532ac9f 100644 --- a/docs/cli/defradb_client.md +++ b/docs/cli/defradb_client.md @@ -18,11 +18,12 @@ Execute queries, add schema types, obtain node info, etc. ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) @@ -38,6 +39,7 @@ Execute queries, add schema types, obtain node info, etc. ### SEE ALSO * [defradb](defradb.md) - DefraDB Edge Database +* [defradb client acp](defradb_client_acp.md) - Interact with the access control system of a DefraDB node * [defradb client backup](defradb_client_backup.md) - Interact with the backup utility * [defradb client collection](defradb_client_collection.md) - Interact with a collection. * [defradb client dump](defradb_client_dump.md) - Dump the contents of DefraDB node-side diff --git a/docs/cli/defradb_client_acp.md b/docs/cli/defradb_client_acp.md new file mode 100644 index 0000000000..ab4ac22d6d --- /dev/null +++ b/docs/cli/defradb_client_acp.md @@ -0,0 +1,46 @@ +## defradb client acp + +Interact with the access control system of a DefraDB node + +### Synopsis + +Interact with the access control system of a DefraDB node + +Learn more about [ACP](/acp/README.md) + + + +### Options + +``` + -h, --help help for acp +``` + +### Options inherited from parent commands + +``` + --allowed-origins stringArray List of origins to allow for CORS requests + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs + --max-txn-retries int Specify the maximum number of retries per transaction (default 5) + --no-p2p Disable the peer-to-peer network synchronization system + --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) + --peers stringArray List of peers to connect to + --privkeypath string Path to the private key for tls + --pubkeypath string Path to the public key for tls + --rootdir string Directory for persistent data (default: $HOME/.defradb) + --store string Specify the datastore to use (supported: badger, memory) (default "badger") + --tx uint Transaction ID + --url string URL of HTTP endpoint to listen on or connect to (default "127.0.0.1:9181") + --valuelogfilesize int Specify the datastore value log file size (in bytes). In memory size will be 2*valuelogfilesize (default 1073741824) +``` + +### SEE ALSO + +* [defradb client](defradb_client.md) - Interact with a DefraDB node +* [defradb client acp policy](defradb_client_acp_policy.md) - Interact with the acp policy features of DefraDB instance + diff --git a/docs/cli/defradb_client_acp_policy.md b/docs/cli/defradb_client_acp_policy.md new file mode 100644 index 0000000000..ef6d02e3dc --- /dev/null +++ b/docs/cli/defradb_client_acp_policy.md @@ -0,0 +1,42 @@ +## defradb client acp policy + +Interact with the acp policy features of DefraDB instance + +### Synopsis + +Interact with the acp policy features of DefraDB instance + +### Options + +``` + -h, --help help for policy +``` + +### Options inherited from parent commands + +``` + --allowed-origins stringArray List of origins to allow for CORS requests + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs + --max-txn-retries int Specify the maximum number of retries per transaction (default 5) + --no-p2p Disable the peer-to-peer network synchronization system + --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) + --peers stringArray List of peers to connect to + --privkeypath string Path to the private key for tls + --pubkeypath string Path to the public key for tls + --rootdir string Directory for persistent data (default: $HOME/.defradb) + --store string Specify the datastore to use (supported: badger, memory) (default "badger") + --tx uint Transaction ID + --url string URL of HTTP endpoint to listen on or connect to (default "127.0.0.1:9181") + --valuelogfilesize int Specify the datastore value log file size (in bytes). In memory size will be 2*valuelogfilesize (default 1073741824) +``` + +### SEE ALSO + +* [defradb client acp](defradb_client_acp.md) - Interact with the access control system of a DefraDB node +* [defradb client acp policy add](defradb_client_acp_policy_add.md) - Add new policy + diff --git a/docs/cli/defradb_client_acp_policy_add.md b/docs/cli/defradb_client_acp_policy_add.md new file mode 100644 index 0000000000..322842a962 --- /dev/null +++ b/docs/cli/defradb_client_acp_policy_add.md @@ -0,0 +1,90 @@ +## defradb client acp policy add + +Add new policy + +### Synopsis + +Add new policy + +Notes: + - Can not add a policy without specifying an identity. + - ACP must be available (i.e. ACP can not be disabled). + - A non-DPI policy will be accepted (will be registered with acp system). + - But only a valid DPI policyID & resource can be specified on a schema. + - DPI validation happens when attempting to add a schema with '@policy'. + - Learn more about [ACP & DPI Rules](/acp/README.md) + +Example: add from an argument string: + defradb client acp policy add -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j ' +description: A Valid DefraDB Policy Interface + +actor: + name: actor + +resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor +' + +Example: add from file: + defradb client acp policy add -i cosmos17r39df0hdcrgnmmw4mvu7qgk5nu888c7uvv37y -f policy.yml + +Example: add from file, verbose flags: + defradb client acp policy add --identity cosmos1kpw734v54g0t0d8tcye8ee5jc3gld0tcr2q473 --file policy.yml + +Example: add from stdin: + cat policy.yml | defradb client acp policy add - + + + +``` +defradb client acp policy add [-i --identity] [policy] [flags] +``` + +### Options + +``` + -f, --file string File to load a policy from + -h, --help help for add + -i, --identity string [Required] Identity of the creator +``` + +### Options inherited from parent commands + +``` + --allowed-origins stringArray List of origins to allow for CORS requests + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs + --max-txn-retries int Specify the maximum number of retries per transaction (default 5) + --no-p2p Disable the peer-to-peer network synchronization system + --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) + --peers stringArray List of peers to connect to + --privkeypath string Path to the private key for tls + --pubkeypath string Path to the public key for tls + --rootdir string Directory for persistent data (default: $HOME/.defradb) + --store string Specify the datastore to use (supported: badger, memory) (default "badger") + --tx uint Transaction ID + --url string URL of HTTP endpoint to listen on or connect to (default "127.0.0.1:9181") + --valuelogfilesize int Specify the datastore value log file size (in bytes). In memory size will be 2*valuelogfilesize (default 1073741824) +``` + +### SEE ALSO + +* [defradb client acp policy](defradb_client_acp_policy.md) - Interact with the acp policy features of DefraDB instance + diff --git a/docs/cli/defradb_client_backup.md b/docs/cli/defradb_client_backup.md index a7c7ae453b..c8b48818b9 100644 --- a/docs/cli/defradb_client_backup.md +++ b/docs/cli/defradb_client_backup.md @@ -17,11 +17,12 @@ Currently only supports JSON format. ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_backup_export.md b/docs/cli/defradb_client_backup_export.md index 6992b120c6..c47e46de3f 100644 --- a/docs/cli/defradb_client_backup_export.md +++ b/docs/cli/defradb_client_backup_export.md @@ -31,11 +31,12 @@ defradb client backup export [-c --collections | -p --pretty | -f --format] ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_backup_import.md b/docs/cli/defradb_client_backup_import.md index ad2d3a1117..9e2c9c54e0 100644 --- a/docs/cli/defradb_client_backup_import.md +++ b/docs/cli/defradb_client_backup_import.md @@ -23,11 +23,12 @@ defradb client backup import [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_collection.md b/docs/cli/defradb_client_collection.md index 593e2d01ee..c33b9970bf 100644 --- a/docs/cli/defradb_client_collection.md +++ b/docs/cli/defradb_client_collection.md @@ -21,11 +21,12 @@ Create, read, update, and delete documents within a collection. ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) @@ -46,5 +47,6 @@ Create, read, update, and delete documents within a collection. * [defradb client collection describe](defradb_client_collection_describe.md) - View collection description. * [defradb client collection docIDs](defradb_client_collection_docIDs.md) - List all document IDs (docIDs). * [defradb client collection get](defradb_client_collection_get.md) - View document fields. +* [defradb client collection patch](defradb_client_collection_patch.md) - Patch existing collection descriptions * [defradb client collection update](defradb_client_collection_update.md) - Update documents by docID or filter. diff --git a/docs/cli/defradb_client_collection_create.md b/docs/cli/defradb_client_collection_create.md index 7c2cba7487..492bb6a060 100644 --- a/docs/cli/defradb_client_collection_create.md +++ b/docs/cli/defradb_client_collection_create.md @@ -6,28 +6,32 @@ Create a new document. Create a new document. -Example: create from string +Example: create from string: defradb client collection create --name User '{ "name": "Bob" }' -Example: create multiple from string +Example: create from string, with identity: + defradb client collection create -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name User '{ "name": "Bob" }' + +Example: create multiple from string: defradb client collection create --name User '[{ "name": "Alice" }, { "name": "Bob" }]' -Example: create from file +Example: create from file: defradb client collection create --name User -f document.json -Example: create from stdin +Example: create from stdin: cat document.json | defradb client collection create --name User - ``` -defradb client collection create [flags] +defradb client collection create [-i --identity] [flags] ``` ### Options ``` - -f, --file string File containing document(s) - -h, --help help for create + -f, --file string File containing document(s) + -h, --help help for create + -i, --identity string Identity of the actor ``` ### Options inherited from parent commands @@ -35,11 +39,12 @@ defradb client collection create [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests --get-inactive Get inactive collections as well as active - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --name string Collection name --no-p2p Disable the peer-to-peer network synchronization system diff --git a/docs/cli/defradb_client_collection_delete.md b/docs/cli/defradb_client_collection_delete.md index 33a5af4809..ce2f6ff8ab 100644 --- a/docs/cli/defradb_client_collection_delete.md +++ b/docs/cli/defradb_client_collection_delete.md @@ -6,23 +6,27 @@ Delete documents by docID or filter. Delete documents by docID or filter and lists the number of documents deleted. -Example: delete by docID(s) - defradb client collection delete --name User --docID bae-123,bae-456 +Example: delete by docID(s): + defradb client collection delete --name User --docID bae-123,bae-456 -Example: delete by filter +Example: delete by docID(s) with identity: + defradb client collection delete -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name User --docID bae-123,bae-456 + +Example: delete by filter: defradb client collection delete --name User --filter '{ "_gte": { "points": 100 } }' ``` -defradb client collection delete [--filter --docID ] [flags] +defradb client collection delete [-i --identity] [--filter --docID ] [flags] ``` ### Options ``` - --docID strings Document ID - --filter string Document filter - -h, --help help for delete + --docID strings Document ID + --filter string Document filter + -h, --help help for delete + -i, --identity string Identity of the actor ``` ### Options inherited from parent commands @@ -30,11 +34,12 @@ defradb client collection delete [--filter --docID ] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests --get-inactive Get inactive collections as well as active - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --name string Collection name --no-p2p Disable the peer-to-peer network synchronization system diff --git a/docs/cli/defradb_client_collection_describe.md b/docs/cli/defradb_client_collection_describe.md index 46e8623d6a..54d4dd1b99 100644 --- a/docs/cli/defradb_client_collection_describe.md +++ b/docs/cli/defradb_client_collection_describe.md @@ -37,11 +37,12 @@ defradb client collection describe [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_collection_docIDs.md b/docs/cli/defradb_client_collection_docIDs.md index c976d05417..1ee91d7462 100644 --- a/docs/cli/defradb_client_collection_docIDs.md +++ b/docs/cli/defradb_client_collection_docIDs.md @@ -6,18 +6,22 @@ List all document IDs (docIDs). List all document IDs (docIDs). -Example: +Example: list all docID(s): defradb client collection docIDs --name User + +Example: list all docID(s), with an identity: + defradb client collection docIDs -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name User ``` -defradb client collection docIDs [flags] +defradb client collection docIDs [-i --identity] [flags] ``` ### Options ``` - -h, --help help for docIDs + -h, --help help for docIDs + -i, --identity string Identity of the actor ``` ### Options inherited from parent commands @@ -25,11 +29,12 @@ defradb client collection docIDs [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests --get-inactive Get inactive collections as well as active - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --name string Collection name --no-p2p Disable the peer-to-peer network synchronization system diff --git a/docs/cli/defradb_client_collection_get.md b/docs/cli/defradb_client_collection_get.md index c2aeac17b3..a73228829b 100644 --- a/docs/cli/defradb_client_collection_get.md +++ b/docs/cli/defradb_client_collection_get.md @@ -8,17 +8,21 @@ View document fields. Example: defradb client collection get --name User bae-123 + +Example to get a private document we must use an identity: + defradb client collection get -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name User bae-123 ``` -defradb client collection get [--show-deleted] [flags] +defradb client collection get [-i --identity] [--show-deleted] [flags] ``` ### Options ``` - -h, --help help for get - --show-deleted Show deleted documents + -h, --help help for get + -i, --identity string Identity of the actor + --show-deleted Show deleted documents ``` ### Options inherited from parent commands @@ -26,11 +30,12 @@ defradb client collection get [--show-deleted] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests --get-inactive Get inactive collections as well as active - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --name string Collection name --no-p2p Disable the peer-to-peer network synchronization system diff --git a/docs/cli/defradb_client_collection_patch.md b/docs/cli/defradb_client_collection_patch.md new file mode 100644 index 0000000000..a5d0179a41 --- /dev/null +++ b/docs/cli/defradb_client_collection_patch.md @@ -0,0 +1,63 @@ +## defradb client collection patch + +Patch existing collection descriptions + +### Synopsis + +Patch existing collection descriptions. + +Uses JSON Patch to modify collection descriptions. + +Example: patch from an argument string: + defradb client collection patch '[{ "op": "add", "path": "...", "value": {...} }]' + +Example: patch from file: + defradb client collection patch -p patch.json + +Example: patch from stdin: + cat patch.json | defradb client collection patch - + +To learn more about the DefraDB GraphQL Schema Language, refer to https://docs.source.network. + +``` +defradb client collection patch [patch] [flags] +``` + +### Options + +``` + -h, --help help for patch + -p, --patch-file string File to load a patch from +``` + +### Options inherited from parent commands + +``` + --allowed-origins stringArray List of origins to allow for CORS requests + --get-inactive Get inactive collections as well as active + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs + --max-txn-retries int Specify the maximum number of retries per transaction (default 5) + --name string Collection name + --no-p2p Disable the peer-to-peer network synchronization system + --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) + --peers stringArray List of peers to connect to + --privkeypath string Path to the private key for tls + --pubkeypath string Path to the public key for tls + --rootdir string Directory for persistent data (default: $HOME/.defradb) + --schema string Collection schema Root + --store string Specify the datastore to use (supported: badger, memory) (default "badger") + --tx uint Transaction ID + --url string URL of HTTP endpoint to listen on or connect to (default "127.0.0.1:9181") + --valuelogfilesize int Specify the datastore value log file size (in bytes). In memory size will be 2*valuelogfilesize (default 1073741824) + --version string Collection version ID +``` + +### SEE ALSO + +* [defradb client collection](defradb_client_collection.md) - Interact with a collection. + diff --git a/docs/cli/defradb_client_collection_update.md b/docs/cli/defradb_client_collection_update.md index 1200cc5b3e..dd2d1864c3 100644 --- a/docs/cli/defradb_client_collection_update.md +++ b/docs/cli/defradb_client_collection_update.md @@ -6,29 +6,34 @@ Update documents by docID or filter. Update documents by docID or filter. -Example: update from string +Example: update from string: defradb client collection update --name User --docID bae-123 '{ "name": "Bob" }' -Example: update by filter +Example: update by filter: defradb client collection update --name User \ --filter '{ "_gte": { "points": 100 } }' --updater '{ "verified": true }' -Example: update by docIDs +Example: update by docIDs: defradb client collection update --name User \ --docID bae-123,bae-456 --updater '{ "verified": true }' + +Example: update private docIDs, with identity: + defradb client collection update -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j --name User \ + --docID bae-123,bae-456 --updater '{ "verified": true }' ``` -defradb client collection update [--filter --docID --updater ] [flags] +defradb client collection update [-i --identity] [--filter --docID --updater ] [flags] ``` ### Options ``` - --docID strings Document ID - --filter string Document filter - -h, --help help for update - --updater string Document updater + --docID strings Document ID + --filter string Document filter + -h, --help help for update + -i, --identity string Identity of the actor + --updater string Document updater ``` ### Options inherited from parent commands @@ -36,11 +41,12 @@ defradb client collection update [--filter --docID --updater ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --name string Collection name --no-p2p Disable the peer-to-peer network synchronization system diff --git a/docs/cli/defradb_client_dump.md b/docs/cli/defradb_client_dump.md index bc00e292b9..3eff3e397e 100644 --- a/docs/cli/defradb_client_dump.md +++ b/docs/cli/defradb_client_dump.md @@ -16,11 +16,12 @@ defradb client dump [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_index.md b/docs/cli/defradb_client_index.md index 0dab1de7fe..22e136c11c 100644 --- a/docs/cli/defradb_client_index.md +++ b/docs/cli/defradb_client_index.md @@ -16,11 +16,12 @@ Manage (create, drop, or list) collection indexes on a DefraDB node. ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_index_create.md b/docs/cli/defradb_client_index_create.md index cbdbbe1d50..9cecd2d0ff 100644 --- a/docs/cli/defradb_client_index_create.md +++ b/docs/cli/defradb_client_index_create.md @@ -33,11 +33,12 @@ defradb client index create -c --collection --fields [-n - ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_index_drop.md b/docs/cli/defradb_client_index_drop.md index bb9e6ec30a..9659958a28 100644 --- a/docs/cli/defradb_client_index_drop.md +++ b/docs/cli/defradb_client_index_drop.md @@ -25,11 +25,12 @@ defradb client index drop -c --collection -n --name [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_index_list.md b/docs/cli/defradb_client_index_list.md index a2d7ca8dd0..ee00938d6e 100644 --- a/docs/cli/defradb_client_index_list.md +++ b/docs/cli/defradb_client_index_list.md @@ -27,11 +27,12 @@ defradb client index list [-c --collection ] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_p2p.md b/docs/cli/defradb_client_p2p.md index 171e2ab661..020506b92f 100644 --- a/docs/cli/defradb_client_p2p.md +++ b/docs/cli/defradb_client_p2p.md @@ -16,11 +16,12 @@ Interact with the DefraDB P2P system ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_p2p_collection.md b/docs/cli/defradb_client_p2p_collection.md index 11ace67212..873362f041 100644 --- a/docs/cli/defradb_client_p2p_collection.md +++ b/docs/cli/defradb_client_p2p_collection.md @@ -17,11 +17,12 @@ The selected collections synchronize their events on the pubsub network. ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_p2p_collection_add.md b/docs/cli/defradb_client_p2p_collection_add.md index c54f235a60..6fa00a5673 100644 --- a/docs/cli/defradb_client_p2p_collection_add.md +++ b/docs/cli/defradb_client_p2p_collection_add.md @@ -28,11 +28,12 @@ defradb client p2p collection add [collectionIDs] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_p2p_collection_getall.md b/docs/cli/defradb_client_p2p_collection_getall.md index 07c536d716..e2946022cb 100644 --- a/docs/cli/defradb_client_p2p_collection_getall.md +++ b/docs/cli/defradb_client_p2p_collection_getall.md @@ -21,11 +21,12 @@ defradb client p2p collection getall [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_p2p_collection_remove.md b/docs/cli/defradb_client_p2p_collection_remove.md index 5a8eb969b6..da09bdf70f 100644 --- a/docs/cli/defradb_client_p2p_collection_remove.md +++ b/docs/cli/defradb_client_p2p_collection_remove.md @@ -28,11 +28,12 @@ defradb client p2p collection remove [collectionIDs] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_p2p_info.md b/docs/cli/defradb_client_p2p_info.md index 27fdf7cb9b..809d84fb6b 100644 --- a/docs/cli/defradb_client_p2p_info.md +++ b/docs/cli/defradb_client_p2p_info.md @@ -20,11 +20,12 @@ defradb client p2p info [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_p2p_replicator.md b/docs/cli/defradb_client_p2p_replicator.md index 725845a726..8b5dc88fdb 100644 --- a/docs/cli/defradb_client_p2p_replicator.md +++ b/docs/cli/defradb_client_p2p_replicator.md @@ -17,11 +17,12 @@ A replicator replicates one or all collection(s) from one node to another. ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_p2p_replicator_delete.md b/docs/cli/defradb_client_p2p_replicator_delete.md index ef89979be6..c5fded6a5b 100644 --- a/docs/cli/defradb_client_p2p_replicator_delete.md +++ b/docs/cli/defradb_client_p2p_replicator_delete.md @@ -26,11 +26,12 @@ defradb client p2p replicator delete [-c, --collection] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_p2p_replicator_getall.md b/docs/cli/defradb_client_p2p_replicator_getall.md index 4d33b5243f..9f983de9fa 100644 --- a/docs/cli/defradb_client_p2p_replicator_getall.md +++ b/docs/cli/defradb_client_p2p_replicator_getall.md @@ -25,11 +25,12 @@ defradb client p2p replicator getall [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_p2p_replicator_set.md b/docs/cli/defradb_client_p2p_replicator_set.md index 55654ded0f..0f3446c87b 100644 --- a/docs/cli/defradb_client_p2p_replicator_set.md +++ b/docs/cli/defradb_client_p2p_replicator_set.md @@ -26,11 +26,12 @@ defradb client p2p replicator set [-c, --collection] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_query.md b/docs/cli/defradb_client_query.md index b23bf50553..cd320e9de6 100644 --- a/docs/cli/defradb_client_query.md +++ b/docs/cli/defradb_client_query.md @@ -12,6 +12,9 @@ A query request can be sent as a single argument. Example command: Do a query request from a file by using the '-f' flag. Example command: defradb client query -f request.graphql +Do a query request from a file and with an identity. Example command: + defradb client query -i cosmos1f2djr7dl9vhrk3twt3xwqp09nhtzec9mdkf70j -f request.graphql + Or it can be sent via stdin by using the '-' special syntax. Example command: cat request.graphql | defradb client query - @@ -21,25 +24,27 @@ with the database more conveniently. To learn more about the DefraDB GraphQL Query Language, refer to https://docs.source.network. ``` -defradb client query [query request] [flags] +defradb client query [-i --identity] [request] [flags] ``` ### Options ``` - -f, --file string File containing the query request - -h, --help help for query + -f, --file string File containing the query request + -h, --help help for query + -i, --identity string Identity of the actor ``` ### Options inherited from parent commands ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_schema.md b/docs/cli/defradb_client_schema.md index d37251c8db..2e5a7db88c 100644 --- a/docs/cli/defradb_client_schema.md +++ b/docs/cli/defradb_client_schema.md @@ -16,11 +16,12 @@ Make changes, updates, or look for existing schema types. ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_schema_add.md b/docs/cli/defradb_client_schema_add.md index e0ad675241..dc72a6a354 100644 --- a/docs/cli/defradb_client_schema_add.md +++ b/docs/cli/defradb_client_schema_add.md @@ -6,6 +6,11 @@ Add new schema Add new schema. +Schema Object with a '@policy(id:".." resource: "..")' linked will only be accepted if: + - ACP is available (i.e. ACP is not disabled). + - The specified resource adheres to the Document Access Control DPI Rules. + - Learn more about [ACP & DPI Rules](/acp/README.md) + Example: add from an argument string: defradb client schema add 'type Foo { ... }' @@ -32,11 +37,12 @@ defradb client schema add [schema] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_schema_describe.md b/docs/cli/defradb_client_schema_describe.md index cd79cce3c1..3ab0c1dda8 100644 --- a/docs/cli/defradb_client_schema_describe.md +++ b/docs/cli/defradb_client_schema_describe.md @@ -36,11 +36,12 @@ defradb client schema describe [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_schema_migration.md b/docs/cli/defradb_client_schema_migration.md index b49420401c..2ee26c8521 100644 --- a/docs/cli/defradb_client_schema_migration.md +++ b/docs/cli/defradb_client_schema_migration.md @@ -16,11 +16,12 @@ Make set or look for existing schema migrations on a DefraDB node. ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_schema_migration_down.md b/docs/cli/defradb_client_schema_migration_down.md index 6172bf09b1..e5541396f6 100644 --- a/docs/cli/defradb_client_schema_migration_down.md +++ b/docs/cli/defradb_client_schema_migration_down.md @@ -33,11 +33,12 @@ defradb client schema migration down --collection [fl ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_schema_migration_get.md b/docs/cli/defradb_client_schema_migration_get.md deleted file mode 100644 index 20ed8edb91..0000000000 --- a/docs/cli/defradb_client_schema_migration_get.md +++ /dev/null @@ -1,41 +0,0 @@ -## defradb client schema migration get - -Gets the schema migrations within DefraDB - -### Synopsis - -Gets the schema migrations within the local DefraDB node. - -Example: - defradb client schema migration get' - -Learn more about the DefraDB GraphQL Schema Language on https://docs.source.network. - -``` -defradb client schema migration get [flags] -``` - -### Options - -``` - -h, --help help for get -``` - -### Options inherited from parent commands - -``` - --logformat string Log format to use. Options are csv, json (default "csv") - --logger stringArray Override logger parameters. Usage: --logger ,level=,output=,... - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs - --rootdir string Directory for data and configuration to use (default: $HOME/.defradb) - --tx uint Transaction ID - --url string URL of HTTP endpoint to listen on or connect to (default "localhost:9181") -``` - -### SEE ALSO - -* [defradb client schema migration](defradb_client_schema_migration.md) - Interact with the schema migration system of a running DefraDB instance - diff --git a/docs/cli/defradb_client_schema_migration_reload.md b/docs/cli/defradb_client_schema_migration_reload.md index 01051e419a..ef89b749e8 100644 --- a/docs/cli/defradb_client_schema_migration_reload.md +++ b/docs/cli/defradb_client_schema_migration_reload.md @@ -20,11 +20,12 @@ defradb client schema migration reload [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_schema_migration_set-registry.md b/docs/cli/defradb_client_schema_migration_set-registry.md index 8e80aa132d..2eae5aba48 100644 --- a/docs/cli/defradb_client_schema_migration_set-registry.md +++ b/docs/cli/defradb_client_schema_migration_set-registry.md @@ -26,11 +26,12 @@ defradb client schema migration set-registry [collectionID] [cfg] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_schema_migration_set.md b/docs/cli/defradb_client_schema_migration_set.md index 9e6bcfcfc4..1a5f923218 100644 --- a/docs/cli/defradb_client_schema_migration_set.md +++ b/docs/cli/defradb_client_schema_migration_set.md @@ -33,11 +33,12 @@ defradb client schema migration set [src] [dst] [cfg] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_schema_migration_up.md b/docs/cli/defradb_client_schema_migration_up.md index bcd28453cf..62fd063cd4 100644 --- a/docs/cli/defradb_client_schema_migration_up.md +++ b/docs/cli/defradb_client_schema_migration_up.md @@ -33,11 +33,12 @@ defradb client schema migration up --collection [flag ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_schema_patch.md b/docs/cli/defradb_client_schema_patch.md index f24670b945..393786eb05 100644 --- a/docs/cli/defradb_client_schema_patch.md +++ b/docs/cli/defradb_client_schema_patch.md @@ -12,7 +12,7 @@ Example: patch from an argument string: defradb client schema patch '[{ "op": "add", "path": "...", "value": {...} }]' '{"lenses": [...' Example: patch from file: - defradb client schema patch -f patch.json + defradb client schema patch -p patch.json Example: patch from stdin: cat patch.json | defradb client schema patch - @@ -36,11 +36,12 @@ defradb client schema patch [schema] [migration] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_schema_set-active.md b/docs/cli/defradb_client_schema_set-active.md index ff94ff88fe..77ec843f9c 100644 --- a/docs/cli/defradb_client_schema_set-active.md +++ b/docs/cli/defradb_client_schema_set-active.md @@ -21,11 +21,12 @@ defradb client schema set-active [versionID] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_schema_set-default.md b/docs/cli/defradb_client_schema_set-default.md deleted file mode 100644 index 0698b0e6d5..0000000000 --- a/docs/cli/defradb_client_schema_set-default.md +++ /dev/null @@ -1,36 +0,0 @@ -## defradb client schema set-default - -Set the default schema version - -### Synopsis - -Set the default schema version - -``` -defradb client schema set-default [versionID] [flags] -``` - -### Options - -``` - -h, --help help for set-default -``` - -### Options inherited from parent commands - -``` - --logformat string Log format to use. Options are csv, json (default "csv") - --logger stringArray Override logger parameters. Usage: --logger ,level=,output=,... - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs - --rootdir string Directory for data and configuration to use (default: $HOME/.defradb) - --tx uint Transaction ID - --url string URL of HTTP endpoint to listen on or connect to (default "localhost:9181") -``` - -### SEE ALSO - -* [defradb client schema](defradb_client_schema.md) - Interact with the schema system of a DefraDB node - diff --git a/docs/cli/defradb_client_tx.md b/docs/cli/defradb_client_tx.md index 65f7740419..f3007f574e 100644 --- a/docs/cli/defradb_client_tx.md +++ b/docs/cli/defradb_client_tx.md @@ -16,11 +16,12 @@ Create, commit, and discard DefraDB transactions ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_tx_commit.md b/docs/cli/defradb_client_tx_commit.md index 621459e134..e9ae2e529d 100644 --- a/docs/cli/defradb_client_tx_commit.md +++ b/docs/cli/defradb_client_tx_commit.md @@ -20,11 +20,12 @@ defradb client tx commit [id] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_tx_create.md b/docs/cli/defradb_client_tx_create.md index cf695da6c7..a2c45a9b44 100644 --- a/docs/cli/defradb_client_tx_create.md +++ b/docs/cli/defradb_client_tx_create.md @@ -22,11 +22,12 @@ defradb client tx create [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_tx_discard.md b/docs/cli/defradb_client_tx_discard.md index 7340bedf2a..2b1b4badb8 100644 --- a/docs/cli/defradb_client_tx_discard.md +++ b/docs/cli/defradb_client_tx_discard.md @@ -20,11 +20,12 @@ defradb client tx discard [id] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_view.md b/docs/cli/defradb_client_view.md index 9b93884430..8b8f47e8bc 100644 --- a/docs/cli/defradb_client_view.md +++ b/docs/cli/defradb_client_view.md @@ -16,11 +16,12 @@ Manage (add) views withing a running DefraDB instance ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_client_view_add.md b/docs/cli/defradb_client_view_add.md index cdbab25a51..e522b86f1b 100644 --- a/docs/cli/defradb_client_view_add.md +++ b/docs/cli/defradb_client_view_add.md @@ -26,11 +26,12 @@ defradb client view add [query] [sdl] [transform] [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_init.md b/docs/cli/defradb_init.md deleted file mode 100644 index f8d69f5794..0000000000 --- a/docs/cli/defradb_init.md +++ /dev/null @@ -1,37 +0,0 @@ -## defradb init - -Initialize DefraDB's root directory and configuration file - -### Synopsis - -Initialize a directory for configuration and data at the given path. -Passed flags will be persisted in the stored configuration. - -``` -defradb init [flags] -``` - -### Options - -``` - -h, --help help for init - --reinitialize Reinitialize the configuration file -``` - -### Options inherited from parent commands - -``` - --logformat string Log format to use. Options are csv, json (default "csv") - --logger stringArray Override logger parameters. Usage: --logger ,level=,output=,... - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs - --rootdir string Directory for data and configuration to use (default: $HOME/.defradb) - --url string URL of HTTP endpoint to listen on or connect to (default "localhost:9181") -``` - -### SEE ALSO - -* [defradb](defradb.md) - DefraDB Edge Database - diff --git a/docs/cli/defradb_server-dump.md b/docs/cli/defradb_server-dump.md index 2b590da6fe..ff58487c65 100644 --- a/docs/cli/defradb_server-dump.md +++ b/docs/cli/defradb_server-dump.md @@ -16,11 +16,12 @@ defradb server-dump [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_start.md b/docs/cli/defradb_start.md index 2591f9bc06..4a4edeaf48 100644 --- a/docs/cli/defradb_start.md +++ b/docs/cli/defradb_start.md @@ -20,11 +20,12 @@ defradb start [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/docs/cli/defradb_version.md b/docs/cli/defradb_version.md index ce43eb148c..810d0dc477 100644 --- a/docs/cli/defradb_version.md +++ b/docs/cli/defradb_version.md @@ -18,11 +18,12 @@ defradb version [flags] ``` --allowed-origins stringArray List of origins to allow for CORS requests - --logformat string Log format to use. Options are csv, json (default "csv") - --loglevel string Log level to use. Options are debug, info, error, fatal (default "info") - --lognocolor Disable colored log output - --logoutput string Log output path (default "stderr") - --logtrace Include stacktrace in error and fatal logs + --log-format string Log format to use. Options are text or json (default "text") + --log-level string Log level to use. Options are debug, info, error, fatal (default "info") + --log-output string Log output path. Options are stderr or stdout. (default "stderr") + --log-overrides string Logger config overrides. Format ,=,...;,... + --log-source Include source location in logs + --log-stacktrace Include stacktrace in error and fatal logs --max-txn-retries int Specify the maximum number of retries per transaction (default 5) --no-p2p Disable the peer-to-peer network synchronization system --p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171]) diff --git a/examples/dpi_policy/user_dpi_policy.json b/examples/dpi_policy/user_dpi_policy.json new file mode 100644 index 0000000000..74028d8ee6 --- /dev/null +++ b/examples/dpi_policy/user_dpi_policy.json @@ -0,0 +1,30 @@ +{ + "description": "A Valid Defra Policy Interface (DPI)", + "actor": { + "name": "actor" + }, + "resources": { + "users": { + "permissions": { + "read": { + "expr": "owner + reader" + }, + "write": { + "expr": "owner" + } + }, + "relations": { + "owner": { + "types": [ + "actor" + ] + }, + "reader": { + "types": [ + "actor" + ] + } + } + } + } +} diff --git a/examples/dpi_policy/user_dpi_policy.yml b/examples/dpi_policy/user_dpi_policy.yml new file mode 100644 index 0000000000..fafae06957 --- /dev/null +++ b/examples/dpi_policy/user_dpi_policy.yml @@ -0,0 +1,29 @@ +# The below policy contains an example with valid DPI compliant resource that can be linked to a collection +# object during the schema add command to have access control enabled for documents of that collection. +# +# This policy is specified to the Users object example in: `examples/schema/permissioned/users.graphql` +# +# The same policy example in json format is in: `examples/dpi_policy/user_dpi_policy.json` +# +# Learn more about the DefraDB Policy Interface [DPI](/acp/README.md) + +description: A Valid DefraDB Policy Interface (DPI) + +actor: + name: actor + +resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor diff --git a/examples/schema/permissioned/book.graphql b/examples/schema/permissioned/book.graphql new file mode 100644 index 0000000000..96bdcbb877 --- /dev/null +++ b/examples/schema/permissioned/book.graphql @@ -0,0 +1,14 @@ +# The below sdl contains an example `Book` object with an example source hub policy id and resource name. +# +# The policy id must exist in sourcehub (for remote acp) or local acp first, and the resource name +# must exist on the corresponding policy to the policy id. +# +# Note: The resource name does not need to be similar to the collection name. +# +# The policy must be a valid DPI, learn more about the DefraDB Policy Interface [DPI](/acp/README.md) + +type Book @policy(id:"7dc51aabc0248cf106265c902bf56faa1989ec41a6bbd36b6e438cfade7aee4a", resource:"book") { + name: String + rating: Float +} + diff --git a/examples/schema/permissioned/users.graphql b/examples/schema/permissioned/users.graphql new file mode 100644 index 0000000000..771e6da2c9 --- /dev/null +++ b/examples/schema/permissioned/users.graphql @@ -0,0 +1,18 @@ +# The below sdl contains an example `Users` object with an example source hub policy id and resource name. +# +# The policy id must exist in sourcehub (for remote acp) or local acp first, and the resource name +# must exist on the corresponding policy to the policy id. +# +# The resource name does not need to be similar to the collection name. +# +# The linked policy id and resource correspond to the policy at: `examples/dpi_policy/user_dpi_policy.yml` +# +# The policy must be a valid DPI, learn more about the DefraDB Policy Interface [DPI](/acp/README.md) + +type Users @policy( + id: "24ab8cba6d6f0bcfe4d2712c7d95c09dd1b8076ea5a8896476413fd6c891c18c", + resource: "users" +) { + name: String + age: Int +} diff --git a/go.mod b/go.mod index eff296f3e3..35047db29f 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.21.3 require ( github.com/bits-and-blooms/bitset v1.13.0 github.com/bxcodec/faker v2.0.1+incompatible + github.com/cosmos/gogoproto v1.4.11 github.com/evanphx/json-patch/v5 v5.9.0 github.com/fxamacker/cbor/v2 v2.6.0 github.com/getkin/kin-openapi v0.123.0 @@ -34,6 +35,7 @@ require ( github.com/sourcenetwork/go-libp2p-pubsub-rpc v0.0.13 github.com/sourcenetwork/graphql-go v0.7.10-0.20231113214537-a9560c1898dd github.com/sourcenetwork/immutable v0.3.0 + github.com/sourcenetwork/sourcehub v0.2.1-0.20240305165631-9b75b1000724 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 @@ -45,53 +47,131 @@ require ( go.opentelemetry.io/otel/metric v1.24.0 go.opentelemetry.io/otel/sdk/metric v1.24.0 go.uber.org/zap v1.27.0 - golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc + golang.org/x/exp v0.0.0-20240213143201-ec583247a57a google.golang.org/grpc v1.62.1 - google.golang.org/protobuf v1.33.0 + google.golang.org/protobuf v1.32.0 ) require ( + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1 // indirect + cosmossdk.io/api v0.7.3 // indirect + cosmossdk.io/collections v0.4.0 // indirect + cosmossdk.io/core v0.11.0 // indirect + cosmossdk.io/depinject v1.0.0-alpha.4 // indirect + cosmossdk.io/errors v1.0.1 // indirect + cosmossdk.io/log v1.3.1 // indirect + cosmossdk.io/math v1.2.0 // indirect + cosmossdk.io/store v1.0.2 // indirect + cosmossdk.io/x/tx v0.13.0 // indirect + filippo.io/edwards25519 v1.0.0 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.1 // indirect + github.com/DataDog/datadog-go v3.2.0+incompatible // indirect + github.com/DataDog/zstd v1.5.5 // indirect github.com/Jorropo/jsync v1.0.1 // indirect + github.com/NathanBaulch/protoc-gen-cobra v1.2.1 // indirect + github.com/awalterschulze/gographviz v2.0.3+incompatible // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect + github.com/btcsuite/btcd v0.22.1 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect github.com/bytecodealliance/wasmtime-go/v15 v15.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.0 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/cometbft/cometbft v0.38.5 // indirect + github.com/cometbft/cometbft-db v0.9.1 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-db v1.0.0 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.4 // indirect + github.com/cosmos/cosmos-sdk v0.50.4 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/gorocksdb v1.2.0 // indirect + github.com/cosmos/iavl v1.0.1 // indirect + github.com/cosmos/ics23/go v0.10.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/cskr/pubsub v1.0.2 // indirect + github.com/danieljoos/wincred v1.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect + github.com/dgraph-io/badger/v2 v2.2007.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dvsekhvalnov/jose2go v1.6.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect + github.com/emicklei/dot v1.6.1 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/flynn/noise v1.0.1 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/go-jose/go-jose/v3 v3.0.1-0.20221117193127-916db76e8214 // indirect + github.com/go-kit/kit v0.12.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.20.2 // indirect github.com/go-openapi/swag v0.22.8 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/btree v1.1.2 // indirect github.com/google/flatbuffers v2.0.6+incompatible // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gopacket v1.1.19 // indirect + github.com/google/orderedcode v0.0.1 // indirect github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/handlers v1.5.2 // indirect + github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-plugin v1.5.2 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/golang-lru/arc/v2 v2.0.5 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hdevalence/ed25519consensus v0.1.0 // indirect + github.com/huandu/skiplist v1.2.0 // indirect github.com/huin/goupnp v1.3.0 // indirect + github.com/hyperledger/aries-framework-go v0.3.2 // indirect + github.com/hyperledger/aries-framework-go/component/kmscrypto v0.0.0-20230427134832-0c9969493bd3 // indirect + github.com/hyperledger/aries-framework-go/component/log v0.0.0-20230427134832-0c9969493bd3 // indirect + github.com/hyperledger/aries-framework-go/component/models v0.0.0-20230501135648-a9a7ad029347 // indirect + github.com/hyperledger/aries-framework-go/spi v0.0.0-20230427134832-0c9969493bd3 // indirect + github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/yaml v0.2.0 // indirect github.com/ipfs/bbloom v0.0.4 // indirect @@ -107,10 +187,15 @@ require ( github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect + github.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69 // indirect + github.com/klauspost/compress v1.17.6 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/koron/go-ssdp v0.0.4 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.10.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect @@ -122,18 +207,22 @@ require ( github.com/libp2p/go-netroute v0.2.1 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.1 // indirect + github.com/linxGnu/grocksdb v1.8.12 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/miekg/dns v1.1.57 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect + github.com/minio/highwayhash v1.0.2 // indirect github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/mr-tron/base58 v1.2.0 // indirect + github.com/mtibben/percent v0.2.1 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect @@ -141,38 +230,58 @@ require ( github.com/multiformats/go-multicodec v0.9.0 // indirect github.com/multiformats/go-multistream v0.5.0 // indirect github.com/multiformats/go-varint v0.0.7 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect + github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect + github.com/oklog/run v1.1.0 // indirect github.com/onsi/ginkgo/v2 v2.13.2 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect + github.com/piprate/json-gold v0.5.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polydawn/refmt v0.89.0 // indirect + github.com/pquerna/cachecontrol v0.1.0 // indirect github.com/prometheus/client_golang v1.18.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.47.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/quic-go/quic-go v0.40.1 // indirect github.com/quic-go/webtransport-go v0.6.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/cors v1.10.1 // indirect + github.com/rs/zerolog v1.32.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/sourcegraph/conc v0.3.0 // indirect + github.com/sourcenetwork/raccoondb v0.2.0 // indirect + github.com/sourcenetwork/zanzi v0.3.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/tendermint/go-amino v0.16.0 // indirect + github.com/tendermint/tm-db v0.6.7 // indirect + github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 // indirect github.com/textileio/go-log/v2 v2.1.3-gke-2 // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect + go.etcd.io/bbolt v1.3.8 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel v1.24.0 // indirect go.opentelemetry.io/otel/sdk v1.24.0 // indirect @@ -182,15 +291,22 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.19.0 // indirect - golang.org/x/mod v0.14.0 // indirect + golang.org/x/mod v0.15.0 // indirect golang.org/x/net v0.21.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.17.0 // indirect + golang.org/x/term v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.16.1 // indirect + golang.org/x/tools v0.18.0 // indirect gonum.org/v1/gonum v0.14.0 // indirect + google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.5.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect + nhooyr.io/websocket v1.8.7 // indirect + pgregory.net/rapid v1.1.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 22c2ef750c..925f4e9590 100644 --- a/go.sum +++ b/go.sum @@ -1,61 +1,228 @@ +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1 h1:tdpHgTbmbvEIARu+bixzmleMi14+3imnpoFXz+Qzjp4= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= +cosmossdk.io/api v0.7.3 h1:V815i8YOwOAQa1rLCsSMjVG5Gnzs02JLq+l7ks8s1jk= +cosmossdk.io/api v0.7.3/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= +cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= +cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= +cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= +cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= +cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= +cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= +cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= +cosmossdk.io/math v1.2.0 h1:8gudhTkkD3NxOP2YyyJIYYmt6dQ55ZfJkDOaxXpy7Ig= +cosmossdk.io/math v1.2.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= +cosmossdk.io/store v1.0.2 h1:lSg5BTvJBHUDwswNNyeh4K/CbqiHER73VU4nDNb8uk0= +cosmossdk.io/store v1.0.2/go.mod h1:EFtENTqVTuWwitGW1VwaBct+yDagk7oG/axBMPH+FXs= +cosmossdk.io/x/tx v0.13.0 h1:8lzyOh3zONPpZv2uTcUmsv0WTXy6T1/aCVDCqShmpzU= +cosmossdk.io/x/tx v0.13.0/go.mod h1:CpNQtmoqbXa33/DVxWQNx5Dcnbkv2xGUhL7tYQ5wUsY= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= +github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= -github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Jorropo/jsync v1.0.1 h1:6HgRolFZnsdfzRUj+ImB9og1JYOxQoReSywkHOGSaUU= github.com/Jorropo/jsync v1.0.1/go.mod h1:jCOZj3vrBCri3bSU3ErUYvevKlnbssrXeCivybS5ABQ= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/NathanBaulch/protoc-gen-cobra v1.2.1 h1:BOqX9glwicbqDJDGndMnhHhx8psGTSjGdZzRDY1a7A8= +github.com/NathanBaulch/protoc-gen-cobra v1.2.1/go.mod h1:ZLPLEPQgV3jP3a7IEp+xxYPk8tF4lhY9ViV0hn6K3iA= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= +github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs= github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= +github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= +github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= +github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= +github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40Nwln+M/+faA= github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6rHShT+veBxNCnjCx5XM= github.com/bytecodealliance/wasmtime-go/v15 v15.0.0 h1:4R2MpSPPbtSxqdsOTvsMn1pnwdEhzbDGMao6LUUSLv4= github.com/bytecodealliance/wasmtime-go/v15 v15.0.0/go.mod h1:m6vB/SsM+pnJkVHmO1wzHYUeYtciltTKuxuvkR8pYcY= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= +github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= +github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/cometbft/cometbft v0.38.5 h1:4lOcK5VTPrfbLOhNHmPYe6c7eDXHtBdMCQuKbAfFJdU= +github.com/cometbft/cometbft v0.38.5/go.mod h1:0tqKin+KQs8zDwzYD8rPHzSBIDNPuB4NrwwGDNb/hUg= +github.com/cometbft/cometbft-db v0.9.1 h1:MIhVX5ja5bXNHF8EYrThkG9F7r9kSfv8BX4LWaxWJ4M= +github.com/cometbft/cometbft-db v0.9.1/go.mod h1:iliyWaoV0mRwBJoizElCwwRA9Tf7jZJOURcRZF9m60U= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= +github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= +github.com/cosmos/cosmos-db v1.0.0 h1:EVcQZ+qYag7W6uorBKFPvX6gRjw6Uq2hIh4hCWjuQ0E= +github.com/cosmos/cosmos-db v1.0.0/go.mod h1:iBvi1TtqaedwLdcrZVYRSSCb6eSy61NLj4UNmdIgs0U= +github.com/cosmos/cosmos-proto v1.0.0-beta.4 h1:aEL7tU/rLOmxZQ9z4i7mzxcLbSCY48OdY7lIWTLG7oU= +github.com/cosmos/cosmos-proto v1.0.0-beta.4/go.mod h1:oeB+FyVzG3XrQJbJng0EnV8Vljfk9XvTIpGILNU/9Co= +github.com/cosmos/cosmos-sdk v0.50.4 h1:hQT5/+Z1XXNF7skaPq0i247Ts2dzzqg/j2WO/BPHSto= +github.com/cosmos/cosmos-sdk v0.50.4/go.mod h1:UbShFs6P8Ly29xxJvkNGaNaL/UGj5a686NRtb1Cqra0= +github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= +github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= +github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= +github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= +github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= +github.com/cosmos/gogoproto v1.4.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR2g= +github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y= +github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y= +github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= +github.com/cosmos/iavl v1.0.1 h1:D+mYbcRO2wptYzOM1Hxl9cpmmHU1ZEt9T2Wv5nZTeUw= +github.com/cosmos/iavl v1.0.1/go.mod h1:8xIUkgVvwvVrBu81scdPty+/Dx9GqwHnAvXz4cwF7RY= +github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= +github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= +github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 h1:ZFUue+PNxmHlu7pYv+IYMtqlaO/0VwaGEqKepZf9JpA= github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= +github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= +github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -66,45 +233,91 @@ github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5il github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= +github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= github.com/dgraph-io/badger/v3 v3.2011.1 h1:Hmyof0WMEF/QtutX5SQHzIMnJQxb/IrSzhjckV2SD6g= github.com/dgraph-io/badger/v3 v3.2011.1/go.mod h1:0rLLrQpKVQAL0or/lBLMQznhr6dWWX7h5AKnmnqx268= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= +github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= +github.com/emicklei/dot v1.6.1 h1:ujpDlBkkwgWUY+qPId5IwapRW/xEoligRSYjioR6DFI= +github.com/emicklei/dot v1.6.1/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.0.1 h1:vPp/jdQLXC6ppsXSj/pM3W1BIJ5FEHE2TulSJBpb43Y= github.com/flynn/noise v1.0.1/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/getkin/kin-openapi v0.123.0 h1:zIik0mRwFNLyvtXK274Q6ut+dPh6nlxBp0x7mNrPhs8= github.com/getkin/kin-openapi v0.123.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= @@ -113,6 +326,22 @@ github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vz github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v3 v3.0.1-0.20221117193127-916db76e8214 h1:w5li6eMV6NCHh1YVbKRM/gMCVtZ2w7mnwq78eNnHXQQ= +github.com/go-jose/go-jose/v3 v3.0.1-0.20221117193127-916db76e8214/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -122,34 +351,72 @@ github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbX github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ= +github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -159,11 +426,17 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/flatbuffers v2.0.6+incompatible h1:XHFReMv7nFFusa+CEokzWbzaYocKXI6C7hdU5Kgh9Lw= github.com/google/flatbuffers v2.0.6+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -174,17 +447,29 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= +github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8= github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= +github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -193,15 +478,62 @@ github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE0 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk= github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.2 h1:ErEYO2f//CjKsUDw4SmLzelsK6L3ZmOAR/4P9iS7ruY= +github.com/hashicorp/go-metrics v0.5.2/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y= +github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw= @@ -210,15 +542,47 @@ github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= +github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hsanjuan/ipfs-lite v1.8.1 h1:Rpd9bTXYgkmnt8M5QsZnWwtW6ebxAB7HlU/d0zE4BmA= github.com/hsanjuan/ipfs-lite v1.8.1/go.mod h1:oGCaHBi+I73UFjc6wPAQ75hr4FjJhoqy6YPZjtghDIc= +github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= +github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= +github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= +github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/hyperledger/aries-framework-go v0.3.2 h1:GsSUaSEW82cr5X8b3Qf90GAi37kmTKHqpPJLhar13X8= +github.com/hyperledger/aries-framework-go v0.3.2/go.mod h1:SorUysWEBw+uyXhY5RAtg2iyNkWTIIPM8+Slkt1Spno= +github.com/hyperledger/aries-framework-go/component/kmscrypto v0.0.0-20230427134832-0c9969493bd3 h1:PCbDSujjQ6oTEnAHgtThNmbS7SPAYEDBlKOnZFE+Ujw= +github.com/hyperledger/aries-framework-go/component/kmscrypto v0.0.0-20230427134832-0c9969493bd3/go.mod h1:aEk0vHBmZsAdDfXaI12Kg5ipZGiB3qNqgbPt/e/Hm2s= +github.com/hyperledger/aries-framework-go/component/log v0.0.0-20230427134832-0c9969493bd3 h1:x5qFQraTX86z9GCwF28IxfnPm6QH5YgHaX+4x97Jwvw= +github.com/hyperledger/aries-framework-go/component/log v0.0.0-20230427134832-0c9969493bd3/go.mod h1:CvYs4l8X2NrrF93weLOu5RTOIJeVdoZITtjEflyuTyM= +github.com/hyperledger/aries-framework-go/component/models v0.0.0-20230501135648-a9a7ad029347 h1:oPGUCpmnm7yxsVllcMQnHF3uc3hy4jfrSCh7nvzXA00= +github.com/hyperledger/aries-framework-go/component/models v0.0.0-20230501135648-a9a7ad029347/go.mod h1:nF8fHsYY+GZl74AFAQaKAhYWOOSaLVzW/TZ0Sq/6axI= +github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20230427134832-0c9969493bd3 h1:JGYA9l5zTlvsvfnXT9hYPpCokAjmVKX0/r7njba7OX4= +github.com/hyperledger/aries-framework-go/component/storageutil v0.0.0-20230427134832-0c9969493bd3/go.mod h1:aSG2dWjYVzu2PVBtOqsYghaChA5+UUXnBbL+MfVceYQ= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20230427134832-0c9969493bd3 h1:ytWmOQZIYQfVJ4msFvrqlp6d+ZLhT43wS8rgE2m+J1A= +github.com/hyperledger/aries-framework-go/spi v0.0.0-20230427134832-0c9969493bd3/go.mod h1:oryUyWb23l/a3tAP9KW+GBbfcfqp9tZD4y5hSkFrkqI= +github.com/hyperledger/ursa-wrapper-go v0.3.1 h1:Do+QrVNniY77YK2jTIcyWqj9rm/Yb5SScN0bqCjiibA= +github.com/hyperledger/ursa-wrapper-go v0.3.1/go.mod h1:nPSAuMasIzSVciQo22PedBk4Opph6bJ6ia3ms7BH/mk= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= +github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= @@ -276,21 +640,48 @@ github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPw github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= +github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69 h1:kMJlf8z8wUcpyI+FQJIdGjAhfTww1y0AbQEv86bpVQI= +github.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69/go.mod h1:tlkavyke+Ac7h8R3gZIjI5LKBcvMlSWnXNMgT3vZXo8= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -302,6 +693,11 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lens-vm/lens/host-go v0.0.0-20231127204031-8d858ed2926c h1:bG+mr4SqbYRU69L6CSvHDsKbRg5Q9vaN2T5g7qcrPdQ= github.com/lens-vm/lens/host-go v0.0.0-20231127204031-8d858ed2926c/go.mod h1:a4edl+KcOVk1Nj3EjG77htqg2/0Mmy3bSG0kl+FWVqQ= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= @@ -338,21 +734,39 @@ github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCy github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q= github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/linxGnu/grocksdb v1.8.12 h1:1/pCztQUOa3BX/1gR3jSZDoaKFpeHFvQ1XrqZpSvZVo= +github.com/linxGnu/grocksdb v1.8.12/go.mod h1:xZCIb5Muw+nhbDK4Y5UJuOrin5MceOuiXkVUR7vp4WY= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= @@ -363,20 +777,38 @@ github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKo github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= @@ -401,55 +833,140 @@ github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dy github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a h1:dlRvE5fWabOchtH7znfiFCcOvmIYgOeAS5ifBXBlh9Q= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs= github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= +github.com/opencontainers/runc v1.1.3/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= +github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc h1:8bQZVK1X6BJR/6nYUPxQEP+ReTsceJTKizeuwjWOPUA= +github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/piprate/json-gold v0.5.0 h1:RmGh1PYboCFcchVFuh2pbSWAZy4XJaqTMU4KQYsApbM= +github.com/piprate/json-gold v0.5.0/go.mod h1:WZ501QQMbZZ+3pXFPhQKzNwS1+jls0oqov3uQ2WasLs= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= +github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.47.0 h1:p5Cz0FNHo7SnWOmWmoRozVcjEp0bIVU8cV7OShpjL1k= +github.com/prometheus/common v0.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= @@ -462,19 +979,36 @@ github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFD github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= +github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= @@ -499,11 +1033,20 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= @@ -518,19 +1061,38 @@ github.com/sourcenetwork/graphql-go v0.7.10-0.20231113214537-a9560c1898dd h1:lmp github.com/sourcenetwork/graphql-go v0.7.10-0.20231113214537-a9560c1898dd/go.mod h1:rkahXkgRH/3vZErN1Bx+qt1+w+CV5fgaJyKKWgISe4U= github.com/sourcenetwork/immutable v0.3.0 h1:gHPtGvLrTBTK5YpDAhMU+u+S8v1F6iYmc3nbZLryMdc= github.com/sourcenetwork/immutable v0.3.0/go.mod h1:GD7ceuh/HD7z6cdIwzKK2ctzgZ1qqYFJpsFp+8qYnbI= +github.com/sourcenetwork/raccoondb v0.2.0 h1:lQ/r8IUm1IMaivXWhqndgpisLsI59c6M9jn6ujKYBzk= +github.com/sourcenetwork/raccoondb v0.2.0/go.mod h1:A5ElVAhdf9yDjmpLrA3DLqYib09Fnuzm3sFUbY5r9BE= +github.com/sourcenetwork/sourcehub v0.2.1-0.20240305165631-9b75b1000724 h1:Dr13Lb9bTmycQZbNHAP+7RUVcy9g6jxL5rz74ipVyrs= +github.com/sourcenetwork/sourcehub v0.2.1-0.20240305165631-9b75b1000724/go.mod h1:jhWsUtCgIE6vDKg9/uvu1rXAOcVTrALjBXf2kLQGrCk= +github.com/sourcenetwork/zanzi v0.3.0 h1:Y9uyrpsT569QjzAxNOwWDxeWOkcntm+26qDLR7nGuo4= +github.com/sourcenetwork/zanzi v0.3.0/go.mod h1:eLQ94tdz96vfwHIZXL5ZoHbV9YHQeMyFeTc5hFSGDRU= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= @@ -542,6 +1104,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -549,9 +1112,15 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= +github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tendermint/tm-db v0.6.7 h1:fE00Cbl0jayAoqlExN6oyQJ7fR/ZtoVOmvPJ//+shu8= +github.com/tendermint/tm-db v0.6.7/go.mod h1:byQDzFkZV1syXr/ReXS808NxA2xvyuuVgXOJ/088L6I= +github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 h1:RBkacARv7qY5laaXGlF4wFB/tk5rnthhPb8oIBGoagY= +github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8/go.mod h1:9PdLyPiZIiW3UopXyRnPYyjUXSpiQNHRLu8fOsR3o8M= github.com/textileio/go-datastore-extensions v1.0.1 h1:qIJGqJaigQ1wD4TdwS/hf73u0HChhXvvUSJuxBEKS+c= github.com/textileio/go-datastore-extensions v1.0.1/go.mod h1:Pzj9FDRkb55910dr/FX8M7WywvnS26gBgEDez1ZBuLE= github.com/textileio/go-ds-badger3 v0.1.0 h1:q0kBuBmAcRUR3ClMSYlyw0224XeuzjjGinU53Qz1uXI= @@ -560,8 +1129,15 @@ github.com/textileio/go-log/v2 v2.1.3-gke-2 h1:YkMA5ua0Cf/X6CkbexInsoJ/HdaHQBlgi github.com/textileio/go-log/v2 v2.1.3-gke-2/go.mod h1:DwACkjFS3kjZZR/4Spx3aPfSsciyslwUe5bxV8CEU2w= github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= @@ -582,10 +1158,30 @@ github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdz github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= @@ -598,6 +1194,10 @@ go.opentelemetry.io/otel/sdk/metric v1.24.0 h1:yyMQrPzF+k88/DbH7o4FMAs80puqd+9os go.opentelemetry.io/otel/sdk/metric v1.24.0/go.mod h1:I6Y5FjH6rvEnTTAYQz3Mmv2kl6Ek5IIrmwTLqMrrOE0= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -606,74 +1206,111 @@ go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -686,42 +1323,86 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -729,28 +1410,34 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -760,18 +1447,41 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -782,25 +1492,39 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -810,6 +1534,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -818,5 +1544,14 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/http/client.go b/http/client.go index 33b9c21fb8..69c5f2a503 100644 --- a/http/client.go +++ b/http/client.go @@ -22,9 +22,10 @@ import ( blockstore "github.com/ipfs/boxo/blockstore" "github.com/lens-vm/lens/host-go/config/model" - "github.com/sourcenetwork/immutable" sse "github.com/vito/go-sse/sse" + "github.com/sourcenetwork/immutable" + "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/datastore" "github.com/sourcenetwork/defradb/events" @@ -341,7 +342,11 @@ func (c *Client) GetAllIndexes(ctx context.Context) (map[client.CollectionName][ return indexes, nil } -func (c *Client) ExecRequest(ctx context.Context, query string) *client.RequestResult { +func (c *Client) ExecRequest( + ctx context.Context, + identity immutable.Option[string], + query string, +) *client.RequestResult { methodURL := c.http.baseURL.JoinPath("graphql") result := &client.RequestResult{} @@ -356,6 +361,7 @@ func (c *Client) ExecRequest(ctx context.Context, query string) *client.RequestR return result } c.http.setDefaultHeaders(req) + addIdentityToAuthHeaderIfExists(req, identity) res, err := c.http.client.Do(req) if err != nil { diff --git a/http/client_acp.go b/http/client_acp.go new file mode 100644 index 0000000000..bdd9e6ed3b --- /dev/null +++ b/http/client_acp.go @@ -0,0 +1,63 @@ +// 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 http + +import ( + "bytes" + "context" + "encoding/json" + "net/http" + + "github.com/sourcenetwork/defradb/client" +) + +// AddPolicyResult wraps the result of successfully adding/registering a Policy. +type AddPolicyRequest struct { + // Policy body in JSON or YAML format. + Policy string `json:"policy"` +} + +func (c *Client) AddPolicy( + ctx context.Context, + creator string, + policy string, +) (client.AddPolicyResult, error) { + methodURL := c.http.baseURL.JoinPath("acp", "policy") + + addPolicyRequest := AddPolicyRequest{ + Policy: policy, + } + + addPolicyBody, err := json.Marshal(addPolicyRequest) + if err != nil { + return client.AddPolicyResult{}, err + } + + req, err := http.NewRequestWithContext( + ctx, + http.MethodPost, + methodURL.String(), + bytes.NewBuffer(addPolicyBody), + ) + + addIdentityToAuthHeader(req, creator) + + if err != nil { + return client.AddPolicyResult{}, err + } + + var policyResult client.AddPolicyResult + if err := c.http.requestJson(req, &policyResult); err != nil { + return client.AddPolicyResult{}, err + } + + return policyResult, nil +} diff --git a/http/client_collection.go b/http/client_collection.go index 876c175338..c53bc7e7ff 100644 --- a/http/client_collection.go +++ b/http/client_collection.go @@ -60,7 +60,11 @@ func (c *Collection) Definition() client.CollectionDefinition { return c.def } -func (c *Collection) Create(ctx context.Context, doc *client.Document) error { +func (c *Collection) Create( + ctx context.Context, + identity immutable.Option[string], + doc *client.Document, +) error { if !c.Description().Name.HasValue() { return client.ErrOperationNotPermittedOnNamelessCols } @@ -71,10 +75,14 @@ func (c *Collection) Create(ctx context.Context, doc *client.Document) error { if err != nil { return err } + req, err := http.NewRequestWithContext(ctx, http.MethodPost, methodURL.String(), strings.NewReader(body)) if err != nil { return err } + + addIdentityToAuthHeaderIfExists(req, identity) + _, err = c.http.request(req) if err != nil { return err @@ -83,7 +91,11 @@ func (c *Collection) Create(ctx context.Context, doc *client.Document) error { return nil } -func (c *Collection) CreateMany(ctx context.Context, docs []*client.Document) error { +func (c *Collection) CreateMany( + ctx context.Context, + identity immutable.Option[string], + docs []*client.Document, +) error { if !c.Description().Name.HasValue() { return client.ErrOperationNotPermittedOnNamelessCols } @@ -97,25 +109,35 @@ func (c *Collection) CreateMany(ctx context.Context, docs []*client.Document) er } docMapList = append(docMapList, docMap) } + body, err := json.Marshal(docMapList) if err != nil { return err } + req, err := http.NewRequestWithContext(ctx, http.MethodPost, methodURL.String(), bytes.NewBuffer(body)) if err != nil { return err } + + addIdentityToAuthHeaderIfExists(req, identity) + _, err = c.http.request(req) if err != nil { return err } + for _, doc := range docs { doc.Clean() } return nil } -func (c *Collection) Update(ctx context.Context, doc *client.Document) error { +func (c *Collection) Update( + ctx context.Context, + identity immutable.Option[string], + doc *client.Document, +) error { if !c.Description().Name.HasValue() { return client.ErrOperationNotPermittedOnNamelessCols } @@ -130,6 +152,9 @@ func (c *Collection) Update(ctx context.Context, doc *client.Document) error { if err != nil { return err } + + addIdentityToAuthHeaderIfExists(req, identity) + _, err = c.http.request(req) if err != nil { return err @@ -138,18 +163,26 @@ func (c *Collection) Update(ctx context.Context, doc *client.Document) error { return nil } -func (c *Collection) Save(ctx context.Context, doc *client.Document) error { - _, err := c.Get(ctx, doc.ID(), true) +func (c *Collection) Save( + ctx context.Context, + identity immutable.Option[string], + doc *client.Document, +) error { + _, err := c.Get(ctx, identity, doc.ID(), true) if err == nil { - return c.Update(ctx, doc) + return c.Update(ctx, identity, doc) } - if errors.Is(err, client.ErrDocumentNotFound) { - return c.Create(ctx, doc) + if errors.Is(err, client.ErrDocumentNotFoundOrNotAuthorized) { + return c.Create(ctx, identity, doc) } return err } -func (c *Collection) Delete(ctx context.Context, docID client.DocID) (bool, error) { +func (c *Collection) Delete( + ctx context.Context, + identity immutable.Option[string], + docID client.DocID, +) (bool, error) { if !c.Description().Name.HasValue() { return false, client.ErrOperationNotPermittedOnNamelessCols } @@ -160,6 +193,9 @@ func (c *Collection) Delete(ctx context.Context, docID client.DocID) (bool, erro if err != nil { return false, err } + + addIdentityToAuthHeaderIfExists(req, identity) + _, err = c.http.request(req) if err != nil { return false, err @@ -167,22 +203,31 @@ func (c *Collection) Delete(ctx context.Context, docID client.DocID) (bool, erro return true, nil } -func (c *Collection) Exists(ctx context.Context, docID client.DocID) (bool, error) { - _, err := c.Get(ctx, docID, false) +func (c *Collection) Exists( + ctx context.Context, + identity immutable.Option[string], + docID client.DocID, +) (bool, error) { + _, err := c.Get(ctx, identity, docID, false) if err != nil { return false, err } return true, nil } -func (c *Collection) UpdateWith(ctx context.Context, target any, updater string) (*client.UpdateResult, error) { +func (c *Collection) UpdateWith( + ctx context.Context, + identity immutable.Option[string], + target any, + updater string, +) (*client.UpdateResult, error) { switch t := target.(type) { case string, map[string]any, *request.Filter: - return c.UpdateWithFilter(ctx, t, updater) + return c.UpdateWithFilter(ctx, identity, t, updater) case client.DocID: - return c.UpdateWithDocID(ctx, t, updater) + return c.UpdateWithDocID(ctx, identity, t, updater) case []client.DocID: - return c.UpdateWithDocIDs(ctx, t, updater) + return c.UpdateWithDocIDs(ctx, identity, t, updater) default: return nil, client.ErrInvalidUpdateTarget } @@ -190,6 +235,7 @@ func (c *Collection) UpdateWith(ctx context.Context, target any, updater string) func (c *Collection) updateWith( ctx context.Context, + identity immutable.Option[string], request CollectionUpdateRequest, ) (*client.UpdateResult, error) { if !c.Description().Name.HasValue() { @@ -206,6 +252,9 @@ func (c *Collection) updateWith( if err != nil { return nil, err } + + addIdentityToAuthHeaderIfExists(req, identity) + var result client.UpdateResult if err := c.http.requestJson(req, &result); err != nil { return nil, err @@ -215,28 +264,39 @@ func (c *Collection) updateWith( func (c *Collection) UpdateWithFilter( ctx context.Context, + identity immutable.Option[string], filter any, updater string, ) (*client.UpdateResult, error) { - return c.updateWith(ctx, CollectionUpdateRequest{ - Filter: filter, - Updater: updater, - }) + return c.updateWith( + ctx, + identity, + CollectionUpdateRequest{ + Filter: filter, + Updater: updater, + }, + ) } func (c *Collection) UpdateWithDocID( ctx context.Context, + identity immutable.Option[string], docID client.DocID, updater string, ) (*client.UpdateResult, error) { - return c.updateWith(ctx, CollectionUpdateRequest{ - DocID: docID.String(), - Updater: updater, - }) + return c.updateWith( + ctx, + identity, + CollectionUpdateRequest{ + DocID: docID.String(), + Updater: updater, + }, + ) } func (c *Collection) UpdateWithDocIDs( ctx context.Context, + identity immutable.Option[string], docIDs []client.DocID, updater string, ) (*client.UpdateResult, error) { @@ -244,20 +304,28 @@ func (c *Collection) UpdateWithDocIDs( for _, docID := range docIDs { strDocIDs = append(strDocIDs, docID.String()) } - return c.updateWith(ctx, CollectionUpdateRequest{ - DocIDs: strDocIDs, - Updater: updater, - }) + return c.updateWith( + ctx, + identity, + CollectionUpdateRequest{ + DocIDs: strDocIDs, + Updater: updater, + }, + ) } -func (c *Collection) DeleteWith(ctx context.Context, target any) (*client.DeleteResult, error) { +func (c *Collection) DeleteWith( + ctx context.Context, + identity immutable.Option[string], + target any, +) (*client.DeleteResult, error) { switch t := target.(type) { case string, map[string]any, *request.Filter: - return c.DeleteWithFilter(ctx, t) + return c.DeleteWithFilter(ctx, identity, t) case client.DocID: - return c.DeleteWithDocID(ctx, t) + return c.DeleteWithDocID(ctx, identity, t) case []client.DocID: - return c.DeleteWithDocIDs(ctx, t) + return c.DeleteWithDocIDs(ctx, identity, t) default: return nil, client.ErrInvalidDeleteTarget } @@ -265,6 +333,7 @@ func (c *Collection) DeleteWith(ctx context.Context, target any) (*client.Delete func (c *Collection) deleteWith( ctx context.Context, + identity immutable.Option[string], request CollectionDeleteRequest, ) (*client.DeleteResult, error) { if !c.Description().Name.HasValue() { @@ -277,10 +346,14 @@ func (c *Collection) deleteWith( if err != nil { return nil, err } + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, methodURL.String(), bytes.NewBuffer(body)) if err != nil { return nil, err } + + addIdentityToAuthHeaderIfExists(req, identity) + var result client.DeleteResult if err := c.http.requestJson(req, &result); err != nil { return nil, err @@ -288,29 +361,58 @@ func (c *Collection) deleteWith( return &result, nil } -func (c *Collection) DeleteWithFilter(ctx context.Context, filter any) (*client.DeleteResult, error) { - return c.deleteWith(ctx, CollectionDeleteRequest{ - Filter: filter, - }) +func (c *Collection) DeleteWithFilter( + ctx context.Context, + identity immutable.Option[string], + filter any, +) (*client.DeleteResult, error) { + return c.deleteWith( + ctx, + identity, + CollectionDeleteRequest{ + Filter: filter, + }, + ) } -func (c *Collection) DeleteWithDocID(ctx context.Context, docID client.DocID) (*client.DeleteResult, error) { - return c.deleteWith(ctx, CollectionDeleteRequest{ - DocID: docID.String(), - }) +func (c *Collection) DeleteWithDocID( + ctx context.Context, + identity immutable.Option[string], + docID client.DocID, +) (*client.DeleteResult, error) { + return c.deleteWith( + ctx, + identity, + CollectionDeleteRequest{ + DocID: docID.String(), + }, + ) } -func (c *Collection) DeleteWithDocIDs(ctx context.Context, docIDs []client.DocID) (*client.DeleteResult, error) { +func (c *Collection) DeleteWithDocIDs( + ctx context.Context, + identity immutable.Option[string], + docIDs []client.DocID, +) (*client.DeleteResult, error) { var strDocIDs []string for _, docID := range docIDs { strDocIDs = append(strDocIDs, docID.String()) } - return c.deleteWith(ctx, CollectionDeleteRequest{ - DocIDs: strDocIDs, - }) + return c.deleteWith( + ctx, + identity, + CollectionDeleteRequest{ + DocIDs: strDocIDs, + }, + ) } -func (c *Collection) Get(ctx context.Context, docID client.DocID, showDeleted bool) (*client.Document, error) { +func (c *Collection) Get( + ctx context.Context, + identity immutable.Option[string], + docID client.DocID, + showDeleted bool, +) (*client.Document, error) { if !c.Description().Name.HasValue() { return nil, client.ErrOperationNotPermittedOnNamelessCols } @@ -327,6 +429,9 @@ func (c *Collection) Get(ctx context.Context, docID client.DocID, showDeleted bo if err != nil { return nil, err } + + addIdentityToAuthHeaderIfExists(req, identity) + data, err := c.http.request(req) if err != nil { return nil, err @@ -347,7 +452,10 @@ func (c *Collection) WithTxn(tx datastore.Txn) client.Collection { } } -func (c *Collection) GetAllDocIDs(ctx context.Context) (<-chan client.DocIDResult, error) { +func (c *Collection) GetAllDocIDs( + ctx context.Context, + identity immutable.Option[string], +) (<-chan client.DocIDResult, error) { if !c.Description().Name.HasValue() { return nil, client.ErrOperationNotPermittedOnNamelessCols } @@ -358,8 +466,11 @@ func (c *Collection) GetAllDocIDs(ctx context.Context) (<-chan client.DocIDResul if err != nil { return nil, err } + c.http.setDefaultHeaders(req) + addIdentityToAuthHeaderIfExists(req, identity) + res, err := c.http.client.Do(req) if err != nil { return nil, err diff --git a/http/errors.go b/http/errors.go index 1510c2e520..ef25d06421 100644 --- a/http/errors.go +++ b/http/errors.go @@ -19,6 +19,7 @@ import ( const ( errFailedToLoadKeys string = "failed to load given keys" errMethodIsNotImplemented string = "the method is not implemented" + errFailedToGetContext string = "failed to get context" ) // Errors returnable from this package. @@ -54,6 +55,13 @@ func (e *errorResponse) UnmarshalJSON(data []byte) error { return nil } +func NewErrFailedToGetContext(contextType string) error { + return errors.New( + errFailedToGetContext, + errors.NewKV("ContextType", contextType), + ) +} + func NewErrFailedToLoadKeys(inner error, publicKeyPath, privateKeyPath string) error { return errors.Wrap( errFailedToLoadKeys, diff --git a/http/handler.go b/http/handler.go index b06ef06cb6..7cd278593b 100644 --- a/http/handler.go +++ b/http/handler.go @@ -31,6 +31,7 @@ var playgroundHandler http.Handler = http.HandlerFunc(http.NotFound) func NewApiRouter() (*Router, error) { tx_handler := &txHandler{} store_handler := &storeHandler{} + acp_handler := &acpHandler{} collection_handler := &collectionHandler{} p2p_handler := &p2pHandler{} lens_handler := &lensHandler{} @@ -43,6 +44,7 @@ func NewApiRouter() (*Router, error) { tx_handler.bindRoutes(router) store_handler.bindRoutes(router) + acp_handler.bindRoutes(router) p2p_handler.bindRoutes(router) ccip_handler.bindRoutes(router) diff --git a/http/handler_acp.go b/http/handler_acp.go new file mode 100644 index 0000000000..b754223c91 --- /dev/null +++ b/http/handler_acp.go @@ -0,0 +1,83 @@ +// 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 http + +import ( + "net/http" + + "github.com/getkin/kin-openapi/openapi3" + + "github.com/sourcenetwork/defradb/acp" + "github.com/sourcenetwork/defradb/client" +) + +type acpHandler struct{} + +func (s *acpHandler) AddPolicy(rw http.ResponseWriter, req *http.Request) { + db, ok := req.Context().Value(dbContextKey).(client.DB) + if !ok { + responseJSON(rw, http.StatusBadRequest, errorResponse{NewErrFailedToGetContext("db")}) + return + } + + var addPolicyRequest AddPolicyRequest + if err := requestJSON(req, &addPolicyRequest); err != nil { + responseJSON(rw, http.StatusBadRequest, errorResponse{err}) + return + } + + identity := getIdentityFromAuthHeader(req) + if !identity.HasValue() { + responseJSON(rw, http.StatusBadRequest, errorResponse{acp.ErrPolicyCreatorMustNotBeEmpty}) + return + } + + addPolicyResult, err := db.AddPolicy( + req.Context(), + identity.Value(), + addPolicyRequest.Policy, + ) + if err != nil { + responseJSON(rw, http.StatusBadRequest, errorResponse{err}) + return + } + + responseJSON(rw, http.StatusOK, addPolicyResult) +} + +func (h *acpHandler) bindRoutes(router *Router) { + successResponse := &openapi3.ResponseRef{ + Ref: "#/components/responses/success", + } + errorResponse := &openapi3.ResponseRef{ + Ref: "#/components/responses/error", + } + acpAddPolicySchema := &openapi3.SchemaRef{ + Ref: "#/components/schemas/add_policy_request", + } + + acpAddPolicyRequest := openapi3.NewRequestBody(). + WithRequired(true). + WithJSONSchemaRef(acpAddPolicySchema) + + acpAddPolicy := openapi3.NewOperation() + acpAddPolicy.OperationID = "add policy" + acpAddPolicy.Description = "Add a policy using acp system" + acpAddPolicy.Tags = []string{"acp_policy"} + acpAddPolicy.Responses = openapi3.NewResponses() + acpAddPolicy.Responses.Set("200", successResponse) + acpAddPolicy.Responses.Set("400", errorResponse) + acpAddPolicy.RequestBody = &openapi3.RequestBodyRef{ + Value: acpAddPolicyRequest, + } + + router.AddRoute("/acp/policy", http.MethodPost, acpAddPolicy, h.AddPolicy) +} diff --git a/http/handler_ccip.go b/http/handler_ccip.go index c0eb6a5918..36151c5cc3 100644 --- a/http/handler_ccip.go +++ b/http/handler_ccip.go @@ -60,7 +60,8 @@ func (c *ccipHandler) ExecCCIP(rw http.ResponseWriter, req *http.Request) { return } - result := store.ExecRequest(req.Context(), request.Query) + identity := getIdentityFromAuthHeader(req) + result := store.ExecRequest(req.Context(), identity, request.Query) if result.Pub != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{ErrStreamingNotSupported}) return diff --git a/http/handler_ccip_test.go b/http/handler_ccip_test.go index 2a2cc4f077..37e0da951f 100644 --- a/http/handler_ccip_test.go +++ b/http/handler_ccip_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/datastore/memory" "github.com/sourcenetwork/defradb/db" @@ -206,7 +207,7 @@ func setupDatabase(t *testing.T) client.DB { doc, err := client.NewDocFromJSON([]byte(`{"name": "bob"}`), col.Schema()) require.NoError(t, err) - err = col.Create(ctx, doc) + err = col.Create(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) return cdb diff --git a/http/handler_collection.go b/http/handler_collection.go index d713afdf40..1f41442849 100644 --- a/http/handler_collection.go +++ b/http/handler_collection.go @@ -47,6 +47,8 @@ func (s *collectionHandler) Create(rw http.ResponseWriter, req *http.Request) { return } + identity := getIdentityFromAuthHeader(req) + switch { case client.IsJSONArray(data): docList, err := client.NewDocsFromJSON(data, col.Schema()) @@ -55,7 +57,7 @@ func (s *collectionHandler) Create(rw http.ResponseWriter, req *http.Request) { return } - if err := col.CreateMany(req.Context(), docList); err != nil { + if err := col.CreateMany(req.Context(), identity, docList); err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return } @@ -66,7 +68,7 @@ func (s *collectionHandler) Create(rw http.ResponseWriter, req *http.Request) { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return } - if err := col.Create(req.Context(), doc); err != nil { + if err := col.Create(req.Context(), identity, doc); err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return } @@ -83,9 +85,11 @@ func (s *collectionHandler) DeleteWith(rw http.ResponseWriter, req *http.Request return } + identity := getIdentityFromAuthHeader(req) + switch { case request.Filter != nil: - result, err := col.DeleteWith(req.Context(), request.Filter) + result, err := col.DeleteWith(req.Context(), identity, request.Filter) if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return @@ -97,7 +101,7 @@ func (s *collectionHandler) DeleteWith(rw http.ResponseWriter, req *http.Request responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return } - result, err := col.DeleteWith(req.Context(), docID) + result, err := col.DeleteWith(req.Context(), identity, docID) if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return @@ -113,7 +117,7 @@ func (s *collectionHandler) DeleteWith(rw http.ResponseWriter, req *http.Request } docIDs = append(docIDs, docID) } - result, err := col.DeleteWith(req.Context(), docIDs) + result, err := col.DeleteWith(req.Context(), identity, docIDs) if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return @@ -133,9 +137,11 @@ func (s *collectionHandler) UpdateWith(rw http.ResponseWriter, req *http.Request return } + identity := getIdentityFromAuthHeader(req) + switch { case request.Filter != nil: - result, err := col.UpdateWith(req.Context(), request.Filter, request.Updater) + result, err := col.UpdateWith(req.Context(), identity, request.Filter, request.Updater) if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return @@ -147,7 +153,7 @@ func (s *collectionHandler) UpdateWith(rw http.ResponseWriter, req *http.Request responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return } - result, err := col.UpdateWith(req.Context(), docID, request.Updater) + result, err := col.UpdateWith(req.Context(), identity, docID, request.Updater) if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return @@ -163,7 +169,7 @@ func (s *collectionHandler) UpdateWith(rw http.ResponseWriter, req *http.Request } docIDs = append(docIDs, docID) } - result, err := col.UpdateWith(req.Context(), docIDs, request.Updater) + result, err := col.UpdateWith(req.Context(), identity, docIDs, request.Updater) if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return @@ -182,11 +188,20 @@ func (s *collectionHandler) Update(rw http.ResponseWriter, req *http.Request) { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return } - doc, err := col.Get(req.Context(), docID, true) + + identity := getIdentityFromAuthHeader(req) + + doc, err := col.Get(req.Context(), identity, docID, true) if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return } + + if doc == nil { + responseJSON(rw, http.StatusBadRequest, errorResponse{client.ErrDocumentNotFoundOrNotAuthorized}) + return + } + patch, err := io.ReadAll(req.Body) if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) @@ -196,7 +211,7 @@ func (s *collectionHandler) Update(rw http.ResponseWriter, req *http.Request) { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return } - err = col.Update(req.Context(), doc) + err = col.Update(req.Context(), identity, doc) if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return @@ -212,7 +227,10 @@ func (s *collectionHandler) Delete(rw http.ResponseWriter, req *http.Request) { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return } - _, err = col.Delete(req.Context(), docID) + + identity := getIdentityFromAuthHeader(req) + + _, err = col.Delete(req.Context(), identity, docID) if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return @@ -229,11 +247,20 @@ func (s *collectionHandler) Get(rw http.ResponseWriter, req *http.Request) { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return } - doc, err := col.Get(req.Context(), docID, showDeleted) + + identity := getIdentityFromAuthHeader(req) + + doc, err := col.Get(req.Context(), identity, docID, showDeleted) if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return } + + if doc == nil { + responseJSON(rw, http.StatusBadRequest, errorResponse{client.ErrDocumentNotFoundOrNotAuthorized}) + return + } + docMap, err := doc.ToMap() if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) @@ -256,7 +283,9 @@ func (s *collectionHandler) GetAllDocIDs(rw http.ResponseWriter, req *http.Reque return } - docIDsResult, err := col.GetAllDocIDs(req.Context()) + identity := getIdentityFromAuthHeader(req) + + docIDsResult, err := col.GetAllDocIDs(req.Context(), identity) if err != nil { responseJSON(rw, http.StatusBadRequest, errorResponse{err}) return diff --git a/http/handler_store.go b/http/handler_store.go index 6077e6ea60..4c57eda34f 100644 --- a/http/handler_store.go +++ b/http/handler_store.go @@ -311,7 +311,9 @@ func (s *storeHandler) ExecRequest(rw http.ResponseWriter, req *http.Request) { responseJSON(rw, http.StatusBadRequest, errorResponse{ErrMissingRequest}) return } - result := store.ExecRequest(req.Context(), request.Query) + + identity := getIdentityFromAuthHeader(req) + result := store.ExecRequest(req.Context(), identity, request.Query) if result.Pub == nil { responseJSON(rw, http.StatusOK, GraphQLResponse{result.GQL.Data, result.GQL.Errors}) @@ -621,7 +623,6 @@ func (h *storeHandler) bindRoutes(router *Router) { router.AddRoute("/collections", http.MethodGet, collectionDescribe, h.GetCollection) router.AddRoute("/collections", http.MethodPatch, patchCollection, h.PatchCollection) router.AddRoute("/view", http.MethodPost, views, h.AddView) - router.AddRoute("/view", http.MethodPost, views, h.AddView) router.AddRoute("/graphql", http.MethodGet, graphQLGet, h.ExecRequest) router.AddRoute("/graphql", http.MethodPost, graphQLPost, h.ExecRequest) router.AddRoute("/debug/dump", http.MethodGet, debugDump, h.PrintDump) diff --git a/http/openapi.go b/http/openapi.go index 12a832c704..9e1f58c854 100644 --- a/http/openapi.go +++ b/http/openapi.go @@ -40,6 +40,7 @@ var openApiSchemas = map[string]any{ "ccip_response": &CCIPResponse{}, "patch_schema_request": &patchSchemaRequest{}, "add_view_request": &addViewRequest{}, + "add_policy_request": &AddPolicyRequest{}, "migrate_request": &migrateRequest{}, "set_migration_request": &setMigrationRequest{}, } @@ -134,6 +135,10 @@ func NewOpenAPISpec() (*openapi3.T, error) { Name: "p2p", Description: "Peer-to-peer network operations", }, + &openapi3.Tag{ + Name: "acp", + Description: "Access control policy operations", + }, &openapi3.Tag{ Name: "transaction", Description: "Database transaction operations", diff --git a/http/utils.go b/http/utils.go index c7b1507c4e..97d9a9181c 100644 --- a/http/utils.go +++ b/http/utils.go @@ -15,11 +15,22 @@ import ( "fmt" "io" "net/http" + "strings" + "github.com/sourcenetwork/immutable" + + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/datastore/badger/v4" ) +// Using Basic right now, but this will soon change to 'Bearer' as acp authentication +// gets implemented: https://github.com/sourcenetwork/defradb/issues/2017 +const authSchemaPrefix = "Basic " + +// Name of authorization header +const authHeaderName = "Authorization" + func requestJSON(req *http.Request, out any) error { data, err := io.ReadAll(req.Body) if err != nil { @@ -36,11 +47,46 @@ func responseJSON(rw http.ResponseWriter, status int, out any) { func parseError(msg any) error { switch msg { - case client.ErrDocumentNotFound.Error(): - return client.ErrDocumentNotFound + case client.ErrDocumentNotFoundOrNotAuthorized.Error(): + return client.ErrDocumentNotFoundOrNotAuthorized case badger.ErrTxnConflict.Error(): return badger.ErrTxnConflict default: return fmt.Errorf("%s", msg) } } + +// addIdentityToAuthHeader adds the identity to auth header as it must always exist. +func addIdentityToAuthHeader(req *http.Request, identity string) { + // Create a bearer that will get added to authorization header. + bearerWithIdentity := authSchemaPrefix + identity + + // Add the authorization header with the bearer containing identity. + req.Header.Add(authHeaderName, bearerWithIdentity) +} + +// addIdentityToAuthHeaderIfExists adds the identity to auth header if it exsits, otherwise does nothing. +func addIdentityToAuthHeaderIfExists(req *http.Request, identity immutable.Option[string]) { + // Do nothing if there is no identity to add. + if !identity.HasValue() { + return + } + addIdentityToAuthHeader(req, identity.Value()) +} + +// getIdentityFromAuthHeader tries to get the identity from the auth header, if it is found +// with the expecte auth schema then it is returned, otherwise no identity is returned. +func getIdentityFromAuthHeader(req *http.Request) immutable.Option[string] { + authHeader := req.Header.Get(authHeaderName) + if authHeader == "" { + return acpIdentity.NoIdentity + } + + identity := strings.TrimPrefix(authHeader, authSchemaPrefix) + // If expected schema prefix was not found, or empty, then assume no identity. + if identity == authHeader || identity == "" { + return acpIdentity.NoIdentity + } + + return acpIdentity.NewIdentity(identity) +} diff --git a/lens/fetcher.go b/lens/fetcher.go index 90c80c78fb..f4895a0fd7 100644 --- a/lens/fetcher.go +++ b/lens/fetcher.go @@ -16,6 +16,9 @@ import ( "github.com/fxamacker/cbor/v2" + "github.com/sourcenetwork/immutable" + + "github.com/sourcenetwork/defradb/acp" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/client/request" "github.com/sourcenetwork/defradb/core" @@ -35,6 +38,7 @@ type lensedFetcher struct { txn datastore.Txn col client.Collection + // Cache the fieldDescriptions mapped by name to allow for cheaper access within the fetcher loop fieldDescriptionsByName map[string]client.FieldDefinition @@ -57,7 +61,9 @@ func NewFetcher(source fetcher.Fetcher, registry client.LensRegistry) fetcher.Fe func (f *lensedFetcher) Init( ctx context.Context, + identity immutable.Option[string], txn datastore.Txn, + acp immutable.Option[acp.ACP], col client.Collection, fields []client.FieldDefinition, filter *mapper.Filter, @@ -105,7 +111,18 @@ historyLoop: } else { innerFetcherFields = fields } - return f.source.Init(ctx, txn, col, innerFetcherFields, filter, docmapper, reverse, showDeleted) + return f.source.Init( + ctx, + identity, + txn, + acp, + col, + innerFetcherFields, + filter, + docmapper, + reverse, + showDeleted, + ) } func (f *lensedFetcher) Start(ctx context.Context, spans core.Spans) error { diff --git a/net/client_test.go b/net/client_test.go index 89c26e06b5..3be892e3f2 100644 --- a/net/client_test.go +++ b/net/client_test.go @@ -18,6 +18,7 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/grpc" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/events" ) @@ -113,12 +114,12 @@ func TestPushlogW_WithValidPeerID_NoError(t *testing.T) { doc, err := client.NewDocFromJSON([]byte(`{"name": "test"}`), col.Schema()) require.NoError(t, err) - err = col.Save(ctx, doc) + err = col.Save(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) col, err = n2.db.GetCollectionByName(ctx, "User") require.NoError(t, err) - err = col.Save(ctx, doc) + err = col.Save(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) cid, err := createCID(doc) diff --git a/net/dag_test.go b/net/dag_test.go index ddd9e9aab3..5229e68e93 100644 --- a/net/dag_test.go +++ b/net/dag_test.go @@ -22,6 +22,7 @@ import ( mh "github.com/multiformats/go-multihash" "github.com/stretchr/testify/require" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/core" "github.com/sourcenetwork/defradb/merkle/clock" @@ -172,7 +173,7 @@ func TestSendJobWorker_WithPeer_NoError(t *testing.T) { require.NoError(t, err) dsKey := core.DataStoreKeyFromDocID(doc.ID()) - err = col.Create(ctx, doc) + err = col.Create(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) txn1, _ := db1.NewTxn(ctx, false) diff --git a/net/errors.go b/net/errors.go index 1ca2d857d5..773eb8765d 100644 --- a/net/errors.go +++ b/net/errors.go @@ -29,12 +29,15 @@ const ( ) var ( - ErrPeerConnectionWaitTimout = errors.New("waiting for peer connection timed out") - ErrPubSubWaitTimeout = errors.New("waiting for pubsub timed out") - ErrPushLogWaitTimeout = errors.New("waiting for pushlog timed out") - ErrNilDB = errors.New("database object can't be nil") - ErrNilUpdateChannel = errors.New("tried to subscribe to update channel, but update channel is nil") - ErrSelfTargetForReplicator = errors.New("can't target ourselves as a replicator") + ErrP2PColHasPolicy = errors.New("p2p collection specified has a policy on it") + ErrReplicatorColHasPolicy = errors.New("replicator collection specified has a policy on it") + ErrReplicatorSomeColsHavePolicy = errors.New("replicator can not use all collections, as some have policy") + ErrPeerConnectionWaitTimout = errors.New("waiting for peer connection timed out") + ErrPubSubWaitTimeout = errors.New("waiting for pubsub timed out") + ErrPushLogWaitTimeout = errors.New("waiting for pushlog timed out") + ErrNilDB = errors.New("database object can't be nil") + ErrNilUpdateChannel = errors.New("tried to subscribe to update channel, but update channel is nil") + ErrSelfTargetForReplicator = errors.New("can't target ourselves as a replicator") ) func NewErrPushLog(inner error, kv ...errors.KV) error { diff --git a/net/node_test.go b/net/node_test.go index 3b7f28d017..bf0bc653c5 100644 --- a/net/node_test.go +++ b/net/node_test.go @@ -31,13 +31,11 @@ import ( func FixtureNewMemoryDBWithBroadcaster(t *testing.T) client.DB { var database client.DB - var options []db.Option ctx := context.Background() - options = append(options, db.WithUpdateEvents()) opts := badgerds.Options{Options: badger.DefaultOptions("").WithInMemory(true)} rootstore, err := badgerds.NewDatastore("", &opts) require.NoError(t, err) - database, err = db.NewDB(ctx, rootstore, options...) + database, err = db.NewDB(ctx, rootstore, db.WithUpdateEvents()) require.NoError(t, err) return database } diff --git a/net/peer_collection.go b/net/peer_collection.go index 6f4f4d8ba8..4ef1139a1c 100644 --- a/net/peer_collection.go +++ b/net/peer_collection.go @@ -16,6 +16,7 @@ import ( dsq "github.com/ipfs/go-datastore/query" "github.com/sourcenetwork/immutable" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/core" ) @@ -47,6 +48,14 @@ func (p *Peer) AddP2PCollections(ctx context.Context, collectionIDs []string) er storeCollections = append(storeCollections, storeCol...) } + // Ensure none of the collections have a policy on them, until following is implemented: + // TODO-ACP: ACP <> P2P https://github.com/sourcenetwork/defradb/issues/2366 + for _, col := range storeCollections { + if col.Description().Policy.HasValue() { + return ErrP2PColHasPolicy + } + } + // Ensure we can add all the collections to the store on the transaction // before adding to topics. for _, col := range storeCollections { @@ -71,7 +80,8 @@ func (p *Peer) AddP2PCollections(ctx context.Context, collectionIDs []string) er // from the pubsub topics to avoid receiving duplicate events. removedTopics := []string{} for _, col := range storeCollections { - keyChan, err := col.GetAllDocIDs(p.ctx) + // TODO-ACP: Support ACP <> P2P - https://github.com/sourcenetwork/defradb/issues/2366 + keyChan, err := col.GetAllDocIDs(p.ctx, acpIdentity.NoIdentity) if err != nil { return err } @@ -141,7 +151,8 @@ func (p *Peer) RemoveP2PCollections(ctx context.Context, collectionIDs []string) // to the pubsub topics. addedTopics := []string{} for _, col := range storeCollections { - keyChan, err := col.GetAllDocIDs(p.ctx) + // TODO-ACP: Support ACP <> P2P - https://github.com/sourcenetwork/defradb/issues/2366 + keyChan, err := col.GetAllDocIDs(p.ctx, acpIdentity.NoIdentity) if err != nil { return err } diff --git a/net/peer_replicator.go b/net/peer_replicator.go index 3638122a2a..93f6070f0b 100644 --- a/net/peer_replicator.go +++ b/net/peer_replicator.go @@ -18,6 +18,7 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peerstore" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/core" ) @@ -48,15 +49,30 @@ func (p *Peer) SetReplicator(ctx context.Context, rep client.Replicator) error { if err != nil { return NewErrReplicatorCollections(err) } + + if col.Description().Policy.HasValue() { + return ErrReplicatorColHasPolicy + } + collections = append(collections, col) } default: - // default to all collections - collections, err = p.db.WithTxn(txn).GetCollections(ctx, client.CollectionFetchOptions{}) + // default to all collections (unless a collection contains a policy). + // TODO-ACP: default to all collections after resolving https://github.com/sourcenetwork/defradb/issues/2366 + allCollections, err := p.db.WithTxn(txn).GetCollections(ctx, client.CollectionFetchOptions{}) if err != nil { return NewErrReplicatorCollections(err) } + + for _, col := range allCollections { + // Can not default to all collections if any collection has a policy. + // TODO-ACP: remove this check/loop after https://github.com/sourcenetwork/defradb/issues/2366 + if col.Description().Policy.HasValue() { + return ErrReplicatorSomeColsHavePolicy + } + } + collections = allCollections } rep.Schemas = nil @@ -92,7 +108,8 @@ func (p *Peer) SetReplicator(ctx context.Context, rep client.Replicator) error { // push all collection documents to the replicator peer for _, col := range added { - keysCh, err := col.WithTxn(txn).GetAllDocIDs(ctx) + // TODO-ACP: Support ACP <> P2P - https://github.com/sourcenetwork/defradb/issues/2366 + keysCh, err := col.WithTxn(txn).GetAllDocIDs(ctx, acpIdentity.NoIdentity) if err != nil { return NewErrReplicatorDocID(err, col.Name().Value(), rep.Info.ID) } diff --git a/net/peer_test.go b/net/peer_test.go index 0a863b8112..3350e026ba 100644 --- a/net/peer_test.go +++ b/net/peer_test.go @@ -12,6 +12,7 @@ package net import ( "context" + "fmt" "testing" "time" @@ -25,6 +26,7 @@ import ( rpc "github.com/sourcenetwork/go-libp2p-pubsub-rpc" "github.com/stretchr/testify/require" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/core/crdt" "github.com/sourcenetwork/defradb/datastore/memory" @@ -115,7 +117,7 @@ const randomMultiaddr = "/ip4/127.0.0.1/tcp/0" func newTestNode(ctx context.Context, t *testing.T) (client.DB, *Node) { store := memory.NewDatastore(ctx) - db, err := db.NewDB(ctx, store, db.WithUpdateEvents()) + db, err := db.NewDB(ctx, store, db.WithUpdateEvents(), db.WithACPInMemory()) require.NoError(t, err) n, err := NewNode( @@ -169,7 +171,7 @@ func TestNewPeer_WithExistingTopic_TopicAlreadyExistsError(t *testing.T) { doc, err := client.NewDocFromJSON([]byte(`{"name": "John", "age": 30}`), col.Schema()) require.NoError(t, err) - err = col.Create(ctx, doc) + err = col.Create(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) h, err := libp2p.New() @@ -389,6 +391,105 @@ func TestSetReplicator_NoError(t *testing.T) { require.NoError(t, err) } +// This test documents that we don't allow setting replicator with a collection that has a policy +// until the following is implemented: +// TODO-ACP: ACP <> P2P https://github.com/sourcenetwork/defradb/issues/2366 +func TestSetReplicatorWithACollectionSpecifiedThatHasPolicy_ReturnError(t *testing.T) { + ctx := context.Background() + db, n := newTestNode(ctx, t) + defer n.Close() + + policy := ` + description: a policy + actor: + name: actor + resources: + user: + permissions: + read: + expr: owner + write: + expr: owner + relations: + owner: + types: + - actor + ` + policyResult, err := db.AddPolicy(ctx, "cosmos1zzg43wdrhmmk89z3pmejwete2kkd4a3vn7w969", policy) + policyID := policyResult.PolicyID + require.NoError(t, err) + require.Equal(t, "fc3a0a39c73949c70a79e02b8d928028e9cbcc772ba801463a6acdcf2f256cd4", policyID) + + schema := fmt.Sprintf(` + type User @policy(id: "%s", resource: "user") { + name: String + age: Int + } + `, policyID, + ) + _, err = db.AddSchema(ctx, schema) + require.NoError(t, err) + + info, err := peer.AddrInfoFromString("/ip4/0.0.0.0/tcp/0/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N") + require.NoError(t, err) + + err = n.Peer.SetReplicator(ctx, client.Replicator{ + Info: *info, + Schemas: []string{"User"}, + }) + require.Error(t, err) + require.ErrorIs(t, err, ErrReplicatorColHasPolicy) +} + +// This test documents that we don't allow setting replicator using default option when any collection has a policy +// until the following is implemented: +// TODO-ACP: ACP <> P2P https://github.com/sourcenetwork/defradb/issues/2366 +func TestSetReplicatorWithSomeCollectionThatHasPolicyUsingAllCollectionsByDefault_ReturnError(t *testing.T) { + ctx := context.Background() + db, n := newTestNode(ctx, t) + defer n.Close() + + policy := ` + description: a policy + actor: + name: actor + resources: + user: + permissions: + read: + expr: owner + write: + expr: owner + relations: + owner: + types: + - actor + ` + policyResult, err := db.AddPolicy(ctx, "cosmos1zzg43wdrhmmk89z3pmejwete2kkd4a3vn7w969", policy) + policyID := policyResult.PolicyID + require.NoError(t, err) + require.Equal(t, "fc3a0a39c73949c70a79e02b8d928028e9cbcc772ba801463a6acdcf2f256cd4", policyID) + + schema := fmt.Sprintf(` + type User @policy(id: "%s", resource: "user") { + name: String + age: Int + } + `, policyID, + ) + _, err = db.AddSchema(ctx, schema) + require.NoError(t, err) + + info, err := peer.AddrInfoFromString("/ip4/0.0.0.0/tcp/0/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N") + require.NoError(t, err) + + err = n.Peer.SetReplicator(ctx, client.Replicator{ + Info: *info, + // Note: The missing explicit input of schemas here + }) + require.ErrorIs(t, err, ErrReplicatorSomeColsHavePolicy) +} + func TestSetReplicator_WithInvalidAddress_EmptyPeerIDError(t *testing.T) { ctx := context.Background() db, n := newTestNode(ctx, t) @@ -475,10 +576,10 @@ func TestPushToReplicator_SingleDocumentNoPeer_FailedToReplicateLogError(t *test doc, err := client.NewDocFromJSON([]byte(`{"name": "John", "age": 30}`), col.Schema()) require.NoError(t, err) - err = col.Create(ctx, doc) + err = col.Create(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) - keysCh, err := col.GetAllDocIDs(ctx) + keysCh, err := col.GetAllDocIDs(ctx, acpIdentity.NoIdentity) require.NoError(t, err) txn, err := db.NewTxn(ctx, true) @@ -698,6 +799,53 @@ func TestAddP2PCollections_WithInvalidCollectionID_NotFoundError(t *testing.T) { require.Error(t, err, ds.ErrNotFound) } +// This test documents that we don't allow adding p2p collections that have a policy +// until the following is implemented: +// TODO-ACP: ACP <> P2P https://github.com/sourcenetwork/defradb/issues/2366 +func TestAddP2PCollectionsWithPermissionedCollection_Error(t *testing.T) { + ctx := context.Background() + db, n := newTestNode(ctx, t) + defer n.Close() + + policy := ` + description: a policy + actor: + name: actor + resources: + user: + permissions: + read: + expr: owner + write: + expr: owner + relations: + owner: + types: + - actor + ` + policyResult, err := db.AddPolicy(ctx, "cosmos1zzg43wdrhmmk89z3pmejwete2kkd4a3vn7w969", policy) + policyID := policyResult.PolicyID + require.NoError(t, err) + require.Equal(t, "fc3a0a39c73949c70a79e02b8d928028e9cbcc772ba801463a6acdcf2f256cd4", policyID) + + schema := fmt.Sprintf(` + type User @policy(id: "%s", resource: "user") { + name: String + age: Int + } + `, policyID, + ) + _, err = db.AddSchema(ctx, schema) + require.NoError(t, err) + + col, err := db.GetCollectionByName(ctx, "User") + require.NoError(t, err) + + err = n.Peer.AddP2PCollections(ctx, []string{col.SchemaRoot()}) + require.Error(t, err) + require.ErrorIs(t, err, ErrP2PColHasPolicy) +} + func TestAddP2PCollections_NoError(t *testing.T) { ctx := context.Background() db, n := newTestNode(ctx, t) @@ -792,7 +940,7 @@ func TestHandleDocCreateLog_NoError(t *testing.T) { doc, err := client.NewDocFromJSON([]byte(`{"name": "John", "age": 30}`), col.Schema()) require.NoError(t, err) - err = col.Create(ctx, doc) + err = col.Create(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) docCid, err := createCID(doc) @@ -845,7 +993,7 @@ func TestHandleDocCreateLog_WithExistingTopic_TopicExistsError(t *testing.T) { doc, err := client.NewDocFromJSON([]byte(`{"name": "John", "age": 30}`), col.Schema()) require.NoError(t, err) - err = col.Create(ctx, doc) + err = col.Create(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) _, err = rpc.NewTopic(ctx, n.ps, n.host.ID(), doc.ID().String(), true) @@ -875,7 +1023,7 @@ func TestHandleDocUpdateLog_NoError(t *testing.T) { doc, err := client.NewDocFromJSON([]byte(`{"name": "John", "age": 30}`), col.Schema()) require.NoError(t, err) - err = col.Create(ctx, doc) + err = col.Create(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) docCid, err := createCID(doc) @@ -928,7 +1076,7 @@ func TestHandleDocUpdateLog_WithExistingDocIDTopic_TopicExistsError(t *testing.T doc, err := client.NewDocFromJSON([]byte(`{"name": "John", "age": 30}`), col.Schema()) require.NoError(t, err) - err = col.Create(ctx, doc) + err = col.Create(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) docCid, err := createCID(doc) @@ -972,7 +1120,7 @@ func TestHandleDocUpdateLog_WithExistingSchemaTopic_TopicExistsError(t *testing. doc, err := client.NewDocFromJSON([]byte(`{"name": "John", "age": 30}`), col.Schema()) require.NoError(t, err) - err = col.Create(ctx, doc) + err = col.Create(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) docCid, err := createCID(doc) diff --git a/net/server.go b/net/server.go index 41cfd3625b..58a9f16f75 100644 --- a/net/server.go +++ b/net/server.go @@ -29,6 +29,7 @@ import ( grpcpeer "google.golang.org/grpc/peer" "google.golang.org/protobuf/proto" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/core" "github.com/sourcenetwork/defradb/datastore/badger/v4" @@ -107,7 +108,8 @@ func newServer(p *Peer, db client.DB, opts ...grpc.DialOption) (*server, error) if _, ok := colMap[col.SchemaRoot()]; ok { continue } - docIDChan, err := col.GetAllDocIDs(p.ctx) + // TODO-ACP: Support ACP <> P2P - https://github.com/sourcenetwork/defradb/issues/2366 + docIDChan, err := col.GetAllDocIDs(p.ctx, acpIdentity.NoIdentity) if err != nil { return nil, err } @@ -353,14 +355,18 @@ func (s *server) syncIndexedDocs( return err } - oldDoc, err := preTxnCol.Get(ctx, docID, false) - isNewDoc := errors.Is(err, client.ErrDocumentNotFound) + //TODO-ACP: https://github.com/sourcenetwork/defradb/issues/2365 + // Resolve while handling acp <> secondary indexes. + oldDoc, err := preTxnCol.Get(ctx, acpIdentity.NoIdentity, docID, false) + isNewDoc := errors.Is(err, client.ErrDocumentNotFoundOrNotAuthorized) if !isNewDoc && err != nil { return err } - doc, err := col.Get(ctx, docID, false) - isDeletedDoc := errors.Is(err, client.ErrDocumentNotFound) + //TODO-ACP: https://github.com/sourcenetwork/defradb/issues/2365 + // Resolve while handling acp <> secondary indexes. + doc, err := col.Get(ctx, acpIdentity.NoIdentity, docID, false) + isDeletedDoc := errors.Is(err, client.ErrDocumentNotFoundOrNotAuthorized) if !isDeletedDoc && err != nil { return err } diff --git a/net/server_test.go b/net/server_test.go index 099f426887..5e6eda3d1d 100644 --- a/net/server_test.go +++ b/net/server_test.go @@ -18,9 +18,11 @@ import ( "github.com/libp2p/go-libp2p/core/event" "github.com/libp2p/go-libp2p/core/host" rpc "github.com/sourcenetwork/go-libp2p-pubsub-rpc" + "github.com/sourcenetwork/immutable" "github.com/stretchr/testify/require" grpcpeer "google.golang.org/grpc/peer" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/datastore/memory" "github.com/sourcenetwork/defradb/errors" @@ -98,7 +100,11 @@ type mockCollection struct { func (mCol *mockCollection) SchemaRoot() string { return "mockColID" } -func (mCol *mockCollection) GetAllDocIDs(ctx context.Context) (<-chan client.DocIDResult, error) { +func (mCol *mockCollection) GetAllDocIDs( + ctx context.Context, + identity immutable.Option[string], + +) (<-chan client.DocIDResult, error) { return nil, mockError } @@ -134,7 +140,7 @@ func TestNewServerWithAddTopicError(t *testing.T) { doc, err := client.NewDocFromJSON([]byte(`{"name": "John", "age": 30}`), col.Schema()) require.NoError(t, err) - err = col.Create(ctx, doc) + err = col.Create(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) _, err = rpc.NewTopic(ctx, n.Peer.ps, n.Peer.host.ID(), doc.ID().String(), true) @@ -180,7 +186,7 @@ func TestNewServerWithEmitterError(t *testing.T) { doc, err := client.NewDocFromJSON([]byte(`{"name": "John", "age": 30}`), col.Schema()) require.NoError(t, err) - err = col.Create(ctx, doc) + err = col.Create(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) n.Peer.host = &mockHost{n.Peer.host} diff --git a/planner/create.go b/planner/create.go index a03c429da9..3333ae999e 100644 --- a/planner/create.go +++ b/planner/create.go @@ -78,7 +78,11 @@ func (n *createNode) Next() (bool, error) { return false, nil } - if err := n.collection.WithTxn(n.p.txn).Create(n.p.ctx, n.doc); err != nil { + if err := n.collection.WithTxn(n.p.txn).Create( + n.p.ctx, + n.p.identity, + n.doc, + ); err != nil { return false, err } diff --git a/planner/delete.go b/planner/delete.go index 63cdec9a6f..74bb14d202 100644 --- a/planner/delete.go +++ b/planner/delete.go @@ -53,7 +53,11 @@ func (n *deleteNode) Next() (bool, error) { if err != nil { return false, err } - _, err = n.collection.DeleteWithDocID(n.p.ctx, docID) + _, err = n.collection.DeleteWithDocID( + n.p.ctx, + n.p.identity, + docID, + ) if err != nil { return false, err } diff --git a/planner/planner.go b/planner/planner.go index b2d9bc47a9..eca0168671 100644 --- a/planner/planner.go +++ b/planner/planner.go @@ -13,6 +13,9 @@ package planner import ( "context" + "github.com/sourcenetwork/immutable" + + "github.com/sourcenetwork/defradb/acp" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/client/request" "github.com/sourcenetwork/defradb/connor" @@ -82,17 +85,27 @@ type PlanContext struct { // Planner combines session state and database state to // produce a request plan, which is run by the execution context. type Planner struct { - txn datastore.Txn - db client.Store + txn datastore.Txn + identity immutable.Option[string] + acp immutable.Option[acp.ACP] + db client.Store ctx context.Context } -func New(ctx context.Context, db client.Store, txn datastore.Txn) *Planner { +func New( + ctx context.Context, + identity immutable.Option[string], + acp immutable.Option[acp.ACP], + db client.Store, + txn datastore.Txn, +) *Planner { return &Planner{ - txn: txn, - db: db, - ctx: ctx, + txn: txn, + identity: identity, + acp: acp, + db: db, + ctx: ctx, } } diff --git a/planner/scan.go b/planner/scan.go index 3ba0dd03cb..e52b3c2131 100644 --- a/planner/scan.go +++ b/planner/scan.go @@ -64,7 +64,9 @@ func (n *scanNode) Init() error { // init the fetcher if err := n.fetcher.Init( n.p.ctx, + n.p.identity, n.p.txn, + n.p.acp, n.col, n.fields, n.filter, diff --git a/planner/update.go b/planner/update.go index 077ceb39e4..b86c616dbb 100644 --- a/planner/update.go +++ b/planner/update.go @@ -72,7 +72,7 @@ func (n *updateNode) Next() (bool, error) { if err != nil { return false, err } - _, err = n.collection.UpdateWithDocID(n.p.ctx, docID, string(patch)) + _, err = n.collection.UpdateWithDocID(n.p.ctx, n.p.identity, docID, string(patch)) if err != nil { return false, err } diff --git a/request/graphql/schema/collection.go b/request/graphql/schema/collection.go index 3786d41209..00a4f6503c 100644 --- a/request/graphql/schema/collection.go +++ b/request/graphql/schema/collection.go @@ -108,6 +108,8 @@ func collectionFromAstDefinition( }, } + policyDescription := immutable.None[client.PolicyDescription]() + indexDescriptions := []client.IndexDescription{} for _, field := range def.Fields { tmpFieldsDescriptions, err := fieldsFromAST(field, relationManager, def.Name.Value) @@ -147,12 +149,20 @@ func collectionFromAstDefinition( } indexDescriptions = append(indexDescriptions, index) } + if directive.Name.Value == types.PolicySchemaDirectiveLabel { + policy, err := policyFromAST(directive) + if err != nil { + return client.CollectionDefinition{}, err + } + policyDescription = immutable.Some(policy) + } } return client.CollectionDefinition{ Description: client.CollectionDescription{ Name: immutable.Some(def.Name.Value), Indexes: indexDescriptions, + Policy: policyDescription, }, Schema: client.SchemaDescription{ Name: def.Name.Value, @@ -383,6 +393,31 @@ func fieldsFromAST(field *ast.FieldDefinition, return fieldDescriptions, nil } +// policyFromAST returns the policy description after parsing but the validation +// is not done yet on the values that are returned. This is because we need acp to do that. +func policyFromAST(directive *ast.Directive) (client.PolicyDescription, error) { + policyDesc := client.PolicyDescription{} + for _, arg := range directive.Arguments { + switch arg.Name.Value { + case types.PolicySchemaDirectivePropID: + policyIDProp, ok := arg.Value.(*ast.StringValue) + if !ok { + return client.PolicyDescription{}, ErrPolicyInvalidIDProp + } + policyDesc.ID = policyIDProp.Value + case types.PolicySchemaDirectivePropResource: + policyResourceProp, ok := arg.Value.(*ast.StringValue) + if !ok { + return client.PolicyDescription{}, ErrPolicyInvalidResourceProp + } + policyDesc.ResourceName = policyResourceProp.Value + default: + return client.PolicyDescription{}, ErrPolicyWithUnknownArg + } + } + return policyDesc, nil +} + func setCRDTType(field *ast.FieldDefinition, kind client.FieldKind) (client.CType, error) { if directive, exists := findDirective(field, "crdt"); exists { for _, arg := range directive.Arguments { diff --git a/request/graphql/schema/errors.go b/request/graphql/schema/errors.go index e832e687ee..01f6e9bc9b 100644 --- a/request/graphql/schema/errors.go +++ b/request/graphql/schema/errors.go @@ -27,6 +27,9 @@ const ( errIndexUnknownArgument string = "index with unknown argument" errIndexInvalidArgument string = "index with invalid argument" errIndexInvalidName string = "index with invalid name" + errPolicyUnknownArgument string = "policy with unknown argument" + errPolicyInvalidIDProp string = "policy directive with invalid id property" + errPolicyInvalidResourceProp string = "policy directive with invalid resource property" errViewRelationMustBeOneSided string = "relations in views must only be defined on one schema" ) @@ -51,6 +54,9 @@ var ( ErrIndexMissingFields = errors.New(errIndexMissingFields) ErrIndexWithUnknownArg = errors.New(errIndexUnknownArgument) ErrIndexWithInvalidArg = errors.New(errIndexInvalidArgument) + ErrPolicyWithUnknownArg = errors.New(errPolicyUnknownArgument) + ErrPolicyInvalidIDProp = errors.New(errPolicyInvalidIDProp) + ErrPolicyInvalidResourceProp = errors.New(errPolicyInvalidResourceProp) ErrViewRelationMustBeOneSided = errors.New(errViewRelationMustBeOneSided) ) diff --git a/request/graphql/schema/manager.go b/request/graphql/schema/manager.go index 89860d2c53..f4a2cb3e5b 100644 --- a/request/graphql/schema/manager.go +++ b/request/graphql/schema/manager.go @@ -113,6 +113,7 @@ func defaultDirectivesType() []*gql.Directive { return []*gql.Directive{ schemaTypes.CRDTFieldDirective, schemaTypes.ExplainDirective, + schemaTypes.PolicyDirective, schemaTypes.IndexDirective, schemaTypes.IndexFieldDirective, schemaTypes.PrimaryDirective, diff --git a/request/graphql/schema/types/types.go b/request/graphql/schema/types/types.go index 37cb840d05..2273e3adb9 100644 --- a/request/graphql/schema/types/types.go +++ b/request/graphql/schema/types/types.go @@ -29,6 +29,10 @@ const ( CRDTDirectiveLabel = "crdt" CRDTDirectivePropType = "type" + PolicySchemaDirectiveLabel = "policy" + PolicySchemaDirectivePropID = "id" + PolicySchemaDirectivePropResource = "resource" + IndexDirectiveLabel = "index" IndexDirectivePropName = "name" IndexDirectivePropUnique = "unique" @@ -94,6 +98,22 @@ var ( }, }) + PolicyDirective *gql.Directive = gql.NewDirective(gql.DirectiveConfig{ + Name: PolicySchemaDirectiveLabel, + Description: "@policy is a directive that can be used to link a policy on a collection type.", + Args: gql.FieldConfigArgument{ + PolicySchemaDirectivePropID: &gql.ArgumentConfig{ + Type: gql.String, + }, + PolicySchemaDirectivePropResource: &gql.ArgumentConfig{ + Type: gql.String, + }, + }, + Locations: []string{ + gql.DirectiveLocationObject, + }, + }) + IndexDirective *gql.Directive = gql.NewDirective(gql.DirectiveConfig{ Name: IndexDirectiveLabel, Description: "@index is a directive that can be used to create an index on a type.", diff --git a/tests/bench/bench_util.go b/tests/bench/bench_util.go index dac81d0ce2..90b9cd0768 100644 --- a/tests/bench/bench_util.go +++ b/tests/bench/bench_util.go @@ -22,6 +22,7 @@ import ( "github.com/sourcenetwork/badger/v4" "github.com/sourcenetwork/corelog" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/errors" "github.com/sourcenetwork/defradb/tests/bench/fixtures" @@ -170,7 +171,7 @@ func BackfillBenchmarkDB( // in place. The error check could prob use a wrap system // but its fine :). for { - if err := cols[j].Create(ctx, doc); err != nil && + if err := cols[j].Create(ctx, acpIdentity.NoIdentity, doc); err != nil && err.Error() == badger.ErrConflict.Error() { log.InfoContext( ctx, diff --git a/tests/bench/collection/utils.go b/tests/bench/collection/utils.go index a1bed37d3a..b8faa0dd50 100644 --- a/tests/bench/collection/utils.go +++ b/tests/bench/collection/utils.go @@ -17,6 +17,7 @@ import ( "sync" "testing" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/errors" benchutils "github.com/sourcenetwork/defradb/tests/bench" @@ -72,7 +73,12 @@ func runCollectionBenchGetSync(b *testing.B, for i := 0; i < b.N; i++ { // outer benchmark loop for j := 0; j < opCount/numTypes; j++ { // number of Get operations we want to execute for k := 0; k < numTypes; k++ { // apply op to all the related types - collections[k].Get(ctx, listOfDocIDs[j][k], false) //nolint:errcheck + collections[k].Get( //nolint:errcheck + ctx, + acpIdentity.NoIdentity, + listOfDocIDs[j][k], + false, + ) } } } @@ -98,7 +104,12 @@ func runCollectionBenchGetAsync(b *testing.B, for k := 0; k < numTypes; k++ { // apply op to all the related types wg.Add(1) go func(ctx context.Context, col client.Collection, docID client.DocID) { - col.Get(ctx, docID, false) //nolint:errcheck + col.Get( //nolint:errcheck + ctx, + acpIdentity.NoIdentity, + docID, + false, + ) wg.Done() }(ctx, collections[k], listOfDocIDs[j][k]) } @@ -173,7 +184,7 @@ func runCollectionBenchCreateMany( docs[j], _ = client.NewDocFromJSON([]byte(d[0]), collections[0].Schema()) } - collections[0].CreateMany(ctx, docs) //nolint:errcheck + collections[0].CreateMany(ctx, acpIdentity.NoIdentity, docs) //nolint:errcheck } b.StopTimer() @@ -194,7 +205,7 @@ func runCollectionBenchCreateSync(b *testing.B, docs, _ := fixture.GenerateDocs() for k := 0; k < numTypes; k++ { doc, _ := client.NewDocFromJSON([]byte(docs[k]), collections[k].Schema()) - collections[k].Create(ctx, doc) //nolint:errcheck + collections[k].Create(ctx, acpIdentity.NoIdentity, doc) //nolint:errcheck } } } @@ -233,7 +244,7 @@ func runCollectionBenchCreateAsync(b *testing.B, // create the documents for j := 0; j < numTypes; j++ { doc, _ := client.NewDocFromJSON([]byte(docs[j]), collections[j].Schema()) - collections[j].Create(ctx, doc) //nolint:errcheck + collections[j].Create(ctx, acpIdentity.NoIdentity, doc) //nolint:errcheck } wg.Done() diff --git a/tests/bench/query/planner/utils.go b/tests/bench/query/planner/utils.go index fdd504175a..5bb4472840 100644 --- a/tests/bench/query/planner/utils.go +++ b/tests/bench/query/planner/utils.go @@ -15,6 +15,8 @@ import ( "fmt" "testing" + "github.com/sourcenetwork/defradb/acp" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/core" "github.com/sourcenetwork/defradb/datastore" "github.com/sourcenetwork/defradb/errors" @@ -78,7 +80,13 @@ func runMakePlanBench( b.ResetTimer() for i := 0; i < b.N; i++ { - planner := planner.New(ctx, db.WithTxn(txn), txn) + planner := planner.New( + ctx, + acpIdentity.NoIdentity, + acp.NoACP, + db.WithTxn(txn), + txn, + ) plan, err := planner.MakePlan(q) if err != nil { return errors.Wrap("failed to make plan", err) diff --git a/tests/bench/query/simple/utils.go b/tests/bench/query/simple/utils.go index 14752e7ae2..e4604f96a0 100644 --- a/tests/bench/query/simple/utils.go +++ b/tests/bench/query/simple/utils.go @@ -17,6 +17,7 @@ import ( "strings" "testing" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/errors" benchutils "github.com/sourcenetwork/defradb/tests/bench" @@ -70,7 +71,7 @@ func runQueryBenchGetSync( b.ResetTimer() for i := 0; i < b.N; i++ { - res := db.ExecRequest(ctx, query) + res := db.ExecRequest(ctx, acpIdentity.NoIdentity, query) if len(res.GQL.Errors) > 0 { return errors.New(fmt.Sprintf("Query error: %v", res.GQL.Errors)) } diff --git a/tests/clients/cli/wrapper.go b/tests/clients/cli/wrapper.go index 4c52a86abc..d10188d4b2 100644 --- a/tests/clients/cli/wrapper.go +++ b/tests/clients/cli/wrapper.go @@ -23,6 +23,7 @@ import ( blockstore "github.com/ipfs/boxo/blockstore" "github.com/lens-vm/lens/host-go/config/model" "github.com/libp2p/go-libp2p/core/peer" + "github.com/sourcenetwork/immutable" "github.com/sourcenetwork/defradb/cli" @@ -171,6 +172,28 @@ func (w *Wrapper) BasicExport(ctx context.Context, config *client.BackupConfig) return err } +func (w *Wrapper) AddPolicy( + ctx context.Context, + creator string, + policy string, +) (client.AddPolicyResult, error) { + args := []string{"client", "acp", "policy", "add"} + args = append(args, "--identity", creator) + args = append(args, policy) + + data, err := w.cmd.execute(ctx, args) + if err != nil { + return client.AddPolicyResult{}, err + } + + var addPolicyResult client.AddPolicyResult + if err := json.Unmarshal(data, &addPolicyResult); err != nil { + return client.AddPolicyResult{}, err + } + + return addPolicyResult, err +} + func (w *Wrapper) AddSchema(ctx context.Context, schema string) ([]client.CollectionDescription, error) { args := []string{"client", "schema", "add"} args = append(args, schema) @@ -369,10 +392,18 @@ func (w *Wrapper) GetAllIndexes(ctx context.Context) (map[client.CollectionName] return indexes, nil } -func (w *Wrapper) ExecRequest(ctx context.Context, query string) *client.RequestResult { +func (w *Wrapper) ExecRequest( + ctx context.Context, + identity immutable.Option[string], + query string, +) *client.RequestResult { args := []string{"client", "query"} args = append(args, query) + if identity.HasValue() { + args = append(args, "--identity", identity.Value()) + } + result := &client.RequestResult{} stdOut, stdErr, err := w.cmd.executeStream(args) diff --git a/tests/clients/cli/wrapper_collection.go b/tests/clients/cli/wrapper_collection.go index be7c3302ac..9bb8fb9938 100644 --- a/tests/clients/cli/wrapper_collection.go +++ b/tests/clients/cli/wrapper_collection.go @@ -56,7 +56,11 @@ func (c *Collection) Definition() client.CollectionDefinition { return c.def } -func (c *Collection) Create(ctx context.Context, doc *client.Document) error { +func (c *Collection) Create( + ctx context.Context, + identity immutable.Option[string], + doc *client.Document, +) error { if !c.Description().Name.HasValue() { return client.ErrOperationNotPermittedOnNamelessCols } @@ -64,6 +68,10 @@ func (c *Collection) Create(ctx context.Context, doc *client.Document) error { args := []string{"client", "collection", "create"} args = append(args, "--name", c.Description().Name.Value()) + if identity.HasValue() { + args = append(args, "--identity", identity.Value()) + } + document, err := doc.String() if err != nil { return err @@ -78,7 +86,11 @@ func (c *Collection) Create(ctx context.Context, doc *client.Document) error { return nil } -func (c *Collection) CreateMany(ctx context.Context, docs []*client.Document) error { +func (c *Collection) CreateMany( + ctx context.Context, + identity immutable.Option[string], + docs []*client.Document, +) error { if !c.Description().Name.HasValue() { return client.ErrOperationNotPermittedOnNamelessCols } @@ -86,6 +98,10 @@ func (c *Collection) CreateMany(ctx context.Context, docs []*client.Document) er args := []string{"client", "collection", "create"} args = append(args, "--name", c.Description().Name.Value()) + if identity.HasValue() { + args = append(args, "--identity", identity.Value()) + } + docMapList := make([]map[string]any, len(docs)) for i, doc := range docs { docMap, err := doc.ToMap() @@ -110,13 +126,22 @@ func (c *Collection) CreateMany(ctx context.Context, docs []*client.Document) er return nil } -func (c *Collection) Update(ctx context.Context, doc *client.Document) error { +func (c *Collection) Update( + ctx context.Context, + identity immutable.Option[string], + doc *client.Document, +) error { if !c.Description().Name.HasValue() { return client.ErrOperationNotPermittedOnNamelessCols } args := []string{"client", "collection", "update"} args = append(args, "--name", c.Description().Name.Value()) + + if identity.HasValue() { + args = append(args, "--identity", identity.Value()) + } + args = append(args, "--docID", doc.ID().String()) document, err := doc.ToJSONPatch() @@ -133,41 +158,58 @@ func (c *Collection) Update(ctx context.Context, doc *client.Document) error { return nil } -func (c *Collection) Save(ctx context.Context, doc *client.Document) error { - _, err := c.Get(ctx, doc.ID(), true) +func (c *Collection) Save( + ctx context.Context, + identity immutable.Option[string], + doc *client.Document, +) error { + _, err := c.Get(ctx, identity, doc.ID(), true) if err == nil { - return c.Update(ctx, doc) + return c.Update(ctx, identity, doc) } - if errors.Is(err, client.ErrDocumentNotFound) { - return c.Create(ctx, doc) + if errors.Is(err, client.ErrDocumentNotFoundOrNotAuthorized) { + return c.Create(ctx, identity, doc) } return err } -func (c *Collection) Delete(ctx context.Context, docID client.DocID) (bool, error) { - res, err := c.DeleteWithDocID(ctx, docID) +func (c *Collection) Delete( + ctx context.Context, + identity immutable.Option[string], + docID client.DocID, +) (bool, error) { + res, err := c.DeleteWithDocID(ctx, identity, docID) if err != nil { return false, err } return res.Count == 1, nil } -func (c *Collection) Exists(ctx context.Context, docID client.DocID) (bool, error) { - _, err := c.Get(ctx, docID, false) +func (c *Collection) Exists( + ctx context.Context, + identity immutable.Option[string], + docID client.DocID, +) (bool, error) { + _, err := c.Get(ctx, identity, docID, false) if err != nil { return false, err } return true, nil } -func (c *Collection) UpdateWith(ctx context.Context, target any, updater string) (*client.UpdateResult, error) { +func (c *Collection) UpdateWith( + ctx context.Context, + identity immutable.Option[string], + target any, + updater string, +) (*client.UpdateResult, error) { switch t := target.(type) { case string, map[string]any, *request.Filter: - return c.UpdateWithFilter(ctx, t, updater) + return c.UpdateWithFilter(ctx, identity, t, updater) case client.DocID: - return c.UpdateWithDocID(ctx, t, updater) + return c.UpdateWithDocID(ctx, identity, t, updater) case []client.DocID: - return c.UpdateWithDocIDs(ctx, t, updater) + return c.UpdateWithDocIDs(ctx, identity, t, updater) default: return nil, client.ErrInvalidUpdateTarget } @@ -190,6 +232,7 @@ func (c *Collection) updateWith( func (c *Collection) UpdateWithFilter( ctx context.Context, + identity immutable.Option[string], filter any, updater string, ) (*client.UpdateResult, error) { @@ -199,6 +242,11 @@ func (c *Collection) UpdateWithFilter( args := []string{"client", "collection", "update"} args = append(args, "--name", c.Description().Name.Value()) + + if identity.HasValue() { + args = append(args, "--identity", identity.Value()) + } + args = append(args, "--updater", updater) filterJSON, err := json.Marshal(filter) @@ -212,6 +260,7 @@ func (c *Collection) UpdateWithFilter( func (c *Collection) UpdateWithDocID( ctx context.Context, + identity immutable.Option[string], docID client.DocID, updater string, ) (*client.UpdateResult, error) { @@ -221,6 +270,11 @@ func (c *Collection) UpdateWithDocID( args := []string{"client", "collection", "update"} args = append(args, "--name", c.Description().Name.Value()) + + if identity.HasValue() { + args = append(args, "--identity", identity.Value()) + } + args = append(args, "--docID", docID.String()) args = append(args, "--updater", updater) @@ -229,6 +283,7 @@ func (c *Collection) UpdateWithDocID( func (c *Collection) UpdateWithDocIDs( ctx context.Context, + identity immutable.Option[string], docIDs []client.DocID, updater string, ) (*client.UpdateResult, error) { @@ -238,6 +293,11 @@ func (c *Collection) UpdateWithDocIDs( args := []string{"client", "collection", "update"} args = append(args, "--name", c.Description().Name.Value()) + + if identity.HasValue() { + args = append(args, "--identity", identity.Value()) + } + args = append(args, "--updater", updater) strDocIDs := make([]string, len(docIDs)) @@ -249,14 +309,18 @@ func (c *Collection) UpdateWithDocIDs( return c.updateWith(ctx, args) } -func (c *Collection) DeleteWith(ctx context.Context, target any) (*client.DeleteResult, error) { +func (c *Collection) DeleteWith( + ctx context.Context, + identity immutable.Option[string], + target any, +) (*client.DeleteResult, error) { switch t := target.(type) { case string, map[string]any, *request.Filter: - return c.DeleteWithFilter(ctx, t) + return c.DeleteWithFilter(ctx, identity, t) case client.DocID: - return c.DeleteWithDocID(ctx, t) + return c.DeleteWithDocID(ctx, identity, t) case []client.DocID: - return c.DeleteWithDocIDs(ctx, t) + return c.DeleteWithDocIDs(ctx, identity, t) default: return nil, client.ErrInvalidDeleteTarget } @@ -277,7 +341,11 @@ func (c *Collection) deleteWith( return &res, nil } -func (c *Collection) DeleteWithFilter(ctx context.Context, filter any) (*client.DeleteResult, error) { +func (c *Collection) DeleteWithFilter( + ctx context.Context, + identity immutable.Option[string], + filter any, +) (*client.DeleteResult, error) { if !c.Description().Name.HasValue() { return nil, client.ErrOperationNotPermittedOnNamelessCols } @@ -285,6 +353,10 @@ func (c *Collection) DeleteWithFilter(ctx context.Context, filter any) (*client. args := []string{"client", "collection", "delete"} args = append(args, "--name", c.Description().Name.Value()) + if identity.HasValue() { + args = append(args, "--identity", identity.Value()) + } + filterJSON, err := json.Marshal(filter) if err != nil { return nil, err @@ -294,19 +366,32 @@ func (c *Collection) DeleteWithFilter(ctx context.Context, filter any) (*client. return c.deleteWith(ctx, args) } -func (c *Collection) DeleteWithDocID(ctx context.Context, docID client.DocID) (*client.DeleteResult, error) { +func (c *Collection) DeleteWithDocID( + ctx context.Context, + identity immutable.Option[string], + docID client.DocID, +) (*client.DeleteResult, error) { if !c.Description().Name.HasValue() { return nil, client.ErrOperationNotPermittedOnNamelessCols } args := []string{"client", "collection", "delete"} args = append(args, "--name", c.Description().Name.Value()) + + if identity.HasValue() { + args = append(args, "--identity", identity.Value()) + } + args = append(args, "--docID", docID.String()) return c.deleteWith(ctx, args) } -func (c *Collection) DeleteWithDocIDs(ctx context.Context, docIDs []client.DocID) (*client.DeleteResult, error) { +func (c *Collection) DeleteWithDocIDs( + ctx context.Context, + identity immutable.Option[string], + docIDs []client.DocID, +) (*client.DeleteResult, error) { if !c.Description().Name.HasValue() { return nil, client.ErrOperationNotPermittedOnNamelessCols } @@ -314,6 +399,10 @@ func (c *Collection) DeleteWithDocIDs(ctx context.Context, docIDs []client.DocID args := []string{"client", "collection", "delete"} args = append(args, "--name", c.Description().Name.Value()) + if identity.HasValue() { + args = append(args, "--identity", identity.Value()) + } + strDocIDs := make([]string, len(docIDs)) for i, v := range docIDs { strDocIDs[i] = v.String() @@ -323,13 +412,23 @@ func (c *Collection) DeleteWithDocIDs(ctx context.Context, docIDs []client.DocID return c.deleteWith(ctx, args) } -func (c *Collection) Get(ctx context.Context, docID client.DocID, showDeleted bool) (*client.Document, error) { +func (c *Collection) Get( + ctx context.Context, + identity immutable.Option[string], + docID client.DocID, + showDeleted bool, +) (*client.Document, error) { if !c.Description().Name.HasValue() { return nil, client.ErrOperationNotPermittedOnNamelessCols } args := []string{"client", "collection", "get"} args = append(args, "--name", c.Description().Name.Value()) + + if identity.HasValue() { + args = append(args, "--identity", identity.Value()) + } + args = append(args, docID.String()) if showDeleted { @@ -356,7 +455,10 @@ func (c *Collection) WithTxn(tx datastore.Txn) client.Collection { } } -func (c *Collection) GetAllDocIDs(ctx context.Context) (<-chan client.DocIDResult, error) { +func (c *Collection) GetAllDocIDs( + ctx context.Context, + identity immutable.Option[string], +) (<-chan client.DocIDResult, error) { if !c.Description().Name.HasValue() { return nil, client.ErrOperationNotPermittedOnNamelessCols } diff --git a/tests/clients/http/wrapper.go b/tests/clients/http/wrapper.go index 4de71c4f1f..415212b99c 100644 --- a/tests/clients/http/wrapper.go +++ b/tests/clients/http/wrapper.go @@ -17,6 +17,7 @@ import ( blockstore "github.com/ipfs/boxo/blockstore" "github.com/lens-vm/lens/host-go/config/model" "github.com/libp2p/go-libp2p/core/peer" + "github.com/sourcenetwork/immutable" "github.com/sourcenetwork/defradb/client" @@ -97,6 +98,14 @@ func (w *Wrapper) AddSchema(ctx context.Context, schema string) ([]client.Collec return w.client.AddSchema(ctx, schema) } +func (w *Wrapper) AddPolicy( + ctx context.Context, + creator string, + policy string, +) (client.AddPolicyResult, error) { + return w.client.AddPolicy(ctx, creator, policy) +} + func (w *Wrapper) PatchSchema( ctx context.Context, patch string, @@ -160,8 +169,12 @@ func (w *Wrapper) GetAllIndexes(ctx context.Context) (map[client.CollectionName] return w.client.GetAllIndexes(ctx) } -func (w *Wrapper) ExecRequest(ctx context.Context, query string) *client.RequestResult { - return w.client.ExecRequest(ctx, query) +func (w *Wrapper) ExecRequest( + ctx context.Context, + identity immutable.Option[string], + query string, +) *client.RequestResult { + return w.client.ExecRequest(ctx, identity, query) } func (w *Wrapper) NewTxn(ctx context.Context, readOnly bool) (datastore.Txn, error) { diff --git a/tests/gen/cli/gendocs.go b/tests/gen/cli/gendocs.go index 9123bf0c2b..068af9b25a 100644 --- a/tests/gen/cli/gendocs.go +++ b/tests/gen/cli/gendocs.go @@ -19,6 +19,7 @@ import ( "github.com/spf13/cobra" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/http" "github.com/sourcenetwork/defradb/tests/gen" @@ -121,7 +122,7 @@ func saveBatchToCollections( for colName, colDocs := range colDocsMap { for _, col := range collections { if col.Description().Name.Value() == colName { - err := col.CreateMany(context.Background(), colDocs) + err := col.CreateMany(ctx, acpIdentity.NoIdentity, colDocs) if err != nil { return err } diff --git a/tests/gen/cli/util_test.go b/tests/gen/cli/util_test.go index 7f58fbe0dd..58b2db083b 100644 --- a/tests/gen/cli/util_test.go +++ b/tests/gen/cli/util_test.go @@ -51,7 +51,7 @@ func start(ctx context.Context) (*defraInstance, error) { db, err := db.NewDB(ctx, rootstore) if err != nil { - return nil, errors.Wrap("failed to create database", err) + return nil, errors.Wrap("failed to create a database", err) } handler, err := httpapi.NewHandler(db) diff --git a/tests/integration/acp.go b/tests/integration/acp.go new file mode 100644 index 0000000000..8c4969e228 --- /dev/null +++ b/tests/integration/acp.go @@ -0,0 +1,66 @@ +// 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 tests + +import ( + "github.com/sourcenetwork/immutable" + "github.com/stretchr/testify/require" +) + +// AddPolicy will attempt to add the given policy using DefraDB's ACP system. +type AddPolicy struct { + // NodeID may hold the ID (index) of the node we want to add policy to. + // + // If a value is not provided the policy will be added in all nodes. + NodeID immutable.Option[int] + + // The raw policy string. + Policy string + + // The policy creator, i.e. actor creating the policy. + Creator string + + // The expected policyID generated based on the Policy loaded in to the ACP system. + ExpectedPolicyID string + + // Any error expected from the action. Optional. + // + // String can be a partial, and the test will pass if an error is returned that + // contains this string. + ExpectedError string +} + +// addPolicyACP will attempt to add the given policy using DefraDB's ACP system. +func addPolicyACP( + s *state, + action AddPolicy, +) { + // If we expect an error, then ExpectedPolicyID should be empty. + if action.ExpectedError != "" && action.ExpectedPolicyID != "" { + require.Fail(s.t, "Expected error should not have an expected policyID with it.", s.testCase.Description) + } + + for _, node := range getNodes(action.NodeID, s.nodes) { + policyResult, err := node.AddPolicy( + s.ctx, + action.Creator, + action.Policy, + ) + + if err == nil { + require.Equal(s.t, action.ExpectedError, "") + require.Equal(s.t, action.ExpectedPolicyID, policyResult.PolicyID) + } + + expectedErrorRaised := AssertError(s.t, s.testCase.Description, err, action.ExpectedError) + assertExpectedErrorRaised(s.t, s.testCase.Description, action.ExpectedError, expectedErrorRaised) + } +} diff --git a/tests/integration/acp/README.md b/tests/integration/acp/README.md new file mode 100644 index 0000000000..1de6847ce4 --- /dev/null +++ b/tests/integration/acp/README.md @@ -0,0 +1,20 @@ +## More Information on ACP test directories. + + +1) `./defradb/tests/integration/acp/add_policy` + - This directory tests ONLY the `Adding of a Policy` through DefraDB. + - Does NOT assert the schema. + - Does NOT test DPI validation. + +2) `./defradb/tests/integration/acp/schema/add_dpi` + - This directory tests the loading/adding of a schema that has `@policy(id, resource)` + specified. The tests ensure that only a schema linking to + a valid DPI policy is accepted. Naturally these tests will also be `Adding a Policy` + through DefraDB like in (1) before actually adding the schema. If a schema has a + policy specified that doesn't exist (or wasn't added yet), that schema WILL/MUST + be rejected in these tests. + - The tests assert the schema after to ensure rejection/acceptance. + - Tests DPI validation. + + +### Learn more about [DPI Rules](/acp/README.md) diff --git a/tests/integration/acp/add_policy/README.md b/tests/integration/acp/add_policy/README.md new file mode 100644 index 0000000000..30b88492b9 --- /dev/null +++ b/tests/integration/acp/add_policy/README.md @@ -0,0 +1,20 @@ +## This directory tests the `Adding of a Policy` through DefraDB. + +### These are NOT DefraDB Policy Interface (DPI) Tests +There are certain requirements for a DPI. A resource must be a valid DPI to link to a collection. +However it's important to note that DefraDB does allow adding policies that might not have DPI +compliant resources. But as long as sourcehub (acp system) deems them to be valid they are allowed +to be added. There are various reasons for this, mostly because DefraDB is a tool that can be used +to upload policies to sourcehub that might not be only for use with collections / schema. Nonetheless +we still need a way to validate that the resource specified on the schema that is being added is DPI +compliant resource on a already registered policy. Therefore, when a schema is being added, and it has +the policyID and resource defined using the `@policy` directive, then during the 'adding of the schema' +the validation occurs. Inotherwords, we do not allow a non-DPI compliant resource to be specified on a +schema, if it is, then the schema is rejected. + +### Non-DPI Compliant Policies Documented In Tests +These test files document some cases where DefraDB would upload policies that aren't DPI compliant, +but are sourcehub compatible, might be worthwhile to look at the documented tests and notes there: +- `./with_no_perms_test.go` +- `./with_no_resources_test.go` +- `./with_permissionless_owner_test.go` diff --git a/tests/integration/acp/add_policy/basic_test.go b/tests/integration/acp/add_policy/basic_test.go new file mode 100644 index 0000000000..47aa351e77 --- /dev/null +++ b/tests/integration/acp/add_policy/basic_test.go @@ -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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_BasicYAML_ValidPolicyID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, adding basic policy in YAML format", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a basic policy that satisfies minimum DPI requirements + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + + `, + + ExpectedPolicyID: "dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_BasicJSON_ValidPolicyID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, adding basic policy in JSON format", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + { + "description": "a basic policy that satisfies minimum DPI requirements", + "resources": { + "users": { + "permissions": { + "read": { + "expr": "owner" + }, + "write": { + "expr": "owner" + } + }, + "relations": { + "owner": { + "types": [ + "actor" + ] + } + } + } + }, + "actor": { + "name": "actor" + } + } + `, + + ExpectedPolicyID: "dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/fixture.go b/tests/integration/acp/add_policy/fixture.go new file mode 100644 index 0000000000..8fc2edb7cd --- /dev/null +++ b/tests/integration/acp/add_policy/fixture.go @@ -0,0 +1,18 @@ +// 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 test_acp_add_policy + +import ( + acpUtils "github.com/sourcenetwork/defradb/tests/integration/acp" +) + +var actor1Signature = acpUtils.Actor1Signature +var actor2Signature = acpUtils.Actor2Signature diff --git a/tests/integration/acp/add_policy/with_empty_args_test.go b/tests/integration/acp/add_policy/with_empty_args_test.go new file mode 100644 index 0000000000..de93019a20 --- /dev/null +++ b/tests/integration/acp/add_policy/with_empty_args_test.go @@ -0,0 +1,93 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_EmptyPolicyData_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, adding empty policy, return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: "", + + ExpectedError: "policy data can not be empty", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_EmptyPolicyCreator_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, adding policy, with empty creator, return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: "", + + Policy: ` + description: a basic policy that satisfies minimum DPI requirements + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + + `, + + ExpectedError: "policy creator can not be empty", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_EmptyCreatorAndPolicyArgs_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, adding policy, with empty policy and empty creator, return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: "", + + Policy: "", + + ExpectedError: "policy creator can not be empty", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_extra_perms_and_relations_test.go b/tests/integration/acp/add_policy/with_extra_perms_and_relations_test.go new file mode 100644 index 0000000000..6606b62af5 --- /dev/null +++ b/tests/integration/acp/add_policy/with_extra_perms_and_relations_test.go @@ -0,0 +1,62 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_ExtraPermissionsAndExtraRelations_ValidPolicyID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, extra permissions and relations, still valid", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: owner + read: + expr: owner + reader + extra: + expr: joker + + relations: + owner: + types: + - actor + reader: + types: + - actor + joker: + types: + - actor + `, + + ExpectedPolicyID: "ecfeeebd1b65e6a21b2f1b57006176bcbc6a37ef238f27c7034953f46fe04674", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_extra_perms_test.go b/tests/integration/acp/add_policy/with_extra_perms_test.go new file mode 100644 index 0000000000..0fbcc842c0 --- /dev/null +++ b/tests/integration/acp/add_policy/with_extra_perms_test.go @@ -0,0 +1,95 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_ExtraPermissions_ValidPolicyID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, extra permissions, still valid", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + extra: + expr: owner + + relations: + owner: + types: + - actor + + actor: + name: actor + `, + + ExpectedPolicyID: "9d518bb2d5aceb2c8f9b12b909eecd50276c1bd0250069875f265166e6030bb5", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_ExtraDuplicatePermissions_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, extra duplicate permissions, return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + + actor: + name: actor + `, + + ExpectedError: "key \"write\" already set in map", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_extra_relations_test.go b/tests/integration/acp/add_policy/with_extra_relations_test.go new file mode 100644 index 0000000000..b8f568b5e6 --- /dev/null +++ b/tests/integration/acp/add_policy/with_extra_relations_test.go @@ -0,0 +1,107 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_ExtraRelations_ValidPolicyID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, extra relations, still valid", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: owner + read: + expr: owner + reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + joker: + types: + - actor + `, + + ExpectedPolicyID: "450c47aa47b7b07820f99e5cb38170dc108a2f12b137946e6b47d0c0a73b607f", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_ExtraDuplicateRelations_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, extra duplicate relations permissions, return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: owner + read: + expr: owner + reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + joker: + types: + - actor + + joker: + types: + - actor + `, + + ExpectedError: "key \"joker\" already set in map", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_invalid_creator_arg_test.go b/tests/integration/acp/add_policy/with_invalid_creator_arg_test.go new file mode 100644 index 0000000000..1c7a29c148 --- /dev/null +++ b/tests/integration/acp/add_policy/with_invalid_creator_arg_test.go @@ -0,0 +1,75 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_InvalidCreatorIdentityWithValidPolicy_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, adding policy, with invalid creator, with valid policy, return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: "invalid", + + Policy: ` + description: a basic policy that satisfies minimum DPI requirements + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + + `, + + ExpectedError: "policy creator can not be empty", + }, + }, + } + + //TODO-ACP: https://github.com/sourcenetwork/defradb/issues/2357 + testUtils.AssertPanic(t, func() { testUtils.ExecuteTestCase(t, test) }) +} + +func TestACP_AddPolicy_InvalidCreatorIdentityWithEmptyPolicy_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, adding policy, with invalid creator, with empty policy, return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: "invalid", + + Policy: "", + + ExpectedError: "policy data can not be empty", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_invalid_relations_test.go b/tests/integration/acp/add_policy/with_invalid_relations_test.go new file mode 100644 index 0000000000..9184d69426 --- /dev/null +++ b/tests/integration/acp/add_policy/with_invalid_relations_test.go @@ -0,0 +1,83 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_NoRelations_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, no relations, should return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: owner + read: + expr: owner + reader + + relations: + `, + + ExpectedError: "resource users: resource missing owner relation: invalid policy", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_NoRelationsLabel_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, no relations label, should return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: owner + read: + expr: owner + reader + `, + + ExpectedError: "resource users: resource missing owner relation: invalid policy", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_invalid_required_relation_test.go b/tests/integration/acp/add_policy/with_invalid_required_relation_test.go new file mode 100644 index 0000000000..3f3f3c4db3 --- /dev/null +++ b/tests/integration/acp/add_policy/with_invalid_required_relation_test.go @@ -0,0 +1,94 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_MissingRequiredOwnerRelation_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, missing requred owner relation, should return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: reader + read: + expr: reader + + relations: + reader: + types: + - actor + `, + + ExpectedError: "resource users: resource missing owner relation: invalid policy", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_DuplicateOwnerRelation_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, duplicate required owner relations, return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + owner: + types: + - actor + + actor: + name: actor + `, + + ExpectedError: "key \"owner\" already set in map", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_invalid_resource_test.go b/tests/integration/acp/add_policy/with_invalid_resource_test.go new file mode 100644 index 0000000000..499ff146d1 --- /dev/null +++ b/tests/integration/acp/add_policy/with_invalid_resource_test.go @@ -0,0 +1,44 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_OneResourceThatIsEmpty_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, one resource that is empty, should return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + `, + + ExpectedError: "resource users: resource missing owner relation: invalid policy", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_managed_relation_test.go b/tests/integration/acp/add_policy/with_managed_relation_test.go new file mode 100644 index 0000000000..74f89e365a --- /dev/null +++ b/tests/integration/acp/add_policy/with_managed_relation_test.go @@ -0,0 +1,61 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_WithRelationManagingOtherRelation_ValidPolicyID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, where a relation is managing another relation, valid policy id", + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy with admin relation managing reader relation + + 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", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_multi_policies_test.go b/tests/integration/acp/add_policy/with_multi_policies_test.go new file mode 100644 index 0000000000..52b7333d34 --- /dev/null +++ b/tests/integration/acp/add_policy/with_multi_policies_test.go @@ -0,0 +1,351 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_AddMultipleDifferentPolicies_ValidPolicyIDs(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add multiple different policies", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + + `, + + ExpectedPolicyID: "dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a", + }, + + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: another policy + + 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", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_AddMultipleDifferentPoliciesInDifferentFmts_ValidPolicyIDs(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add multiple different policies in different formats", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + { + "description": "a policy", + "actor": { + "name": "actor" + }, + "resources": { + "users": { + "permissions": { + "read": { + "expr": "owner" + }, + "write": { + "expr": "owner" + } + }, + "relations": { + "owner": { + "types": [ + "actor" + ] + } + } + } + } + } + `, + + ExpectedPolicyID: "dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a", + }, + + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: another policy + + 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", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_AddDuplicatePolicyByOtherCreator_ValidPolicyIDs(t *testing.T) { + const policyUsedByBoth string = ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + ` + + test := testUtils.TestCase{ + + Description: "Test acp, add duplicate policies by different actors, valid", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: policyUsedByBoth, + + ExpectedPolicyID: "dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a", + }, + + testUtils.AddPolicy{ + Creator: actor2Signature, + + Policy: policyUsedByBoth, + + ExpectedPolicyID: "551c57323f33decfdc23312e5e1036e3ab85d2414e962814dab9101619dd9ff9", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_AddMultipleDuplicatePolicies_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add duplicate policies, error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + + `, + + ExpectedPolicyID: "dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a", + }, + + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + + `, + + ExpectedError: "policy dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a: policy exists", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_AddMultipleDuplicatePoliciesDifferentFmts_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add duplicate policies different formats, error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + `, + + ExpectedPolicyID: "dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a", + }, + + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + { + "description": "a policy", + "actor": { + "name": "actor" + }, + "resources": { + "users": { + "permissions": { + "read": { + "expr": "owner" + }, + "write": { + "expr": "owner" + } + }, + "relations": { + "owner": { + "types": [ + "actor" + ] + } + } + } + } + } + `, + + ExpectedError: "policy dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a: policy exists", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_multiple_resources_test.go b/tests/integration/acp/add_policy/with_multiple_resources_test.go new file mode 100644 index 0000000000..c939600663 --- /dev/null +++ b/tests/integration/acp/add_policy/with_multiple_resources_test.go @@ -0,0 +1,173 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_MultipleResources_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, multiple resources, valid ID", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: owner + read: + expr: owner + reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + books: + permissions: + write: + expr: owner + read: + expr: owner + reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: "cf082c11fa812dddaa5093f0ccae66c2b5294efe0a2b50ffdcbc0185adf6adf1", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_MultipleResourcesUsingRelationDefinedInOther_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, multiple resources using other's relation, return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: owner + read: + expr: owner + reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + books: + permissions: + write: + expr: owner + read: + expr: owner + reader + + relations: + owner: + types: + - actor + `, + + ExpectedError: "resource books missing relation reader", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_SecondResourcesMissingRequiredOwner_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, multiple resources second missing required owner, return error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: owner + read: + expr: owner + reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + books: + permissions: + write: + expr: owner + read: + expr: owner + reader + + relations: + reader: + types: + - actor + `, + + ExpectedError: "resource books: resource missing owner relation: invalid policy", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_no_perms_test.go b/tests/integration/acp/add_policy/with_no_perms_test.go new file mode 100644 index 0000000000..0f55851468 --- /dev/null +++ b/tests/integration/acp/add_policy/with_no_perms_test.go @@ -0,0 +1,163 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +// Note: Eventhough this file shows we can load a policy, that has no permissions. It is important +// to know that DPI always has a set of permissions it requires. Therefore when a schema is loaded, +// and it has policyID and resource defined on the collection, then before we accept that schema +// the validation occurs. +// Inotherwords, we do not allow a non-DPI compliant policy to be specified on a collection schema, if +// it is the schema would be rejected. However we register the policy with acp even if +// the policy is not DPI compliant. + +func TestACP_AddPolicy_NoPermissionsOnlyOwner_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, no permissions only owner relation", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + + relations: + owner: + types: + - actor + + `, + + ExpectedPolicyID: "b6edfd9d24a79067a2f5960e1369499ebaf4c5ec6747e2f444f33bf9c3915fcb", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_NoPermissionsMultiRelations_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, no permissions with multi relations", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + + relations: + owner: + types: + - actor + reader: + types: + - actor + + `, + + ExpectedPolicyID: "7eb7448daa631cfe33da3a149f5eea716026f54bf23ce1315c594259382c5c57", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_NoPermissionsLabelOnlyOwner_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, no permissions label only owner relation", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + relations: + owner: + types: + - actor + + `, + + ExpectedPolicyID: "b6edfd9d24a79067a2f5960e1369499ebaf4c5ec6747e2f444f33bf9c3915fcb", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_NoPermissionsLabelMultiRelations_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, no permissions label with multi relations", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + relations: + owner: + types: + - actor + reader: + types: + - actor + + `, + + ExpectedPolicyID: "7eb7448daa631cfe33da3a149f5eea716026f54bf23ce1315c594259382c5c57", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_no_resources_test.go b/tests/integration/acp/add_policy/with_no_resources_test.go new file mode 100644 index 0000000000..861a77c64e --- /dev/null +++ b/tests/integration/acp/add_policy/with_no_resources_test.go @@ -0,0 +1,92 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +// Eventhough empty resources make no sense from a DefraDB (DPI) perspective, +// it is still a valid sourcehub policy for now. +func TestACP_AddPolicy_NoResource_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, no resource, valid policy", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + `, + + ExpectedPolicyID: "b72d8ec56ffb141922781d2b1b0803404bef57be0eeec98f1662f3017fc2de35", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +// Eventhough empty resources make no sense from a DefraDB (DPI) perspective, +// it is still a valid sourcehub policy for now. +func TestACP_AddPolicy_NoResourceLabel_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, no resource label, valid policy", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + `, + + ExpectedPolicyID: "b72d8ec56ffb141922781d2b1b0803404bef57be0eeec98f1662f3017fc2de35", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +// Eventhough empty resources make no sense from a DefraDB (DPI) perspective, +// it is still a valid sourcehub policy for now. +func TestACP_AddPolicy_PolicyWithOnlySpace_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, adding a policy that has only space", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: " ", + + ExpectedPolicyID: "b72d8ec56ffb141922781d2b1b0803404bef57be0eeec98f1662f3017fc2de35", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_perm_expr_test.go b/tests/integration/acp/add_policy/with_perm_expr_test.go new file mode 100644 index 0000000000..7af2c8e690 --- /dev/null +++ b/tests/integration/acp/add_policy/with_perm_expr_test.go @@ -0,0 +1,98 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_PermissionExprWithOwnerInTheEndWithMinus_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy with permission expr having owner in the end with minus, ValidID", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: reader - owner + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: "d74384d99b6732c3a6e0e47c7b75ea19553f643bcca416380530d8ad4e50e529", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +// Note: this and above test both result in different policy ids. +func TestACP_AddPolicy_PermissionExprWithOwnerInTheEndWithMinusNoSpace_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy with permission expr having owner in the end with minus no space, ValidID", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: reader-owner + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: "f6d5d6d8b0183230fcbdf06cfe14b611f782752d276006ad4622231eeaf60820", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_perm_invalid_expr_test.go b/tests/integration/acp/add_policy/with_perm_invalid_expr_test.go new file mode 100644 index 0000000000..c61a6e0b9c --- /dev/null +++ b/tests/integration/acp/add_policy/with_perm_invalid_expr_test.go @@ -0,0 +1,137 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_EmptyExpressionInPermission_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy with permission having empr expr, error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedError: "relation read: error parsing: expression needs: term", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_PermissionExprWithOwnerInTheEndWithInocorrectSymbol_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy with permission expr having owner in the end with incorrect symbol, error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: reader ^ owner + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedError: "error parsing expression reader ^ owner: unknown token:", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_PermissionExprWithOwnerInTheEndWithInocorrectSymbolNoSpace_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy with permission expr having owner in the end with incorrect symbol with no space, error", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: reader^owner + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedError: "error parsing expression reader^owner: unknown token:", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_permissionless_owner_test.go b/tests/integration/acp/add_policy/with_permissionless_owner_test.go new file mode 100644 index 0000000000..6496a1dec3 --- /dev/null +++ b/tests/integration/acp/add_policy/with_permissionless_owner_test.go @@ -0,0 +1,144 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +// Note: Similar to the one in ./with_no_perms_test.go +// Eventhough this file shows we can load a policy, that assigns no read/write permissions which +// are required for DPI. When a schema is loaded, and it has policyID and resource defined on the +// collection, then before we accept that schema the validation occurs. Inotherwords, we do not +// allow a non-DPI compliant policy to be specified on a collection schema, if it is, then the schema +// would be rejected. However we register the policy with acp even if policy isn't DPI compliant. + +func TestACP_AddPolicy_PermissionlessOwnerWrite_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy with owner having no write permissions, valid ID", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: reader + read: + expr: owner + reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: "af1ee9ffe8558da8455dc1cfc5897028c16c038a053b4cf740dfcef8032d944a", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_PermissionlessOwnerRead_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy with owner having no read permissions, valid ID", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: owner + reader + read: + expr: reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: "3ceb4a4be889998496355604b68836bc280dc26dab829af3ec45b63d7767a7f1", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddPolicy_PermissionlessOwnerReadWrite_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy with owner having no read/write permissions, valid ID", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: reader + read: + expr: owner + reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: "af1ee9ffe8558da8455dc1cfc5897028c16c038a053b4cf740dfcef8032d944a", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/add_policy/with_unused_relations_test.go b/tests/integration/acp/add_policy/with_unused_relations_test.go new file mode 100644 index 0000000000..faa7658e5e --- /dev/null +++ b/tests/integration/acp/add_policy/with_unused_relations_test.go @@ -0,0 +1,58 @@ +// 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 test_acp_add_policy + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddPolicy_UnusedRelation_ValidID(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, add policy, unused relation in permissions", + + Actions: []any{ + testUtils.AddPolicy{ + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + + `, + + ExpectedPolicyID: "e1bb7702f653d4f9a0595d2d97c209fc0da8f315be007bd19545599eed41ae42", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/fixture.go b/tests/integration/acp/fixture.go new file mode 100644 index 0000000000..ea0ccfc09d --- /dev/null +++ b/tests/integration/acp/fixture.go @@ -0,0 +1,14 @@ +// 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 test_acp + +var Actor1Signature = "cosmos1zzg43wdrhmmk89z3pmejwete2kkd4a3vn7w969" +var Actor2Signature = "cosmos1x25hhksxhu86r45hqwk28dd70qzux3262hdrll" diff --git a/tests/integration/acp/index/create_test.go b/tests/integration/acp/index/create_test.go new file mode 100644 index 0000000000..4a1d4c6fac --- /dev/null +++ b/tests/integration/acp/index/create_test.go @@ -0,0 +1,174 @@ +// 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 test_acp_index + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" + acpUtils "github.com/sourcenetwork/defradb/tests/integration/acp" +) + +// This test documents that we don't allow creating indexes on collections that have policy +// until the following is implemented: +// TODO-ACP: ACP <> P2P https://github.com/sourcenetwork/defradb/issues/2365 +func TestACP_IndexCreateWithSeparateRequest_OnCollectionWithPolicy_ReturnError(t *testing.T) { + test := testUtils.TestCase{ + Description: "Test acp, with creating new index using separate request on permissioned collection, error", + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: acpUtils.Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateIndex{ + CollectionID: 0, + + IndexName: "some_index", + + FieldName: "name", + + ExpectedError: "can not create index on a collection with a policy", + }, + + testUtils.Request{ + Request: ` + query { + Users { + name + age + } + }`, + + Results: []map[string]any{}, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +// This test documents that we don't allow creating indexes on collections that have policy +// until the following is implemented: +// TODO-ACP: ACP <> P2P https://github.com/sourcenetwork/defradb/issues/2365 +func TestACP_IndexCreateWithDirective_OnCollectionWithPolicy_ReturnError(t *testing.T) { + test := testUtils.TestCase{ + Description: "Test acp, with creating new index using directive on permissioned collection, error", + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: acpUtils.Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String @index + age: Int + } + `, + + ExpectedError: "can not create index on a collection with a policy", + }, + + testUtils.Request{ + Request: ` + query { + Users { + name + age + } + }`, + + ExpectedError: `Cannot query field "Users" on type "Query"`, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/p2p/replicator_test.go b/tests/integration/acp/p2p/replicator_test.go new file mode 100644 index 0000000000..8a581347e1 --- /dev/null +++ b/tests/integration/acp/p2p/replicator_test.go @@ -0,0 +1,89 @@ +// 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 test_acp_p2p + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" + acpUtils "github.com/sourcenetwork/defradb/tests/integration/acp" +) + +// This test documents that we don't allow setting replicator with a collections that has a policy +// until the following is implemented: +// TODO-ACP: ACP <> P2P https://github.com/sourcenetwork/defradb/issues/2366 +func TestACP_P2POneToOneReplicatorWithPermissionedCollection_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, with p2p replicator with permissioned collection, error", + + Actions: []any{ + + testUtils.RandomNetworkingConfig(), + testUtils.RandomNetworkingConfig(), + + testUtils.AddPolicy{ + + Creator: acpUtils.Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.ConfigureReplicator{ + SourceNodeID: 0, + TargetNodeID: 1, + ExpectedError: "replicator can not use all collections, as some have policy", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/p2p/subscribe_test.go b/tests/integration/acp/p2p/subscribe_test.go new file mode 100644 index 0000000000..baf4c7cd0d --- /dev/null +++ b/tests/integration/acp/p2p/subscribe_test.go @@ -0,0 +1,99 @@ +// 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 test_acp_p2p + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" + acpUtils "github.com/sourcenetwork/defradb/tests/integration/acp" +) + +// This test documents that we don't allow subscribing to a collection that has a policy +// until the following is implemented: +// TODO-ACP: ACP <> P2P https://github.com/sourcenetwork/defradb/issues/2366 +func TestACP_P2PSubscribeAddGetSingleWithPermissionedCollection_Error(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, with p2p subscribe with permissioned collection, error", + + Actions: []any{ + + testUtils.RandomNetworkingConfig(), + testUtils.RandomNetworkingConfig(), + + testUtils.AddPolicy{ + + Creator: acpUtils.Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.ConnectPeers{ + SourceNodeID: 1, + TargetNodeID: 0, + }, + + testUtils.SubscribeToCollection{ + NodeID: 1, + CollectionIDs: []int{0}, + ExpectedError: "p2p collection specified has a policy on it", + }, + + testUtils.GetAllP2PCollections{ + NodeID: 1, + ExpectedCollectionIDs: []int{}, // Note: Empty + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/register_and_delete_test.go b/tests/integration/acp/register_and_delete_test.go new file mode 100644 index 0000000000..36a2892677 --- /dev/null +++ b/tests/integration/acp/register_and_delete_test.go @@ -0,0 +1,514 @@ +// 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 test_acp + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_CreateWithoutIdentityAndDeleteWithoutIdentity_CanDelete(t *testing.T) { + // The same identity that is used to do the registering/creation should be used in the + // final read check to see the state of that registered document. + // Note: In this test that identity is empty (no identity). + + test := testUtils.TestCase{ + + Description: "Test acp, create without identity, and delete without identity, can delete", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.DeleteDoc{ + CollectionID: 0, + + DocID: 0, + }, + + testUtils.Request{ + Request: ` + query { + Users { + _docID + name + age + } + } + `, + + Results: []map[string]any{}, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_CreateWithoutIdentityAndDeleteWithIdentity_CanDelete(t *testing.T) { + // The same identity that is used to do the registering/creation should be used in the + // final read check to see the state of that registered document. + // Note: In this test that identity is empty (no identity). + + test := testUtils.TestCase{ + + Description: "Test acp, create without identity, and delete with identity, can delete", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.DeleteDoc{ + CollectionID: 0, + + Identity: Actor1Signature, + + DocID: 0, + }, + + testUtils.Request{ + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{}, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_CreateWithIdentityAndDeleteWithIdentity_CanDelete(t *testing.T) { + // OwnerIdentity should be the same identity that is used to do the registering/creation, + // and the final read check to see the state of that registered document. + OwnerIdentity := Actor1Signature + + test := testUtils.TestCase{ + + Description: "Test acp, create with identity, and delete with identity, can delete", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: OwnerIdentity, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Identity: OwnerIdentity, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.DeleteDoc{ + CollectionID: 0, + + Identity: OwnerIdentity, + + DocID: 0, + }, + + testUtils.Request{ + Identity: OwnerIdentity, + + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{}, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_CreateWithIdentityAndDeleteWithoutIdentity_CanNotDelete(t *testing.T) { + // OwnerIdentity should be the same identity that is used to do the registering/creation, + // and the final read check to see the state of that registered document. + OwnerIdentity := Actor1Signature + + test := testUtils.TestCase{ + + Description: "Test acp, create with identity, and delete without identity, can not delete", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: OwnerIdentity, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Identity: OwnerIdentity, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.DeleteDoc{ + CollectionID: 0, + + DocID: 0, + + ExpectedError: "document not found or not authorized to access", + }, + + testUtils.Request{ + Identity: OwnerIdentity, + + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{ + { + "_docID": "bae-1e608f7d-b01e-5dd5-ad4a-9c6cc3005a36", + "name": "Shahzad", + "age": int64(28), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_CreateWithIdentityAndDeleteWithWrongIdentity_CanNotDelete(t *testing.T) { + // OwnerIdentity should be the same identity that is used to do the registering/creation, + // and the final read check to see the state of that registered document. + OwnerIdentity := Actor1Signature + + WrongIdentity := Actor2Signature + + test := testUtils.TestCase{ + + Description: "Test acp, create with identity, and delete without identity, can not delete", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: OwnerIdentity, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Identity: OwnerIdentity, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.DeleteDoc{ + CollectionID: 0, + + Identity: WrongIdentity, + + DocID: 0, + + ExpectedError: "document not found or not authorized to access", + }, + + testUtils.Request{ + Identity: OwnerIdentity, + + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{ + { + "_docID": "bae-1e608f7d-b01e-5dd5-ad4a-9c6cc3005a36", + "name": "Shahzad", + "age": int64(28), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/register_and_read_test.go b/tests/integration/acp/register_and_read_test.go new file mode 100644 index 0000000000..d01a8835ea --- /dev/null +++ b/tests/integration/acp/register_and_read_test.go @@ -0,0 +1,457 @@ +// 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 test_acp + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_CreateWithoutIdentityAndReadWithoutIdentity_CanRead(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, create without identity, and read without identity, can read", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.Request{ + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{ + { + "_docID": "bae-1e608f7d-b01e-5dd5-ad4a-9c6cc3005a36", + "name": "Shahzad", + "age": int64(28), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_CreateWithoutIdentityAndReadWithIdentity_CanRead(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, create without identity, and read with identity, can read", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.Request{ + Identity: Actor1Signature, + + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{ + { + "_docID": "bae-1e608f7d-b01e-5dd5-ad4a-9c6cc3005a36", + "name": "Shahzad", + "age": int64(28), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_CreateWithIdentityAndReadWithIdentity_CanRead(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, create with identity, and read with identity, can read", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Identity: Actor1Signature, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.Request{ + Identity: Actor1Signature, + + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{ + { + "_docID": "bae-1e608f7d-b01e-5dd5-ad4a-9c6cc3005a36", + "name": "Shahzad", + "age": int64(28), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_CreateWithIdentityAndReadWithoutIdentity_CanNotRead(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, create with identity, and read without identity, can not read", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Identity: Actor1Signature, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.Request{ + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{}, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_CreateWithIdentityAndReadWithWrongIdentity_CanNotRead(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Test acp, create with identity, and read without identity, can not read", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Identity: Actor1Signature, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.Request{ + Identity: Actor2Signature, + + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{}, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/register_and_update_test.go b/tests/integration/acp/register_and_update_test.go new file mode 100644 index 0000000000..1afa89cf86 --- /dev/null +++ b/tests/integration/acp/register_and_update_test.go @@ -0,0 +1,810 @@ +// 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 test_acp + +import ( + "testing" + + "github.com/sourcenetwork/immutable" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_CreateWithoutIdentityAndUpdateWithoutIdentity_CanUpdate(t *testing.T) { + // The same identity that is used to do the registering/creation should be used in the + // final read check to see the state of that registered document. + // Note: In this test that identity is empty (no identity). + + test := testUtils.TestCase{ + + Description: "Test acp, create without identity, and update without identity, can update", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.UpdateDoc{ + CollectionID: 0, + + DocID: 0, + + Doc: ` + { + "name": "Shahzad Lone" + } + `, + }, + + testUtils.Request{ + Request: ` + query { + Users { + _docID + name + age + } + } + `, + + Results: []map[string]any{ + { + "_docID": "bae-1e608f7d-b01e-5dd5-ad4a-9c6cc3005a36", + "name": "Shahzad Lone", + "age": int64(28), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_CreateWithoutIdentityAndUpdateWithIdentity_CanUpdate(t *testing.T) { + // The same identity that is used to do the registering/creation should be used in the + // final read check to see the state of that registered document. + // Note: In this test that identity is empty (no identity). + + test := testUtils.TestCase{ + + Description: "Test acp, create without identity, and update with identity, can update", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.UpdateDoc{ + CollectionID: 0, + + Identity: Actor1Signature, + + DocID: 0, + + Doc: ` + { + "name": "Shahzad Lone" + } + `, + }, + + testUtils.Request{ + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{ + { + "_docID": "bae-1e608f7d-b01e-5dd5-ad4a-9c6cc3005a36", + "name": "Shahzad Lone", + "age": int64(28), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_CreateWithIdentityAndUpdateWithIdentity_CanUpdate(t *testing.T) { + // OwnerIdentity should be the same identity that is used to do the registering/creation, + // and the final read check to see the state of that registered document. + OwnerIdentity := Actor1Signature + + test := testUtils.TestCase{ + + Description: "Test acp, create with identity, and update with identity, can update", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: OwnerIdentity, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Identity: OwnerIdentity, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.UpdateDoc{ + CollectionID: 0, + + Identity: OwnerIdentity, + + DocID: 0, + + Doc: ` + { + "name": "Shahzad Lone" + } + `, + }, + + testUtils.Request{ + Identity: OwnerIdentity, + + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{ + { + "_docID": "bae-1e608f7d-b01e-5dd5-ad4a-9c6cc3005a36", + "name": "Shahzad Lone", + "age": int64(28), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_CreateWithIdentityAndUpdateWithoutIdentity_CanNotUpdate(t *testing.T) { + // OwnerIdentity should be the same identity that is used to do the registering/creation, + // and the final read check to see the state of that registered document. + OwnerIdentity := Actor1Signature + + test := testUtils.TestCase{ + + Description: "Test acp, create with identity, and update without identity, can not update", + + SupportedMutationTypes: immutable.Some([]testUtils.MutationType{ + // GQL mutation will return no error when wrong identity is used so test that separately. + testUtils.CollectionNamedMutationType, + testUtils.CollectionSaveMutationType, + }), + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: OwnerIdentity, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Identity: OwnerIdentity, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.UpdateDoc{ + CollectionID: 0, + + DocID: 0, + + Doc: ` + { + "name": "Shahzad Lone" + } + `, + + ExpectedError: "document not found or not authorized to access", + }, + + testUtils.Request{ + Identity: OwnerIdentity, + + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{ + { + "_docID": "bae-1e608f7d-b01e-5dd5-ad4a-9c6cc3005a36", + "name": "Shahzad", + "age": int64(28), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_CreateWithIdentityAndUpdateWithWrongIdentity_CanNotUpdate(t *testing.T) { + // OwnerIdentity should be the same identity that is used to do the registering/creation, + // and the final read check to see the state of that registered document. + OwnerIdentity := Actor1Signature + + WrongIdentity := Actor2Signature + + test := testUtils.TestCase{ + + Description: "Test acp, create with identity, and update without identity, can not update", + + SupportedMutationTypes: immutable.Some([]testUtils.MutationType{ + // GQL mutation will return no error when wrong identity is used so test that separately. + testUtils.CollectionNamedMutationType, + testUtils.CollectionSaveMutationType, + }), + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: OwnerIdentity, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Identity: OwnerIdentity, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.UpdateDoc{ + CollectionID: 0, + + Identity: WrongIdentity, + + DocID: 0, + + Doc: ` + { + "name": "Shahzad Lone" + } + `, + + ExpectedError: "document not found or not authorized to access", + }, + + testUtils.Request{ + Identity: OwnerIdentity, + + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{ + { + "_docID": "bae-1e608f7d-b01e-5dd5-ad4a-9c6cc3005a36", + "name": "Shahzad", + "age": int64(28), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +// This separate GQL test should be merged with the ones above when all the clients are fixed +// to behave the same in: https://github.com/sourcenetwork/defradb/issues/2410 +func TestACP_CreateWithIdentityAndUpdateWithoutIdentityGQL_CanNotUpdate(t *testing.T) { + // OwnerIdentity should be the same identity that is used to do the registering/creation, + // and the final read check to see the state of that registered document. + OwnerIdentity := Actor1Signature + + test := testUtils.TestCase{ + + Description: "Test acp, create with identity, and update without identity (gql), can not update", + + SupportedMutationTypes: immutable.Some([]testUtils.MutationType{ + // GQL mutation will return no error when wrong identity is used so test that separately. + testUtils.GQLRequestMutationType, + }), + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: OwnerIdentity, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Identity: OwnerIdentity, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.UpdateDoc{ + CollectionID: 0, + + DocID: 0, + + Doc: ` + { + "name": "Shahzad Lone" + } + `, + }, + + testUtils.Request{ + Identity: OwnerIdentity, + + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{ + { + "_docID": "bae-1e608f7d-b01e-5dd5-ad4a-9c6cc3005a36", + "name": "Shahzad", + "age": int64(28), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +// This separate GQL test should be merged with the ones above when all the clients are fixed +// to behave the same in: https://github.com/sourcenetwork/defradb/issues/2410 +func TestACP_CreateWithIdentityAndUpdateWithWrongIdentityGQL_CanNotUpdate(t *testing.T) { + // OwnerIdentity should be the same identity that is used to do the registering/creation, + // and the final read check to see the state of that registered document. + OwnerIdentity := Actor1Signature + + WrongIdentity := Actor2Signature + + test := testUtils.TestCase{ + + Description: "Test acp, create with identity, and update without identity (gql), can not update", + + SupportedMutationTypes: immutable.Some([]testUtils.MutationType{ + // GQL mutation will return no error when wrong identity is used so test that separately. + testUtils.GQLRequestMutationType, + }), + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: OwnerIdentity, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.CreateDoc{ + CollectionID: 0, + + Identity: OwnerIdentity, + + Doc: ` + { + "name": "Shahzad", + "age": 28 + } + `, + }, + + testUtils.UpdateDoc{ + CollectionID: 0, + + Identity: WrongIdentity, + + DocID: 0, + + Doc: ` + { + "name": "Shahzad Lone" + } + `, + }, + + testUtils.Request{ + Identity: OwnerIdentity, + + Request: ` + query { + Users { + _docID + name + age + } + } + `, + Results: []map[string]any{ + { + "_docID": "bae-1e608f7d-b01e-5dd5-ad4a-9c6cc3005a36", + "name": "Shahzad", + "age": int64(28), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/README.md b/tests/integration/acp/schema/add_dpi/README.md new file mode 100644 index 0000000000..4bb0b065c9 --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/README.md @@ -0,0 +1,7 @@ +## Accept vs Reject: +- All tests are broken into `accept_*_test.go` and `reject_*_test.go` files. +- Accepted tests are with valid DPIs (hence schema is accepted). +- Rejected tests are with invalid DPIs (hence schema is rejected). +- There are also some Partially-DPI tests that are both accepted and rejected depending on the resource. + +Learn more about the DefraDB Policy Interface [DPI](/acp/README.md) diff --git a/tests/integration/acp/schema/add_dpi/accept_basic_dpi_fmts_test.go b/tests/integration/acp/schema/add_dpi/accept_basic_dpi_fmts_test.go new file mode 100644 index 0000000000..2e08739176 --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/accept_basic_dpi_fmts_test.go @@ -0,0 +1,214 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" + schemaUtils "github.com/sourcenetwork/defradb/tests/integration/schema" +) + +func TestACP_AddDPISchema_BasicYAML_SchemaAccepted(t *testing.T) { + policyIDOfValidDPI := "dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a" + + test := testUtils.TestCase{ + + Description: "Test acp, specify basic policy that was added in YAML format, accept schema", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a basic policy that satisfies minimum DPI requirements + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfValidDPI, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "Users", // NOTE: "Users" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_BasicJSON_SchemaAccepted(t *testing.T) { + policyIDOfValidDPI := "dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a" + + test := testUtils.TestCase{ + + Description: "Test acp, specify basic policy that was added in JSON format, accept schema", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + { + "description": "a basic policy that satisfies minimum DPI requirements", + "resources": { + "users": { + "permissions": { + "read": { + "expr": "owner" + }, + "write": { + "expr": "owner" + } + }, + "relations": { + "owner": { + "types": [ + "actor" + ] + } + } + } + }, + "actor": { + "name": "actor" + } + } + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfValidDPI, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "Users", // NOTE: "Users" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/accept_extra_permissions_on_dpi_test.go b/tests/integration/acp/schema/add_dpi/accept_extra_permissions_on_dpi_test.go new file mode 100644 index 0000000000..5e0bdf5b3c --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/accept_extra_permissions_on_dpi_test.go @@ -0,0 +1,316 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" + schemaUtils "github.com/sourcenetwork/defradb/tests/integration/schema" +) + +func TestACP_AddDPISchema_WithExtraPermsHavingRequiredRelation_AcceptSchema(t *testing.T) { + policyIDOfValidDPI := "16e39e650d4cbd5161ae0c572edad6f7e2950c1c4afa37e427af3c8708e68f0f" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, with extra permissions having required relation, schema accepted", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + reader + magic: + expr: owner - reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfValidDPI, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "Users", // NOTE: "Users" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_WithExtraPermsHavingRequiredRelationInTheEnd_AcceptSchema(t *testing.T) { + policyIDOfValidDPI := "35b6f3db54cfb0f451a4faba77d2c71d8718215caeb5a15a8570dfdba07b694d" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, with extra permissions having required relation in the end, schema accepted", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + magic: + expr: reader & owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfValidDPI, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "Users", // NOTE: "Users" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_WithExtraPermsHavingNoRequiredRelation_AcceptSchema(t *testing.T) { + policyIDOfValidDPI := "7b6266a93bfb6920bf57884f55c3823a5a5147c4ce445a9fc703b7c1e59b2d12" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, with extra permissions having no required relation, schema accepted", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + magic: + expr: reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfValidDPI, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "Users", // NOTE: "Users" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/accept_managed_relation_on_dpi_test.go b/tests/integration/acp/schema/add_dpi/accept_managed_relation_on_dpi_test.go new file mode 100644 index 0000000000..01c674426e --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/accept_managed_relation_on_dpi_test.go @@ -0,0 +1,121 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" + schemaUtils "github.com/sourcenetwork/defradb/tests/integration/schema" +) + +func TestACP_AddDPISchema_WithManagedRelation_AcceptSchemas(t *testing.T) { + policyIDOfValidDPI := "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, where one resource is specified on different schemas, schemas accepted", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + 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: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfValidDPI, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "Users", // NOTE: "Users" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/accept_mixed_resources_on_partial_dpi_test.go b/tests/integration/acp/schema/add_dpi/accept_mixed_resources_on_partial_dpi_test.go new file mode 100644 index 0000000000..666f4264ee --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/accept_mixed_resources_on_partial_dpi_test.go @@ -0,0 +1,131 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" + schemaUtils "github.com/sourcenetwork/defradb/tests/integration/schema" +) + +func TestACP_AddDPISchema_PartialValidDPIButUseOnlyValidDPIResource_AcceptSchema(t *testing.T) { + policyIDOfPartiallyValidDPI := "d5d411825b2d8fa5a550f1e34153b88b375ed9c9af19ce6d2ba1769e237a45d0" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, has both valid & invalid resources, but use only valid resource, schema accepted", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Partially Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + usersValid: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + + usersInvalid: + permissions: + read: + expr: reader - owner + write: + expr: reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfPartiallyValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "usersValid" + ) { + name: String + age: Int + } + `, + policyIDOfPartiallyValidDPI, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "Users", // NOTE: "Users" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/accept_multi_dpis_test.go b/tests/integration/acp/schema/add_dpi/accept_multi_dpis_test.go new file mode 100644 index 0000000000..19181ec1a1 --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/accept_multi_dpis_test.go @@ -0,0 +1,183 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" + schemaUtils "github.com/sourcenetwork/defradb/tests/integration/schema" +) + +func TestACP_AddDPISchema_AddDuplicateDPIsByOtherCreatorsUseBoth_AcceptSchema(t *testing.T) { + const sameResourceNameOnBothDPI string = "users" + const validDPIUsedByBoth string = ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + ` + + const policyIDOfFirstCreatorsDPI string = "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" + const policyIDOfSecondCreatorsDPI string = "d33aa07a28ea19ed07a5256eb7e7f5600b0e0af13254889a7fce60202c4f6c7e" + + test := testUtils.TestCase{ + + Description: "Test acp, add duplicate DPIs by different actors, accept both schemas", + + Actions: []any{ + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: validDPIUsedByBoth, + + ExpectedPolicyID: policyIDOfFirstCreatorsDPI, + }, + + testUtils.AddPolicy{ + + Creator: actor2Signature, + + Policy: validDPIUsedByBoth, + + ExpectedPolicyID: policyIDOfSecondCreatorsDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type OldUsers @policy( + id: "%s", + resource: "%s" + ) { + name: String + age: Int + } + `, + policyIDOfFirstCreatorsDPI, + sameResourceNameOnBothDPI, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "OldUsers") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "OldUsers", // NOTE: "OldUsers" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type NewUsers @policy( + id: "%s", + resource: "%s" + ) { + name: String + age: Int + } + `, + policyIDOfSecondCreatorsDPI, + sameResourceNameOnBothDPI, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "NewUsers") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "NewUsers", // NOTE: "NewUsers" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/accept_multi_resources_on_dpi_test.go b/tests/integration/acp/schema/add_dpi/accept_multi_resources_on_dpi_test.go new file mode 100644 index 0000000000..d8c259d57c --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/accept_multi_resources_on_dpi_test.go @@ -0,0 +1,281 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" + schemaUtils "github.com/sourcenetwork/defradb/tests/integration/schema" +) + +func TestACP_AddDPISchema_WithMultipleResources_AcceptSchema(t *testing.T) { + policyIDOfValidDPI := "f3e521de628fa607ba11af0e9b53e2fb74ca0e6ea33622003d1f43dbae0ce41d" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, with multiple resources, schema accepted", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + + books: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfValidDPI, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "Users", // NOTE: "Users" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_WithMultipleResourcesBothBeingUsed_AcceptSchema(t *testing.T) { + policyIDOfValidDPI := "f3e521de628fa607ba11af0e9b53e2fb74ca0e6ea33622003d1f43dbae0ce41d" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, with multiple resources both being used, schemas accepted", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + + books: + permissions: + read: + expr: owner + write: + expr: owner + + relations: + owner: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfValidDPI, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "Users", // NOTE: "Users" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Books @policy( + id: "%s", + resource: "books" + ) { + name: String + } + `, + policyIDOfValidDPI, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Books") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "Books", // NOTE: "Books" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Tidy(), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/accept_same_resource_on_diff_schemas_test.go b/tests/integration/acp/schema/add_dpi/accept_same_resource_on_diff_schemas_test.go new file mode 100644 index 0000000000..89aab30d6f --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/accept_same_resource_on_diff_schemas_test.go @@ -0,0 +1,172 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" + schemaUtils "github.com/sourcenetwork/defradb/tests/integration/schema" +) + +func TestACP_AddDPISchema_UseSameResourceOnDifferentSchemas_AcceptSchemas(t *testing.T) { + policyIDOfValidDPI := "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" + sharedSameResourceName := "users" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, where one resource is specified on different schemas, schemas accepted", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type OldUsers @policy( + id: "%s", + resource: "%s" + ) { + name: String + age: Int + } + `, + policyIDOfValidDPI, + sharedSameResourceName, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "OldUsers") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "OldUsers", // NOTE: "OldUsers" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type NewUsers @policy( + id: "%s", + resource: "%s" + ) { + name: String + age: Int + } + `, + policyIDOfValidDPI, + sharedSameResourceName, + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "NewUsers") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": map[string]any{ + "name": "NewUsers", // NOTE: "NewUsers" MUST exist + "fields": schemaUtils.DefaultFields.Append( + schemaUtils.Field{ + "name": "name", + "type": map[string]any{ + "kind": "SCALAR", + "name": "String", + }, + }, + ).Append( + schemaUtils.Field{ + "name": "age", + "type": map[string]any{ + "kind": "SCALAR", + "name": "Int", + }, + }, + ).Tidy(), + }, + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/fixture.go b/tests/integration/acp/schema/add_dpi/fixture.go new file mode 100644 index 0000000000..0202cddeaa --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/fixture.go @@ -0,0 +1,18 @@ +// 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 test_acp_schema_add_dpi + +import ( + acpUtils "github.com/sourcenetwork/defradb/tests/integration/acp" +) + +var actor1Signature = acpUtils.Actor1Signature +var actor2Signature = acpUtils.Actor2Signature diff --git a/tests/integration/acp/schema/add_dpi/reject_empty_arg_on_schema_test.go b/tests/integration/acp/schema/add_dpi/reject_empty_arg_on_schema_test.go new file mode 100644 index 0000000000..ea4bd2476f --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/reject_empty_arg_on_schema_test.go @@ -0,0 +1,165 @@ +// 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 test_acp_schema_add_dpi + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddDPISchema_NoArgWasSpecifiedOnSchema_SchemaRejected(t *testing.T) { + policyIDOfValidDPI := "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, but no arg was specified on schema, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy { + name: String + age: Int + } + `, + ExpectedError: "missing policy arguments, must have both id and resource", + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_SpecifiedArgsAreEmptyOnSchema_SchemaRejected(t *testing.T) { + policyIDOfValidDPI := "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, specified args on schema are empty, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy(resource: "", id: "") { + name: String + age: Int + } + `, + ExpectedError: "missing policy arguments, must have both id and resource", + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/reject_invalid_arg_type_on_schema_test.go b/tests/integration/acp/schema/add_dpi/reject_invalid_arg_type_on_schema_test.go new file mode 100644 index 0000000000..a94930424e --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/reject_invalid_arg_type_on_schema_test.go @@ -0,0 +1,169 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddDPISchema_InvalidPolicyIDArgTypeWasSpecifiedOnSchema_SchemaRejected(t *testing.T) { + policyIDOfValidDPI := "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, but invalid policyID arg type was specified on schema, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy(id: 123 , resource: "users") { + name: String + age: Int + } + `, + ExpectedError: "policy directive with invalid id property", + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_InvalidResourceArgTypeWasSpecifiedOnSchema_SchemaRejected(t *testing.T) { + policyIDOfValidDPI := "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, but invalid resource arg type was specified on schema, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy(id: "%s" , resource: 123) { + name: String + age: Int + } + `, + policyIDOfValidDPI, + ), + + ExpectedError: "policy directive with invalid resource property", + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/reject_invalid_owner_read_perm_on_dpi_test.go b/tests/integration/acp/schema/add_dpi/reject_invalid_owner_read_perm_on_dpi_test.go new file mode 100644 index 0000000000..05dc4b8b9c --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/reject_invalid_owner_read_perm_on_dpi_test.go @@ -0,0 +1,438 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddDPISchema_OwnerMissingRequiredReadPermissionOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "782ffee730033ff01a3bdb05a3aa130f08c0914887378b0dfee314be6c3a8dd0" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, with owner missing required read permission, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: owner + read: + expr: r + + relations: + owner: + types: + - actor + r: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission must start with required relation. Permission: %s, Relation: %s", + "read", + "owner", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_OwnerMissingRequiredReadPermissionLabelOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "62d2d65d0304cb9a16bb4f07d1f48c7142911f73bc1db6ee54cdd2c6c7949c73" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, with owner missing required read permission label, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "resource is missing required permission on policy. PolicyID: %s, ResourceName: %s, Permission: %s", + policyIDOfInvalidDPI, + "users", + "read", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_OwnerSpecifiedIncorrectlyOnReadPermissionExprOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "f9fe33e8b2ee18a65d16bdc8017fe829ec13b0797330422639cd9dafac7b00f8" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, owner specified incorrectly on read permission expression, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: reader + owner + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission must start with required relation. Permission: %s, Relation: %s", + "read", + "owner", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_OwnerSpecifiedIncorrectlyOnReadPermissionNoSpaceExprOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "08cc6bed6b9695dd47b6bf1e934ff91975db598631a55c26db9ead1393a77588" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, owner specified incorrectly on read permission expression (no space), reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: reader+owner + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission must start with required relation. Permission: %s, Relation: %s", + "read", + "owner", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_MaliciousOwnerSpecifiedOnReadPermissionExprOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "fff5c6fc25fbc2a9e5a7251c19b1cb950889281d656e5aeb642ce7c16f181c9b" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, malicious owner specified on read permission expression, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: ownerBad + write: + expr: owner + + relations: + owner: + types: + - actor + ownerBad: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission has invalid character after relation. Permission: %s, Relation: %s, Character: %s", + "read", + "owner", + "B", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/reject_invalid_owner_read_perm_symbol_on_dpi_test.go b/tests/integration/acp/schema/add_dpi/reject_invalid_owner_read_perm_symbol_on_dpi_test.go new file mode 100644 index 0000000000..4c16683551 --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/reject_invalid_owner_read_perm_symbol_on_dpi_test.go @@ -0,0 +1,273 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddDPISchema_OwnerRelationWithDifferenceSetOpOnReadPermissionExprOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "c9bb1811862ded3a4a8a931054bd99ecabde3f41231c6aa2c50e1f1f5af2b5e8" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, owner relation with difference (-) set operation on read permission expression, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner - reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission has invalid character after relation. Permission: %s, Relation: %s, Character: %s", + "read", + "owner", + "-", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_OwnerRelationWithIntersectionSetOpOnReadPermissionExprOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "7bff1d8a967df4de99f8daaa2567c660eb6e7b2c554c9a49bf831230e5d9eba6" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, owner relation with intersection (&) set operation on read permission expression, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner & reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission has invalid character after relation. Permission: %s, Relation: %s, Character: %s", + "read", + "owner", + "&", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_OwnerRelationWithInvalidSetOpOnReadPermissionExprOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "cc2fab7c299e94e2bd9370708d26ca1262ff3b0d75f9a58d1086658cfec26c65" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, owner relation with invalid set operation on read permission expression, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner - owner + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission has invalid character after relation. Permission: %s, Relation: %s, Character: %s", + "read", + "owner", + "-", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/reject_invalid_owner_write_perm_on_dpi_test.go b/tests/integration/acp/schema/add_dpi/reject_invalid_owner_write_perm_on_dpi_test.go new file mode 100644 index 0000000000..def7044bf8 --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/reject_invalid_owner_write_perm_on_dpi_test.go @@ -0,0 +1,438 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddDPISchema_OwnerMissingRequiredWritePermissionOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "4256d2b54767cafd0e0a2b39a6faebf44bc99a7fc74ff5b51894f7accf2ef638" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, with owner missing required write permission, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + write: + expr: w + read: + expr: owner + + relations: + owner: + types: + - actor + w: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission must start with required relation. Permission: %s, Relation: %s", + "write", + "owner", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_OwnerMissingRequiredWritePermissionLabelOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "e8be944571cd6b52faa1e8b75fa339a9f60065b65d78ed126d037722e2512593" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, with owner missing required write permission label, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "resource is missing required permission on policy. PolicyID: %s, ResourceName: %s, Permission: %s", + policyIDOfInvalidDPI, + "users", + "write", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_OwnerSpecifiedIncorrectlyOnWritePermissionExprOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "34ff30cb9e80993e2b11f86f85c6daa7cd9bf25724e4d5ff0704518d7970d074" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, owner specified incorrectly on write permission expression, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: writer + owner + + relations: + owner: + types: + - actor + writer: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission must start with required relation. Permission: %s, Relation: %s", + "write", + "owner", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_OwnerSpecifiedIncorrectlyOnWritePermissionNoSpaceExprOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "2e9fc5805b0442e856e9893fea0f4759d333e442856a230ed741b88670e6426c" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, owner specified incorrectly on write permission expression (no space), reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: writer+owner + + relations: + owner: + types: + - actor + writer: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission must start with required relation. Permission: %s, Relation: %s", + "write", + "owner", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_MaliciousOwnerSpecifiedOnWritePermissionExprOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "3bcd650ac1e69d5efe6c930d05420231a0a69e6018d0f1015e0ecef9869d8dd5" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, malicious owner specified on write permission expression, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: ownerBad + + relations: + owner: + types: + - actor + ownerBad: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission has invalid character after relation. Permission: %s, Relation: %s, Character: %s", + "write", + "owner", + "B", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/reject_invalid_owner_write_perm_symbol_on_dpi_test.go b/tests/integration/acp/schema/add_dpi/reject_invalid_owner_write_perm_symbol_on_dpi_test.go new file mode 100644 index 0000000000..56712afd9f --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/reject_invalid_owner_write_perm_symbol_on_dpi_test.go @@ -0,0 +1,273 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddDPISchema_OwnerRelationWithDifferenceSetOpOnWritePermissionExprOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "2e14b379df6008ba577a11ac47d59c09eb0146afc5453e1ac0f40178ac3f5720" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, owner relation with difference (-) set operation on write permission expression, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner - reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission has invalid character after relation. Permission: %s, Relation: %s, Character: %s", + "write", + "owner", + "-", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_OwnerRelationWithIntersectionSetOpOnWritePermissionExprOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "143546c4da209d67466690bf749899c37cd956f64c128ea7cca0662688f832ac" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, owner relation with intersection (&) set operation on write permission expression, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner & reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission has invalid character after relation. Permission: %s, Relation: %s, Character: %s", + "write", + "owner", + "&", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_OwnerRelationWithInvalidSetOpOnWritePermissionExprOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "b9b4e941be904b0472ab6031628ce08ae4f87314e68972a6cfc114ed449820a4" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, owner relation with invalid set operation on write permission expression, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: a policy + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + write: + expr: owner - owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission has invalid character after relation. Permission: %s, Relation: %s, Character: %s", + "write", + "owner", + "-", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/reject_missing_dpi_test.go b/tests/integration/acp/schema/add_dpi/reject_missing_dpi_test.go new file mode 100644 index 0000000000..dc513f9827 --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/reject_missing_dpi_test.go @@ -0,0 +1,149 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddDPISchema_WhereNoPolicyWasAdded_SchemaRejected(t *testing.T) { + nonExistingPolicyID := "dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, but no policy was added, reject schema", + + Actions: []any{ + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + nonExistingPolicyID, + ), + + ExpectedError: "policyID specified does not exist with acp", + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_WhereAPolicyWasAddedButLinkedPolicyWasNotAdded_SchemaRejected(t *testing.T) { + policyAdded := "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" + incorrectPolicyID := "dfe202ffb4f0fe9b46157c313213a3839e08a6f0a7c3aba55e4724cb49ffde8a" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, but specify incorrect policy ID, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyAdded, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + incorrectPolicyID, + ), + + ExpectedError: "policyID specified does not exist with acp", + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/reject_missing_id_arg_on_schema_test.go b/tests/integration/acp/schema/add_dpi/reject_missing_id_arg_on_schema_test.go new file mode 100644 index 0000000000..5b72fcea96 --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/reject_missing_id_arg_on_schema_test.go @@ -0,0 +1,165 @@ +// 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 test_acp_schema_add_dpi + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddDPISchema_NoPolicyIDWasSpecifiedOnSchema_SchemaRejected(t *testing.T) { + policyIDOfValidDPI := "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, but no policyID was specified on schema, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy(resource: "users") { + name: String + age: Int + } + `, + ExpectedError: "policyID must not be empty", + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_SpecifiedPolicyIDArgIsEmptyOnSchema_SchemaRejected(t *testing.T) { + policyIDOfValidDPI := "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, specified policyID arg on schema is empty, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy(resource: "users", id: "") { + name: String + age: Int + } + `, + ExpectedError: "policyID must not be empty", + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/reject_missing_perms_on_dpi_test.go b/tests/integration/acp/schema/add_dpi/reject_missing_perms_on_dpi_test.go new file mode 100644 index 0000000000..1a9b1635bc --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/reject_missing_perms_on_dpi_test.go @@ -0,0 +1,97 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddDPISchema_MissingRequiredReadPermissionOnDPI_SchemaRejected(t *testing.T) { + policyIDOfInvalidDPI := "7eb7448daa631cfe33da3a149f5eea716026f54bf23ce1315c594259382c5c57" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, with missing required read permission, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A policy + + actor: + name: actor + + resources: + users: + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfInvalidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "users" + ) { + name: String + age: Int + } + `, + policyIDOfInvalidDPI, + ), + + ExpectedError: fmt.Sprintf( + "resource is missing required permission on policy. PolicyID: %s, ResourceName: %s, Permission: %s", + policyIDOfInvalidDPI, + "users", + "read", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/reject_missing_resource_arg_on_schema_test.go b/tests/integration/acp/schema/add_dpi/reject_missing_resource_arg_on_schema_test.go new file mode 100644 index 0000000000..a8296f80ca --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/reject_missing_resource_arg_on_schema_test.go @@ -0,0 +1,170 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddDPISchema_NoResourceWasSpecifiedOnSchema_SchemaRejected(t *testing.T) { + policyIDOfValidDPI := "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, but no resource was specified on schema, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy(id: "%s") { + name: String + age: Int + } + `, + policyIDOfValidDPI, + ), + ExpectedError: "resource name must not be empty", + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestACP_AddDPISchema_SpecifiedResourceArgIsEmptyOnSchema_SchemaRejected(t *testing.T) { + policyIDOfValidDPI := "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, specified resource arg on schema is empty, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy(id: "%s", resource: "") { + name: String + age: Int + } + `, + policyIDOfValidDPI, + ), + ExpectedError: "resource name must not be empty", + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/reject_missing_resource_on_dpi_test.go b/tests/integration/acp/schema/add_dpi/reject_missing_resource_on_dpi_test.go new file mode 100644 index 0000000000..edcbe9136a --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/reject_missing_resource_on_dpi_test.go @@ -0,0 +1,98 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddDPISchema_SpecifiedResourceDoesNotExistOnDPI_SchemaRejected(t *testing.T) { + policyIDOfValidDPI := "4f13c5084c3d0e1e5c5db702fceef84c3b6ab948949ca8e27fcaad3fb8bc39f4" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, but specified resource does not exist on DPI, reject schema", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + users: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "doesntExist" + ) { + name: String + age: Int + } + `, + policyIDOfValidDPI, + ), + + ExpectedError: "resource does not exist on the specified policy", + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/acp/schema/add_dpi/reject_mixed_resources_on_partial_dpi_test.go b/tests/integration/acp/schema/add_dpi/reject_mixed_resources_on_partial_dpi_test.go new file mode 100644 index 0000000000..b99c254497 --- /dev/null +++ b/tests/integration/acp/schema/add_dpi/reject_mixed_resources_on_partial_dpi_test.go @@ -0,0 +1,117 @@ +// 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 test_acp_schema_add_dpi + +import ( + "fmt" + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestACP_AddDPISchema_PartialValidDPIButUseInValidDPIResource_RejectSchema(t *testing.T) { + policyIDOfPartiallyValidDPI := "d5d411825b2d8fa5a550f1e34153b88b375ed9c9af19ce6d2ba1769e237a45d0" + + test := testUtils.TestCase{ + + Description: "Test acp, add dpi schema, has both valid & invalid resources, but use invalid resource, schema rejected", + + Actions: []any{ + + testUtils.AddPolicy{ + + Creator: actor1Signature, + + Policy: ` + description: A Partially Valid Defra Policy Interface (DPI) + + actor: + name: actor + + resources: + usersValid: + permissions: + read: + expr: owner + reader + write: + expr: owner + + relations: + owner: + types: + - actor + reader: + types: + - actor + + usersInvalid: + permissions: + read: + expr: reader - owner + write: + expr: reader + + relations: + owner: + types: + - actor + reader: + types: + - actor + `, + + ExpectedPolicyID: policyIDOfPartiallyValidDPI, + }, + + testUtils.SchemaUpdate{ + Schema: fmt.Sprintf(` + type Users @policy( + id: "%s", + resource: "usersInvalid" + ) { + name: String + age: Int + } + `, + policyIDOfPartiallyValidDPI, + ), + + ExpectedError: fmt.Sprintf( + "expr of required permission must start with required relation. Permission: %s, Relation: %s", + "read", + "owner", + ), + }, + + testUtils.IntrospectionRequest{ + Request: ` + query { + __type (name: "Users") { + name + fields { + name + type { + name + kind + } + } + } + } + `, + ExpectedData: map[string]any{ + "__type": nil, // NOTE: No "Users" should exist. + }, + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/collection/update/simple/with_doc_id_test.go b/tests/integration/collection/update/simple/with_doc_id_test.go index 6f990f7e70..7badb3b66c 100644 --- a/tests/integration/collection/update/simple/with_doc_id_test.go +++ b/tests/integration/collection/update/simple/with_doc_id_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/assert" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" testUtils "github.com/sourcenetwork/defradb/tests/integration/collection" ) @@ -41,9 +42,12 @@ func TestUpdateWithDocID(t *testing.T) { "Users": []func(c client.Collection) error{ func(c client.Collection) error { ctx := context.Background() - _, err := c.UpdateWithDocID(ctx, doc.ID(), `{ - name: "Eric" - }`) + _, err := c.UpdateWithDocID( + ctx, + acpIdentity.NoIdentity, + doc.ID(), + `{name: "Eric"}`, + ) return err }, }, @@ -58,7 +62,7 @@ func TestUpdateWithDocID(t *testing.T) { "Users": []func(c client.Collection) error{ func(c client.Collection) error { ctx := context.Background() - _, err := c.UpdateWithDocID(ctx, doc.ID(), `"name: Eric"`) + _, err := c.UpdateWithDocID(ctx, acpIdentity.NoIdentity, doc.ID(), `"name: Eric"`) return err }, }, @@ -73,18 +77,23 @@ func TestUpdateWithDocID(t *testing.T) { "Users": []func(c client.Collection) error{ func(c client.Collection) error { ctx := context.Background() - _, err := c.UpdateWithDocID(ctx, doc.ID(), `[ - { - "name": "Eric" - }, { - "name": "Sam" - } - ]`) + _, err := c.UpdateWithDocID( + ctx, + acpIdentity.NoIdentity, + doc.ID(), + `[ + { + "name": "Eric" + }, { + "name": "Sam" + } + ]`, + ) if err != nil { return err } - d, err := c.Get(ctx, doc.ID(), false) + d, err := c.Get(ctx, acpIdentity.NoIdentity, doc.ID(), false) if err != nil { return err } @@ -109,14 +118,17 @@ func TestUpdateWithDocID(t *testing.T) { "Users": []func(c client.Collection) error{ func(c client.Collection) error { ctx := context.Background() - _, err := c.UpdateWithDocID(ctx, doc.ID(), `{ - "name": "Eric" - }`) + _, err := c.UpdateWithDocID( + ctx, + acpIdentity.NoIdentity, + doc.ID(), + `{"name": "Eric"}`, + ) if err != nil { return err } - d, err := c.Get(ctx, doc.ID(), false) + d, err := c.Get(ctx, acpIdentity.NoIdentity, doc.ID(), false) if err != nil { return err } diff --git a/tests/integration/collection/update/simple/with_doc_ids_test.go b/tests/integration/collection/update/simple/with_doc_ids_test.go index a78fa2cc29..d1b38843a5 100644 --- a/tests/integration/collection/update/simple/with_doc_ids_test.go +++ b/tests/integration/collection/update/simple/with_doc_ids_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/assert" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" testUtils "github.com/sourcenetwork/defradb/tests/integration/collection" ) @@ -54,9 +55,12 @@ func TestUpdateWithDocIDs(t *testing.T) { "Users": []func(c client.Collection) error{ func(c client.Collection) error { ctx := context.Background() - _, err := c.UpdateWithDocIDs(ctx, []client.DocID{doc1.ID(), doc2.ID()}, `{ - name: "Eric" - }`) + _, err := c.UpdateWithDocIDs( + ctx, + acpIdentity.NoIdentity, + []client.DocID{doc1.ID(), doc2.ID()}, + `{name: "Eric"}`, + ) return err }, }, @@ -74,7 +78,12 @@ func TestUpdateWithDocIDs(t *testing.T) { "Users": []func(c client.Collection) error{ func(c client.Collection) error { ctx := context.Background() - _, err := c.UpdateWithDocIDs(ctx, []client.DocID{doc1.ID(), doc2.ID()}, `"name: Eric"`) + _, err := c.UpdateWithDocIDs( + ctx, + acpIdentity.NoIdentity, + []client.DocID{doc1.ID(), doc2.ID()}, + `"name: Eric"`, + ) return err }, }, @@ -92,18 +101,23 @@ func TestUpdateWithDocIDs(t *testing.T) { "Users": []func(c client.Collection) error{ func(c client.Collection) error { ctx := context.Background() - _, err := c.UpdateWithDocIDs(ctx, []client.DocID{doc1.ID(), doc2.ID()}, `[ - { - "name": "Eric" - }, { - "name": "Bob" - } - ]`) + _, err := c.UpdateWithDocIDs( + ctx, + acpIdentity.NoIdentity, + []client.DocID{doc1.ID(), doc2.ID()}, + `[ + { + "name": "Eric" + }, { + "name": "Bob" + } + ]`, + ) if err != nil { return err } - d, err := c.Get(ctx, doc1.ID(), false) + d, err := c.Get(ctx, acpIdentity.NoIdentity, doc1.ID(), false) if err != nil { return err } @@ -115,7 +129,7 @@ func TestUpdateWithDocIDs(t *testing.T) { assert.Equal(t, "John", name) - d2, err := c.Get(ctx, doc2.ID(), false) + d2, err := c.Get(ctx, acpIdentity.NoIdentity, doc2.ID(), false) if err != nil { return err } @@ -143,14 +157,17 @@ func TestUpdateWithDocIDs(t *testing.T) { "Users": []func(c client.Collection) error{ func(c client.Collection) error { ctx := context.Background() - _, err := c.UpdateWithDocIDs(ctx, []client.DocID{doc1.ID(), doc2.ID()}, `{ - "age": 40 - }`) + _, err := c.UpdateWithDocIDs( + ctx, + acpIdentity.NoIdentity, + []client.DocID{doc1.ID(), doc2.ID()}, + `{"age": 40}`, + ) if err != nil { return err } - d, err := c.Get(ctx, doc1.ID(), false) + d, err := c.Get(ctx, acpIdentity.NoIdentity, doc1.ID(), false) if err != nil { return err } @@ -162,7 +179,7 @@ func TestUpdateWithDocIDs(t *testing.T) { assert.Equal(t, int64(40), name) - d2, err := c.Get(ctx, doc2.ID(), false) + d2, err := c.Get(ctx, acpIdentity.NoIdentity, doc2.ID(), false) if err != nil { return err } diff --git a/tests/integration/collection/update/simple/with_filter_test.go b/tests/integration/collection/update/simple/with_filter_test.go index 1dc10b8de8..54f5b918ac 100644 --- a/tests/integration/collection/update/simple/with_filter_test.go +++ b/tests/integration/collection/update/simple/with_filter_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/assert" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" testUtils "github.com/sourcenetwork/defradb/tests/integration/collection" ) @@ -29,9 +30,12 @@ func TestUpdateWithInvalidFilterType(t *testing.T) { func(c client.Collection) error { ctx := context.Background() // test with an invalid filter type - _, err := c.UpdateWithFilter(ctx, t, `{ - "name": "Eric" - }`) + _, err := c.UpdateWithFilter( + ctx, + acpIdentity.NoIdentity, + t, + `{"name": "Eric"}`, + ) return err }, }, @@ -51,9 +55,12 @@ func TestUpdateWithEmptyFilter(t *testing.T) { func(c client.Collection) error { ctx := context.Background() // test with an empty filter - _, err := c.UpdateWithFilter(ctx, "", `{ - "name": "Eric" - }`) + _, err := c.UpdateWithFilter( + ctx, + acpIdentity.NoIdentity, + "", + `{"name": "Eric"}`, + ) return err }, }, @@ -87,9 +94,12 @@ func TestUpdateWithFilter(t *testing.T) { "Users": []func(c client.Collection) error{ func(c client.Collection) error { ctx := context.Background() - _, err := c.UpdateWithFilter(ctx, filter, `{ - name: "Eric" - }`) + _, err := c.UpdateWithFilter( + ctx, + acpIdentity.NoIdentity, + filter, + `{name: "Eric"}`, + ) return err }, }, @@ -104,7 +114,12 @@ func TestUpdateWithFilter(t *testing.T) { "Users": []func(c client.Collection) error{ func(c client.Collection) error { ctx := context.Background() - _, err := c.UpdateWithFilter(ctx, filter, `"name: Eric"`) + _, err := c.UpdateWithFilter( + ctx, + acpIdentity.NoIdentity, + filter, + `"name: Eric"`, + ) return err }, }, @@ -119,18 +134,23 @@ func TestUpdateWithFilter(t *testing.T) { "Users": []func(c client.Collection) error{ func(c client.Collection) error { ctx := context.Background() - _, err := c.UpdateWithFilter(ctx, filter, `[ - { - "name": "Eric" - }, { - "name": "Sam" - } - ]`) + _, err := c.UpdateWithFilter( + ctx, + acpIdentity.NoIdentity, + filter, + `[ + { + "name": "Eric" + }, { + "name": "Sam" + } + ]`, + ) if err != nil { return err } - d, err := c.Get(ctx, doc.ID(), false) + d, err := c.Get(ctx, acpIdentity.NoIdentity, doc.ID(), false) if err != nil { return err } @@ -155,14 +175,17 @@ func TestUpdateWithFilter(t *testing.T) { "Users": []func(c client.Collection) error{ func(c client.Collection) error { ctx := context.Background() - _, err := c.UpdateWithFilter(ctx, filter, `{ - "name": "Eric" - }`) + _, err := c.UpdateWithFilter( + ctx, + acpIdentity.NoIdentity, + filter, + `{"name": "Eric"}`, + ) if err != nil { return err } - d, err := c.Get(ctx, doc.ID(), false) + d, err := c.Get(ctx, acpIdentity.NoIdentity, doc.ID(), false) if err != nil { return err } diff --git a/tests/integration/collection/utils.go b/tests/integration/collection/utils.go index b8bf1cf46b..eb053be594 100644 --- a/tests/integration/collection/utils.go +++ b/tests/integration/collection/utils.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/assert" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/errors" testUtils "github.com/sourcenetwork/defradb/tests/integration" @@ -90,7 +91,7 @@ func setupDatabase( if assertError(t, testCase.Description, err, testCase.ExpectedError) { return } - err = col.Save(ctx, doc) + err = col.Save(ctx, acpIdentity.NoIdentity, doc) if assertError(t, testCase.Description, err, testCase.ExpectedError) { return } diff --git a/tests/integration/collection_description/updates/remove/policy_test.go b/tests/integration/collection_description/updates/remove/policy_test.go new file mode 100644 index 0000000000..8fdff5eb8e --- /dev/null +++ b/tests/integration/collection_description/updates/remove/policy_test.go @@ -0,0 +1,82 @@ +// 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 remove + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" + acpUtils "github.com/sourcenetwork/defradb/tests/integration/acp" +) + +func TestColDescrUpdateRemovePolicy_Errors(t *testing.T) { + test := testUtils.TestCase{ + Actions: []any{ + testUtils.AddPolicy{ + + Creator: acpUtils.Actor1Signature, + + 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", + }, + + testUtils.SchemaUpdate{ + Schema: ` + type Users @policy( + id: "53980e762616fcffbe76307995895e862f87ef3f21d509325d1dc772a770b001", + resource: "users" + ) { + name: String + age: Int + } + `, + }, + + testUtils.PatchCollection{ + Patch: ` + [ + { "op": "remove", "path": "/1/Policy" } + ] + `, + ExpectedError: "collection policy cannot be mutated. CollectionID: 1", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/collection_description/updates/replace/policy_test.go b/tests/integration/collection_description/updates/replace/policy_test.go new file mode 100644 index 0000000000..f71b652c59 --- /dev/null +++ b/tests/integration/collection_description/updates/replace/policy_test.go @@ -0,0 +1,83 @@ +// 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 replace + +import ( + "testing" + + testUtils "github.com/sourcenetwork/defradb/tests/integration" +) + +func TestColDescrUpdateReplacePolicy_Errors(t *testing.T) { + test := testUtils.TestCase{ + Actions: []any{ + testUtils.SchemaUpdate{ + Schema: ` + type Users {} + `, + }, + testUtils.PatchCollection{ + Patch: ` + [ + { "op": "replace", "path": "/1/Policy", "value": {} } + ] + `, + ExpectedError: "collection policy cannot be mutated. CollectionID: 1", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestColDescrUpdateReplacePolicyID_Errors(t *testing.T) { + test := testUtils.TestCase{ + Actions: []any{ + testUtils.SchemaUpdate{ + Schema: ` + type Users {} + `, + }, + testUtils.PatchCollection{ + Patch: ` + [ + { "op": "replace", "path": "/1/Policy", "value": {"ID": "dfe202ffb4f0fe9b46157c313213a383"} } + ] + `, + ExpectedError: "collection policy cannot be mutated. CollectionID: 1", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} + +func TestColDescrUpdateReplacePolicyResource_Errors(t *testing.T) { + test := testUtils.TestCase{ + Actions: []any{ + testUtils.SchemaUpdate{ + Schema: ` + type Users {} + `, + }, + testUtils.PatchCollection{ + Patch: ` + [ + { "op": "replace", "path": "/1/Policy", "value": {"ResourceName": "mutatingResource"} } + ] + `, + ExpectedError: "collection policy cannot be mutated. CollectionID: 1", + }, + }, + } + + testUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/db.go b/tests/integration/db.go index b103f656b3..73d8818934 100644 --- a/tests/integration/db.go +++ b/tests/integration/db.go @@ -76,6 +76,7 @@ func NewBadgerMemoryDB(ctx context.Context, dbopts ...db.Option) (client.DB, err if err != nil { return nil, err } + dbopts = append(dbopts, db.WithACPInMemory()) db, err := db.NewDB(ctx, rootstore, dbopts...) if err != nil { return nil, err @@ -84,6 +85,7 @@ func NewBadgerMemoryDB(ctx context.Context, dbopts ...db.Option) (client.DB, err } func NewInMemoryDB(ctx context.Context, dbopts ...db.Option) (client.DB, error) { + dbopts = append(dbopts, db.WithACPInMemory()) db, err := db.NewDB(ctx, memory.NewDatastore(ctx), dbopts...) if err != nil { return nil, err @@ -110,14 +112,18 @@ func NewBadgerFileDB(ctx context.Context, t testing.TB, dbopts ...db.Option) (cl opts := &badgerds.Options{ Options: badger.DefaultOptions(dbPath), } + rootstore, err := badgerds.NewDatastore(dbPath, opts) if err != nil { return nil, "", err } + + dbopts = append(dbopts, db.WithACP(dbPath)) db, err := db.NewDB(ctx, rootstore, dbopts...) if err != nil { return nil, "", err } + return db, dbPath, err } diff --git a/tests/integration/events/simple/with_create_test.go b/tests/integration/events/simple/with_create_test.go index ec5c174106..1e75687a4e 100644 --- a/tests/integration/events/simple/with_create_test.go +++ b/tests/integration/events/simple/with_create_test.go @@ -17,6 +17,7 @@ import ( "github.com/sourcenetwork/immutable" "github.com/stretchr/testify/assert" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" testUtils "github.com/sourcenetwork/defradb/tests/integration/events" ) @@ -48,11 +49,11 @@ func TestEventsSimpleWithCreate(t *testing.T) { CollectionCalls: map[string][]func(client.Collection){ "Users": []func(c client.Collection){ func(c client.Collection) { - err = c.Save(context.Background(), doc1) + err = c.Save(context.Background(), acpIdentity.NoIdentity, doc1) assert.Nil(t, err) }, func(c client.Collection) { - err = c.Save(context.Background(), doc2) + err = c.Save(context.Background(), acpIdentity.NoIdentity, doc2) assert.Nil(t, err) }, }, diff --git a/tests/integration/events/simple/with_create_txn_test.go b/tests/integration/events/simple/with_create_txn_test.go index c890792157..7ff1f838e7 100644 --- a/tests/integration/events/simple/with_create_txn_test.go +++ b/tests/integration/events/simple/with_create_txn_test.go @@ -17,6 +17,7 @@ import ( "github.com/sourcenetwork/immutable" "github.com/stretchr/testify/assert" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" testUtils "github.com/sourcenetwork/defradb/tests/integration/events" ) @@ -27,6 +28,7 @@ func TestEventsSimpleWithCreateWithTxnDiscarded(t *testing.T) { func(ctx context.Context, d client.DB) { r := d.ExecRequest( ctx, + acpIdentity.NoIdentity, `mutation { create_Users(input: {name: "John"}) { _docID @@ -42,6 +44,7 @@ func TestEventsSimpleWithCreateWithTxnDiscarded(t *testing.T) { assert.Nil(t, err) r := d.WithTxn(txn).ExecRequest( ctx, + acpIdentity.NoIdentity, `mutation { create_Users(input: {name: "Shahzad"}) { _docID diff --git a/tests/integration/events/simple/with_delete_test.go b/tests/integration/events/simple/with_delete_test.go index b02b2505e1..00f5a5977f 100644 --- a/tests/integration/events/simple/with_delete_test.go +++ b/tests/integration/events/simple/with_delete_test.go @@ -17,6 +17,7 @@ import ( "github.com/sourcenetwork/immutable" "github.com/stretchr/testify/assert" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" testUtils "github.com/sourcenetwork/defradb/tests/integration/events" ) @@ -37,11 +38,11 @@ func TestEventsSimpleWithDelete(t *testing.T) { CollectionCalls: map[string][]func(client.Collection){ "Users": []func(c client.Collection){ func(c client.Collection) { - err = c.Save(context.Background(), doc1) + err = c.Save(context.Background(), acpIdentity.NoIdentity, doc1) assert.Nil(t, err) }, func(c client.Collection) { - wasDeleted, err := c.Delete(context.Background(), doc1.ID()) + wasDeleted, err := c.Delete(context.Background(), acpIdentity.NoIdentity, doc1.ID()) assert.Nil(t, err) assert.True(t, wasDeleted) }, diff --git a/tests/integration/events/simple/with_update_test.go b/tests/integration/events/simple/with_update_test.go index 723421f91b..26c2e4363a 100644 --- a/tests/integration/events/simple/with_update_test.go +++ b/tests/integration/events/simple/with_update_test.go @@ -17,6 +17,7 @@ import ( "github.com/sourcenetwork/immutable" "github.com/stretchr/testify/assert" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" testUtils "github.com/sourcenetwork/defradb/tests/integration/events" ) @@ -48,17 +49,17 @@ func TestEventsSimpleWithUpdate(t *testing.T) { CollectionCalls: map[string][]func(client.Collection){ "Users": []func(c client.Collection){ func(c client.Collection) { - err = c.Save(context.Background(), doc1) + err = c.Save(context.Background(), acpIdentity.NoIdentity, doc1) assert.Nil(t, err) }, func(c client.Collection) { - err = c.Save(context.Background(), doc2) + err = c.Save(context.Background(), acpIdentity.NoIdentity, doc2) assert.Nil(t, err) }, func(c client.Collection) { // Update John doc1.Set("name", "Johnnnnn") - err = c.Save(context.Background(), doc1) + err = c.Save(context.Background(), acpIdentity.NoIdentity, doc1) assert.Nil(t, err) }, }, diff --git a/tests/integration/events/utils.go b/tests/integration/events/utils.go index d2bf418294..51299192e8 100644 --- a/tests/integration/events/utils.go +++ b/tests/integration/events/utils.go @@ -19,6 +19,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/db" testUtils "github.com/sourcenetwork/defradb/tests/integration" @@ -152,7 +153,7 @@ func setupDatabase( doc, err := client.NewDocFromJSON([]byte(docStr), col.Schema()) require.NoError(t, err) - err = col.Save(ctx, doc) + err = col.Save(ctx, acpIdentity.NoIdentity, doc) require.NoError(t, err) } } diff --git a/tests/integration/explain.go b/tests/integration/explain.go index eb44744e57..c087401588 100644 --- a/tests/integration/explain.go +++ b/tests/integration/explain.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" ) @@ -78,6 +79,9 @@ type ExplainRequest struct { // NodeID is the node ID (index) of the node in which to explain. NodeID immutable.Option[int] + // The identity of this request. + Identity string + // Has to be a valid explain request type (one of: 'simple', 'debug', 'execute', 'predict'). Request string @@ -127,7 +131,11 @@ func executeExplainRequest( } for _, node := range getNodes(action.NodeID, s.nodes) { - result := node.ExecRequest(s.ctx, action.Request) + result := node.ExecRequest( + s.ctx, + acpIdentity.NewIdentity(action.Identity), + action.Request, + ) assertExplainRequestResults(s, &result.GQL, action) } } diff --git a/tests/integration/mutation/create/field_kinds/one_to_one/with_alias_test.go b/tests/integration/mutation/create/field_kinds/one_to_one/with_alias_test.go index 18d4a2e13c..16da55ce78 100644 --- a/tests/integration/mutation/create/field_kinds/one_to_one/with_alias_test.go +++ b/tests/integration/mutation/create/field_kinds/one_to_one/with_alias_test.go @@ -82,7 +82,7 @@ func TestMutationCreateOneToOne_UseAliasWithNonExistingRelationSecondarySide_Err "name": "Painted House", "author": "bae-fd541c25-229e-5280-b44b-e5c2af3e374d" }`, - ExpectedError: "no document for the given ID exists", + ExpectedError: "document not found or not authorized to access", }, }, } diff --git a/tests/integration/mutation/create/field_kinds/one_to_one/with_simple_test.go b/tests/integration/mutation/create/field_kinds/one_to_one/with_simple_test.go index 30545d6e7c..c693b05187 100644 --- a/tests/integration/mutation/create/field_kinds/one_to_one/with_simple_test.go +++ b/tests/integration/mutation/create/field_kinds/one_to_one/with_simple_test.go @@ -82,7 +82,7 @@ func TestMutationCreateOneToOne_NonExistingRelationSecondarySide_Error(t *testin "name": "Painted House", "author_id": "bae-fd541c25-229e-5280-b44b-e5c2af3e374d" }`, - ExpectedError: "no document for the given ID exists", + ExpectedError: "document not found or not authorized to access", }, }, } diff --git a/tests/integration/mutation/update/field_kinds/one_to_one/with_alias_test.go b/tests/integration/mutation/update/field_kinds/one_to_one/with_alias_test.go index fdb8928964..39e132a6c6 100644 --- a/tests/integration/mutation/update/field_kinds/one_to_one/with_alias_test.go +++ b/tests/integration/mutation/update/field_kinds/one_to_one/with_alias_test.go @@ -184,7 +184,7 @@ func TestMutationUpdateOneToOne_InvalidAliasRelationNameToLinkFromSecondarySide_ }`, invalidAuthorID, ), - ExpectedError: "no document for the given ID exists", + ExpectedError: "document not found or not authorized to access", }, }, } diff --git a/tests/integration/mutation/update/field_kinds/one_to_one/with_simple_test.go b/tests/integration/mutation/update/field_kinds/one_to_one/with_simple_test.go index 6d38a9914d..0c05734204 100644 --- a/tests/integration/mutation/update/field_kinds/one_to_one/with_simple_test.go +++ b/tests/integration/mutation/update/field_kinds/one_to_one/with_simple_test.go @@ -368,7 +368,7 @@ func TestMutationUpdateOneToOne_InvalidRelationIDToLinkFromSecondarySide_Error(t }`, invalidAuthorID, ), - ExpectedError: "no document for the given ID exists", + ExpectedError: "document not found or not authorized to access", }, }, } diff --git a/tests/integration/net/order/tcp_test.go b/tests/integration/net/order/tcp_test.go index f80701c64c..256db2f442 100644 --- a/tests/integration/net/order/tcp_test.go +++ b/tests/integration/net/order/tcp_test.go @@ -17,15 +17,15 @@ import ( "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/net" - testutils "github.com/sourcenetwork/defradb/tests/integration" + testUtils "github.com/sourcenetwork/defradb/tests/integration" ) // TestP2PWithSingleDocumentUpdatePerNode tests document syncing between two nodes with a single update per node func TestP2PWithSingleDocumentUpdatePerNode(t *testing.T) { test := P2PTestCase{ NodeConfig: [][]net.NodeOpt{ - testutils.RandomNetworkingConfig()(), - testutils.RandomNetworkingConfig()(), + testUtils.RandomNetworkingConfig()(), + testUtils.RandomNetworkingConfig()(), }, NodePeers: map[int][]int{ 1: { @@ -75,8 +75,8 @@ func TestP2PWithSingleDocumentUpdatePerNode(t *testing.T) { func TestP2PWithMultipleDocumentUpdatesPerNode(t *testing.T) { test := P2PTestCase{ NodeConfig: [][]net.NodeOpt{ - testutils.RandomNetworkingConfig()(), - testutils.RandomNetworkingConfig()(), + testUtils.RandomNetworkingConfig()(), + testUtils.RandomNetworkingConfig()(), }, NodePeers: map[int][]int{ 1: { @@ -136,7 +136,7 @@ func TestP2PWithMultipleDocumentUpdatesPerNode(t *testing.T) { // TestP2FullPReplicator tests document syncing between a node and a replicator. func TestP2FullPReplicator(t *testing.T) { - colDefMap, err := testutils.ParseSDL(userCollectionGQLSchema) + colDefMap, err := testUtils.ParseSDL(userCollectionGQLSchema) require.NoError(t, err) doc, err := client.NewDocFromJSON([]byte(`{ "Name": "John", @@ -146,8 +146,8 @@ func TestP2FullPReplicator(t *testing.T) { test := P2PTestCase{ NodeConfig: [][]net.NodeOpt{ - testutils.RandomNetworkingConfig()(), - testutils.RandomNetworkingConfig()(), + testUtils.RandomNetworkingConfig()(), + testUtils.RandomNetworkingConfig()(), }, NodeReplicators: map[int][]int{ 0: { diff --git a/tests/integration/net/order/utils.go b/tests/integration/net/order/utils.go index 58857e5b94..2727690281 100644 --- a/tests/integration/net/order/utils.go +++ b/tests/integration/net/order/utils.go @@ -19,6 +19,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/sourcenetwork/immutable" + + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" coreDB "github.com/sourcenetwork/defradb/db" "github.com/sourcenetwork/defradb/errors" @@ -47,6 +50,11 @@ const ( type P2PTestCase struct { Query string + + // The identity for all requests. + // TODO-ACP: https://github.com/sourcenetwork/defradb/issues/2366 - Improve in ACP <> P2P implementation + Identity string + // Configuration parameters for each peer NodeConfig [][]net.NodeOpt @@ -69,6 +77,7 @@ type P2PTestCase struct { func setupDefraNode( t *testing.T, + identity immutable.Option[string], opts []net.NodeOpt, peers []string, seeds []string, @@ -88,7 +97,7 @@ func setupDefraNode( // seed the database with a set of documents docIDs := []client.DocID{} for _, document := range seeds { - docID, err := seedDocument(ctx, db, document) + docID, err := seedDocument(ctx, identity, db, document) require.NoError(t, err) docIDs = append(docIDs, docID) } @@ -125,7 +134,12 @@ func seedSchema(ctx context.Context, db client.DB) error { return err } -func seedDocument(ctx context.Context, db client.DB, document string) (client.DocID, error) { +func seedDocument( + ctx context.Context, + identity immutable.Option[string], + db client.DB, + document string, +) (client.DocID, error) { col, err := db.GetCollectionByName(ctx, userCollection) if err != nil { return client.DocID{}, err @@ -136,7 +150,7 @@ func seedDocument(ctx context.Context, db client.DB, document string) (client.Do return client.DocID{}, err } - err = col.Save(ctx, doc) + err = col.Save(ctx, identity, doc) if err != nil { return client.DocID{}, err } @@ -144,22 +158,33 @@ func seedDocument(ctx context.Context, db client.DB, document string) (client.Do return doc.ID(), nil } -func saveDocument(ctx context.Context, db client.DB, document *client.Document) error { +func saveDocument( + ctx context.Context, + identity immutable.Option[string], + db client.DB, + document *client.Document, +) error { col, err := db.GetCollectionByName(ctx, userCollection) if err != nil { return err } - return col.Save(ctx, document) + return col.Save(ctx, identity, document) } -func updateDocument(ctx context.Context, db client.DB, docID client.DocID, update string) error { +func updateDocument( + ctx context.Context, + identity immutable.Option[string], + db client.DB, + docID client.DocID, + update string, +) error { col, err := db.GetCollectionByName(ctx, userCollection) if err != nil { return err } - doc, err := getDocument(ctx, db, docID) + doc, err := getDocument(ctx, identity, db, docID) if err != nil { return err } @@ -168,16 +193,21 @@ func updateDocument(ctx context.Context, db client.DB, docID client.DocID, updat return err } - return col.Save(ctx, doc) + return col.Save(ctx, identity, doc) } -func getDocument(ctx context.Context, db client.DB, docID client.DocID) (*client.Document, error) { +func getDocument( + ctx context.Context, + identity immutable.Option[string], + db client.DB, + docID client.DocID, +) (*client.Document, error) { col, err := db.GetCollectionByName(ctx, userCollection) if err != nil { return nil, err } - doc, err := col.Get(ctx, docID, false) + doc, err := col.Get(ctx, identity, docID, false) if err != nil { return nil, err } @@ -206,7 +236,13 @@ func executeTestCase(t *testing.T, test P2PTestCase) { ) } } - n, d, err := setupDefraNode(t, cfg, peerAddresses, test.SeedDocuments) + n, d, err := setupDefraNode( + t, + acpIdentity.NewIdentity(test.Identity), + cfg, + peerAddresses, + test.SeedDocuments, + ) require.NoError(t, err) if i == 0 { @@ -234,6 +270,8 @@ func executeTestCase(t *testing.T, test P2PTestCase) { } } + identity := acpIdentity.NewIdentity(test.Identity) + // update and sync peers for n, updateMap := range test.Updates { if n >= len(nodes) { @@ -244,7 +282,13 @@ func executeTestCase(t *testing.T, test P2PTestCase) { for d, updates := range updateMap { for _, update := range updates { log.InfoContext(ctx, fmt.Sprintf("Updating node %d with update %d", n, d)) - err := updateDocument(ctx, nodes[n].DB, docIDs[d], update) + err := updateDocument( + ctx, + identity, + nodes[n].DB, + docIDs[d], + update, + ) require.NoError(t, err) // wait for peers to sync @@ -272,7 +316,12 @@ func executeTestCase(t *testing.T, test P2PTestCase) { for d, results := range resultsMap { for field, result := range results { - doc, err := getDocument(ctx, nodes[n2].DB, docIDs[d]) + doc, err := getDocument( + ctx, + identity, + nodes[n2].DB, + docIDs[d], + ) require.NoError(t, err) val, err := doc.Get(field) @@ -304,7 +353,12 @@ func executeTestCase(t *testing.T, test P2PTestCase) { if len(test.DocumentsToReplicate) > 0 { for n, reps := range test.NodeReplicators { for _, doc := range test.DocumentsToReplicate { - err := saveDocument(ctx, nodes[n].DB, doc) + err := saveDocument( + ctx, + identity, + nodes[n].DB, + doc, + ) require.NoError(t, err) } for _, rep := range reps { @@ -318,7 +372,12 @@ func executeTestCase(t *testing.T, test P2PTestCase) { d, err := client.NewDocIDFromString(docID) require.NoError(t, err) - doc, err := getDocument(ctx, nodes[rep].DB, d) + doc, err := getDocument( + ctx, + identity, + nodes[rep].DB, + d, + ) require.NoError(t, err) val, err := doc.Get(field) diff --git a/tests/integration/p2p.go b/tests/integration/p2p.go index 7c6a919373..0cace429ae 100644 --- a/tests/integration/p2p.go +++ b/tests/integration/p2p.go @@ -55,6 +55,12 @@ type ConfigureReplicator struct { // TargetNodeID is the node ID (index) of the node to which data should be replicated. TargetNodeID int + + // Any error expected from the action. Optional. + // + // String can be a partial, and the test will pass if an error is returned that + // contains this string. + ExpectedError string } // DeleteReplicator deletes a directional replicator relationship between two nodes. @@ -307,8 +313,12 @@ func configureReplicator( err := sourceNode.SetReplicator(s.ctx, client.Replicator{ Info: targetNode.PeerInfo(), }) - require.NoError(s.t, err) - setupReplicatorWaitSync(s, 0, cfg, sourceNode, targetNode) + + expectedErrorRaised := AssertError(s.t, s.testCase.Description, err, cfg.ExpectedError) + assertExpectedErrorRaised(s.t, s.testCase.Description, cfg.ExpectedError, expectedErrorRaised) + if err == nil { + setupReplicatorWaitSync(s, 0, cfg, sourceNode, targetNode) + } } func deleteReplicator( diff --git a/tests/integration/state.go b/tests/integration/state.go index 25a248413b..49030c82a6 100644 --- a/tests/integration/state.go +++ b/tests/integration/state.go @@ -112,5 +112,6 @@ func newState( collectionNames: collectionNames, documents: [][]*client.Document{}, indexes: [][][]client.IndexDescription{}, + isBench: false, } } diff --git a/tests/integration/test_case.go b/tests/integration/test_case.go index 7cda289319..8d9315e4fa 100644 --- a/tests/integration/test_case.go +++ b/tests/integration/test_case.go @@ -205,6 +205,14 @@ type CreateDoc struct { // If a value is not provided the document will be created in all nodes. NodeID immutable.Option[int] + // The identity of this request. Optional. + // + // If an Identity is not provided the created document(s) will be public. + // + // If an Identity is provided and the collection has a policy, then the + // created document(s) will be owned by this Identity. + Identity string + // The collection in which this document should be created. CollectionID int @@ -226,6 +234,14 @@ type DeleteDoc struct { // If a value is not provided the document will be created in all nodes. NodeID immutable.Option[int] + // The identity of this request. Optional. + // + // If an Identity is not provided then can only delete public document(s). + // + // If an Identity is provided and the collection has a policy, then + // can also delete private document(s) that are owned by this Identity. + Identity string + // The collection in which this document should be deleted. CollectionID int @@ -251,6 +267,14 @@ type UpdateDoc struct { // If a value is not provided the update will be applied to all nodes. NodeID immutable.Option[int] + // The identity of this request. Optional. + // + // If an Identity is not provided then can only update public document(s). + // + // If an Identity is provided and the collection has a policy, then + // can also update private document(s) that are owned by this Identity. + Identity string + // The collection in which this document exists. CollectionID int @@ -397,6 +421,14 @@ type Request struct { // in which case the expected results must all match across all nodes. NodeID immutable.Option[int] + // The identity of this request. Optional. + // + // If an Identity is not provided then can only operate over public document(s). + // + // If an Identity is provided and the collection has a policy, then can + // operate over private document(s) that are owned by this Identity. + Identity string + // Used to identify the transaction for this to run against. Optional. TransactionID immutable.Option[int] diff --git a/tests/integration/utils2.go b/tests/integration/utils2.go index 930b429119..fe79b18106 100644 --- a/tests/integration/utils2.go +++ b/tests/integration/utils2.go @@ -27,6 +27,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + acpIdentity "github.com/sourcenetwork/defradb/acp/identity" "github.com/sourcenetwork/defradb/client" "github.com/sourcenetwork/defradb/datastore" badgerds "github.com/sourcenetwork/defradb/datastore/badger/v4" @@ -278,6 +279,9 @@ func performAction( case ConfigureMigration: configureMigration(s, action) + case AddPolicy: + addPolicyACP(s, action) + case CreateDoc: createDoc(s, action) @@ -834,7 +838,12 @@ func refreshDocuments( // The document may have been mutated by other actions, so to be sure we have the latest // version without having to worry about the individual update mechanics we fetch it. - doc, err = collection.Get(s.ctx, doc.ID(), false) + doc, err = collection.Get( + s.ctx, + acpIdentity.NewIdentity(action.Identity), + doc.ID(), + false, + ) if err != nil { // If an err has been returned, ignore it - it may be expected and if not // the test will fail later anyway @@ -1182,7 +1191,11 @@ func createDocViaColSave( return nil, err } - return doc, collections[action.CollectionID].Save(s.ctx, doc) + return doc, collections[action.CollectionID].Save( + s.ctx, + acpIdentity.NewIdentity(action.Identity), + doc, + ) } func createDocViaColCreate( @@ -1197,7 +1210,11 @@ func createDocViaColCreate( return nil, err } - return doc, collections[action.CollectionID].Create(s.ctx, doc) + return doc, collections[action.CollectionID].Create( + s.ctx, + acpIdentity.NewIdentity(action.Identity), + doc, + ) } func createDocViaGQL( @@ -1223,7 +1240,12 @@ func createDocViaGQL( db := getStore(s, node, immutable.None[int](), action.ExpectedError) - result := db.ExecRequest(s.ctx, request) + identity := acpIdentity.NewIdentity(action.Identity) + result := db.ExecRequest( + s.ctx, + identity, + request, + ) if len(result.GQL.Errors) > 0 { return nil, result.GQL.Errors[0] } @@ -1237,7 +1259,7 @@ func createDocViaGQL( docID, err := client.NewDocIDFromString(docIDString) require.NoError(s.t, err) - doc, err := collection.Get(s.ctx, docID, false) + doc, err := collection.Get(s.ctx, identity, docID, false) require.NoError(s.t, err) return doc, nil @@ -1258,7 +1280,11 @@ func deleteDoc( actionNodes, nodeID, func() error { - _, err := collections[action.CollectionID].DeleteWithDocID(s.ctx, doc.ID()) + _, err := collections[action.CollectionID].DeleteWithDocID( + s.ctx, + acpIdentity.NewIdentity(action.Identity), + doc.ID(), + ) return err }, ) @@ -1308,7 +1334,13 @@ func updateDocViaColSave( ) error { cachedDoc := s.documents[action.CollectionID][action.DocID] - doc, err := collections[action.CollectionID].Get(s.ctx, cachedDoc.ID(), true) + identity := acpIdentity.NewIdentity(action.Identity) + doc, err := collections[action.CollectionID].Get( + s.ctx, + identity, + cachedDoc.ID(), + true, + ) if err != nil { return err } @@ -1320,7 +1352,11 @@ func updateDocViaColSave( s.documents[action.CollectionID][action.DocID] = doc - return collections[action.CollectionID].Save(s.ctx, doc) + return collections[action.CollectionID].Save( + s.ctx, + identity, + doc, + ) } func updateDocViaColUpdate( @@ -1331,7 +1367,13 @@ func updateDocViaColUpdate( ) error { cachedDoc := s.documents[action.CollectionID][action.DocID] - doc, err := collections[action.CollectionID].Get(s.ctx, cachedDoc.ID(), true) + identity := acpIdentity.NewIdentity(action.Identity) + doc, err := collections[action.CollectionID].Get( + s.ctx, + identity, + cachedDoc.ID(), + true, + ) if err != nil { return err } @@ -1343,7 +1385,11 @@ func updateDocViaColUpdate( s.documents[action.CollectionID][action.DocID] = doc - return collections[action.CollectionID].Update(s.ctx, doc) + return collections[action.CollectionID].Update( + s.ctx, + identity, + doc, + ) } func updateDocViaGQL( @@ -1371,7 +1417,11 @@ func updateDocViaGQL( db := getStore(s, node, immutable.None[int](), action.ExpectedError) - result := db.ExecRequest(s.ctx, request) + result := db.ExecRequest( + s.ctx, + acpIdentity.NewIdentity(action.Identity), + request, + ) if len(result.GQL.Errors) > 0 { return result.GQL.Errors[0] } @@ -1587,7 +1637,11 @@ func executeRequest( var expectedErrorRaised bool for nodeID, node := range getNodes(action.NodeID, s.nodes) { db := getStore(s, node, action.TransactionID, action.ExpectedError) - result := db.ExecRequest(s.ctx, action.Request) + result := db.ExecRequest( + s.ctx, + acpIdentity.NewIdentity(action.Identity), + action.Request, + ) anyOfByFieldKey := map[docFieldKey][]any{} expectedErrorRaised = assertRequestResults( @@ -1619,7 +1673,11 @@ func executeSubscriptionRequest( subscriptionAssert := make(chan func()) for _, node := range getNodes(action.NodeID, s.nodes) { - result := node.ExecRequest(s.ctx, action.Request) + result := node.ExecRequest( + s.ctx, + acpIdentity.NoIdentity, // No Identity for subscription request. + action.Request, + ) if AssertErrors(s.t, s.testCase.Description, result.GQL.Errors, action.ExpectedError) { return } @@ -1691,7 +1749,8 @@ func AssertError(t *testing.T, description string, err error, expectedError stri return false } else { if !strings.Contains(err.Error(), expectedError) { - assert.ErrorIs(t, err, errors.New(expectedError)) + // Must be require instead of assert, otherwise will show a fake "error not raised". + require.ErrorIs(t, err, errors.New(expectedError)) return false } return true @@ -1800,7 +1859,11 @@ func assertIntrospectionResults( action IntrospectionRequest, ) bool { for _, node := range getNodes(action.NodeID, s.nodes) { - result := node.ExecRequest(s.ctx, action.Request) + result := node.ExecRequest( + s.ctx, + acpIdentity.NoIdentity, // No Identity for introspection requests. + action.Request, + ) if AssertErrors(s.t, s.testCase.Description, result.GQL.Errors, action.ExpectedError) { return true @@ -1831,7 +1894,11 @@ func assertClientIntrospectionResults( action ClientIntrospectionRequest, ) bool { for _, node := range getNodes(action.NodeID, s.nodes) { - result := node.ExecRequest(s.ctx, action.Request) + result := node.ExecRequest( + s.ctx, + acpIdentity.NoIdentity, // No identity for client introspection requests. + action.Request, + ) if AssertErrors(s.t, s.testCase.Description, result.GQL.Errors, action.ExpectedError) { return true