diff --git a/cli/request.go b/cli/request.go index 795046ece9..b6ec8e05ce 100644 --- a/cli/request.go +++ b/cli/request.go @@ -26,21 +26,11 @@ const ( func MakeRequestCommand() *cobra.Command { var filePath string - var shouldEncrypt bool var cmd = &cobra.Command{ Use: "query [-i --identity] [request]", Short: "Send a DefraDB GraphQL query request", Long: `Send a DefraDB GraphQL query request to the database. -Options: - -i, --identity - Marks the document as private and set the identity as the owner. The access to the document - and permissions are controlled by ACP (Access Control Policy). - - -e, --encrypt - Encrypt flag specified if the document needs to be encrypted. If set, DefraDB will generate a - symmetric key for encryption using AES-GCM. - A query request can be sent as a single argument. Example command: defradb client query 'query { ... }' @@ -81,7 +71,6 @@ To learn more about the DefraDB GraphQL Query Language, refer to https://docs.so } store := mustGetContextStore(cmd) - setContextDocEncryption(cmd, shouldEncrypt, nil) result := store.ExecRequest(cmd.Context(), request) var errors []string @@ -100,8 +89,6 @@ To learn more about the DefraDB GraphQL Query Language, refer to https://docs.so }, } - cmd.PersistentFlags().BoolVarP(&shouldEncrypt, "encrypt", "e", false, - "Flag to enable encryption of the document") cmd.Flags().StringVarP(&filePath, "file", "f", "", "File containing the query request") return cmd } diff --git a/client/request/consts.go b/client/request/consts.go index 86bd887c92..cba609b788 100644 --- a/client/request/consts.go +++ b/client/request/consts.go @@ -26,6 +26,8 @@ const ( FieldIDName = "fieldId" ShowDeleted = "showDeleted" + EncryptArgName = "encrypt" + FilterClause = "filter" GroupByClause = "groupBy" LimitClause = "limit" diff --git a/client/request/mutation.go b/client/request/mutation.go index f78ed87ea0..70d0bed1d9 100644 --- a/client/request/mutation.go +++ b/client/request/mutation.go @@ -42,11 +42,14 @@ type ObjectMutation struct { // This is ignored for [DeleteObjects] mutations. Input map[string]any - // Inputs is the array of json representations of the fieldName-value pairs of document + // Inputs is the array of json representations of the fieldName-value pairs of document // properties to mutate. // // This is ignored for [DeleteObjects] mutations. Inputs []map[string]any + + // Encrypt is a boolean flag that indicates whether the input data should be encrypted. + Encrypt bool } // ToSelect returns a basic Select object, with the same Name, Alias, and Fields as diff --git a/docs/website/references/cli/defradb_client_query.md b/docs/website/references/cli/defradb_client_query.md index 7c9b8e72fc..ec868456b1 100644 --- a/docs/website/references/cli/defradb_client_query.md +++ b/docs/website/references/cli/defradb_client_query.md @@ -6,15 +6,6 @@ Send a DefraDB GraphQL query request Send a DefraDB GraphQL query request to the database. -Options: - -i, --identity - Marks the document as private and set the identity as the owner. The access to the document - and permissions are controlled by ACP (Access Control Policy). - - -e, --encrypt - Encrypt flag specified if the document needs to be encrypted. If set, DefraDB will generate a - symmetric key for encryption using AES-GCM. - A query request can be sent as a single argument. Example command: defradb client query 'query { ... }' @@ -39,7 +30,6 @@ defradb client query [-i --identity] [request] [flags] ### Options ``` - -e, --encrypt Flag to enable encryption of the document -f, --file string File containing the query request -h, --help help for query ``` diff --git a/http/client_collection.go b/http/client_collection.go index 4190d28963..b1761366a7 100644 --- a/http/client_collection.go +++ b/http/client_collection.go @@ -79,10 +79,7 @@ func (c *Collection) Create( return err } - encConf := encryption.GetContextConfig(ctx) - if encConf.HasValue() && encConf.Value().IsEncrypted { - req.Header.Set(DocEncryptionHeader, "1") - } + setDocEncryptionHeaderIfNeeded(ctx, req) _, err = c.http.request(req) if err != nil { @@ -120,10 +117,7 @@ func (c *Collection) CreateMany( return err } - encConf := encryption.GetContextConfig(ctx) - if encConf.HasValue() && encConf.Value().IsEncrypted { - req.Header.Set(DocEncryptionHeader, "1") - } + setDocEncryptionHeaderIfNeeded(ctx, req) _, err = c.http.request(req) if err != nil { @@ -136,6 +130,13 @@ func (c *Collection) CreateMany( return nil } +func setDocEncryptionHeaderIfNeeded(ctx context.Context, req *http.Request) { + encConf := encryption.GetContextConfig(ctx) + if encConf.HasValue() && encConf.Value().IsEncrypted { + req.Header.Set(DocEncryptionHeader, "1") + } +} + func (c *Collection) Update( ctx context.Context, doc *client.Document, diff --git a/http/handler_store.go b/http/handler_store.go index df2136db87..de534a8c1d 100644 --- a/http/handler_store.go +++ b/http/handler_store.go @@ -22,7 +22,6 @@ import ( "github.com/sourcenetwork/immutable" "github.com/sourcenetwork/defradb/client" - "github.com/sourcenetwork/defradb/internal/encryption" ) type storeHandler struct{} @@ -313,12 +312,7 @@ func (s *storeHandler) ExecRequest(rw http.ResponseWriter, req *http.Request) { return } - ctx := req.Context() - if req.Header.Get(DocEncryptionHeader) == "1" { - ctx = encryption.SetContextConfig(ctx, encryption.DocEncConfig{IsEncrypted: true}) - } - - result := store.ExecRequest(ctx, request.Query) + result := store.ExecRequest(req.Context(), request.Query) if result.Subscription == nil { responseJSON(rw, http.StatusOK, GraphQLResponse{result.GQL.Data, result.GQL.Errors}) diff --git a/internal/planner/create.go b/internal/planner/create.go index 8688b9fc6b..2b0a47332d 100644 --- a/internal/planner/create.go +++ b/internal/planner/create.go @@ -15,6 +15,7 @@ import ( "github.com/sourcenetwork/defradb/client/request" "github.com/sourcenetwork/defradb/internal/core" "github.com/sourcenetwork/defradb/internal/db/base" + "github.com/sourcenetwork/defradb/internal/encryption" "github.com/sourcenetwork/defradb/internal/planner/mapper" ) @@ -160,6 +161,10 @@ func (p *Planner) CreateDocs(parsed *mapper.Mutation) (planNode, error) { create.input = []map[string]any{parsed.Input} } + if parsed.Encrypt { + p.ctx = encryption.SetContextConfig(p.ctx, encryption.DocEncConfig{IsEncrypted: true}) + } + // get collection col, err := p.db.GetCollectionByName(p.ctx, parsed.Name) if err != nil { diff --git a/internal/planner/mapper/mapper.go b/internal/planner/mapper/mapper.go index f659f346a9..858ef0e0ae 100644 --- a/internal/planner/mapper/mapper.go +++ b/internal/planner/mapper/mapper.go @@ -1165,10 +1165,11 @@ func ToMutation(ctx context.Context, store client.Store, mutationRequest *reques } return &Mutation{ - Select: *underlyingSelect, - Type: MutationType(mutationRequest.Type), - Input: mutationRequest.Input, - Inputs: mutationRequest.Inputs, + Select: *underlyingSelect, + Type: MutationType(mutationRequest.Type), + Input: mutationRequest.Input, + Inputs: mutationRequest.Inputs, + Encrypt: mutationRequest.Encrypt, }, nil } diff --git a/internal/planner/mapper/mutation.go b/internal/planner/mapper/mutation.go index faf2f5209b..251d01298f 100644 --- a/internal/planner/mapper/mutation.go +++ b/internal/planner/mapper/mutation.go @@ -32,4 +32,7 @@ type Mutation struct { // Inputs is the array of maps of fields and values used for the mutation. Inputs []map[string]any + + // Encrypt is a flag to indicate if the input data should be encrypted. + Encrypt bool } diff --git a/internal/request/graphql/parser/mutation.go b/internal/request/graphql/parser/mutation.go index c1a3b31062..2ed4ebf539 100644 --- a/internal/request/graphql/parser/mutation.go +++ b/internal/request/graphql/parser/mutation.go @@ -140,6 +140,8 @@ func parseMutation(schema gql.Schema, parent *gql.Object, field *ast.Field) (*re ids[i] = id.Value } mut.DocIDs = immutable.Some(ids) + } else if prop == request.EncryptArgName { + mut.Encrypt = argument.Value.(*ast.BooleanValue).Value } } diff --git a/internal/request/graphql/schema/generate.go b/internal/request/graphql/schema/generate.go index b3c88ddd24..12fe1860dd 100644 --- a/internal/request/graphql/schema/generate.go +++ b/internal/request/graphql/schema/generate.go @@ -1061,8 +1061,9 @@ func (g *Generator) GenerateMutationInputForGQLType(obj *gql.Object) ([]*gql.Fie Description: createDocumentDescription, Type: obj, Args: gql.FieldConfigArgument{ - "input": schemaTypes.NewArgConfig(mutationInput, "Create field values"), - "inputs": schemaTypes.NewArgConfig(mutationInputs, "Create field values"), + "input": schemaTypes.NewArgConfig(mutationInput, "Create field values"), + "inputs": schemaTypes.NewArgConfig(mutationInputs, "Create field values"), + "encrypt": schemaTypes.NewArgConfig(gql.Boolean, "Encrypt input document(s)"), }, } diff --git a/tests/integration/utils2.go b/tests/integration/utils2.go index ee097be0d4..29c23534dc 100644 --- a/tests/integration/utils2.go +++ b/tests/integration/utils2.go @@ -1314,15 +1314,20 @@ func createDocViaGQL( var docs []*client.Document + params := paramName + ": " + input + + if action.IsEncrypted { + params = params + ", encrypt: true" + } + request := fmt.Sprintf( `mutation { - create_%s(%s: %s) { + create_%s(%s) { _docID } }`, collection.Name().Value(), - paramName, - input, + params, ) txn := getTransaction(s, node, immutable.None[int](), action.ExpectedError)