diff --git a/cmd/policy-resourceMappings.go b/cmd/policy-resourceMappings.go new file mode 100644 index 00000000..97a63b5e --- /dev/null +++ b/cmd/policy-resourceMappings.go @@ -0,0 +1,176 @@ +package cmd + +import ( + _ "embed" + "fmt" + "strings" + + "github.com/opentdf/tructl/docs/man" + "github.com/opentdf/tructl/pkg/cli" + "github.com/spf13/cobra" +) + +var ( + policy_resourceMappingsTerms []string + + policy_resourceMappingsCmd = &cobra.Command{ + Use: man.PolicyResourceMappings["en"].Command, + Aliases: man.PolicyResourceMappings["en"].Aliases, + Short: man.PolicyResourceMappings["en"].ShortWithSubCommands([]string{ + policy_resourceMappingsCreateCmd.Use, + policy_resourceMappingsGetCmd.Use, + policy_resourceMappingsListCmd.Use, + policy_resourceMappingsUpdateCmd.Use, + policy_resourceMappingsDeleteCmd.Use, + }), + Long: man.PolicyResourceMappings["en"].Long, + } + + policy_resourceMappingsCreateCmd = &cobra.Command{ + Use: "create", + Short: "Create resource mappings", + Run: func(cmd *cobra.Command, args []string) { + h := cli.NewHandler(cmd) + defer h.Close() + + flagHelper := cli.NewFlagHelper(cmd) + attrId := flagHelper.GetRequiredString("attribute-value-id") + terms := flagHelper.GetStringSlice("terms", policy_resourceMappingsTerms, cli.FlagHelperStringSliceOptions{ + Min: 1, + }) + + resourceMapping, err := h.CreateResourceMapping(attrId, terms) + if err != nil { + cli.ExitWithError("Failed to create resource mapping", err) + } + + fmt.Println(cli.SuccessMessage("Resource mapping created")) + fmt.Println(cli.NewTabular().Rows([][]string{ + {"Id", resourceMapping.Id}, + {"Attribute Id", resourceMapping.AttributeValue.AttributeId}, + {"Attribute Value", resourceMapping.AttributeValue.Value}, + {"Terms", strings.Join(resourceMapping.Terms, ", ")}, + }...).Render()) + }, + } + + policy_resourceMappingsGetCmd = &cobra.Command{ + Use: "get", + Short: "Get resource mappings", + Run: func(cmd *cobra.Command, args []string) { + h := cli.NewHandler(cmd) + defer h.Close() + + flagHelper := cli.NewFlagHelper(cmd) + id := flagHelper.GetRequiredString("id") + + resourceMapping, err := h.GetResourceMapping(id) + if err != nil { + cli.ExitWithError("Failed to get resource mapping", err) + } + + fmt.Println(cli.NewTabular().Rows([][]string{ + {"Id", resourceMapping.Id}, + {"Attribute Id", resourceMapping.AttributeValue.AttributeId}, + {"Attribute Value", resourceMapping.AttributeValue.Value}, + {"Terms", strings.Join(resourceMapping.Terms, ", ")}, + }...).Render()) + }, + } + + policy_resourceMappingsListCmd = &cobra.Command{ + Use: "list", + Short: "List resource mappings", + Run: func(cmd *cobra.Command, args []string) { + h := cli.NewHandler(cmd) + defer h.Close() + + r, err := h.ListResourceMappings() + if err != nil { + cli.ExitWithError("Failed to list resource mappings", err) + } + + t := cli.NewTable() + t.Headers("Id", "Attribute Id", "Attribute Value", "Terms") + for _, resourceMapping := range r { + t.Row(resourceMapping.Id, resourceMapping.AttributeValue.AttributeId, resourceMapping.AttributeValue.Value, strings.Join(resourceMapping.Terms, ", ")) + } + fmt.Println(t.Render()) + }, + } + + policy_resourceMappingsUpdateCmd = &cobra.Command{ + Use: "update", + Short: "Update resource mappings", + Run: func(cmd *cobra.Command, args []string) { + h := cli.NewHandler(cmd) + defer h.Close() + + flagHelper := cli.NewFlagHelper(cmd) + id := flagHelper.GetRequiredString("id") + attrValueId := flagHelper.GetOptionalString("attribute-value-id") + terms := flagHelper.GetStringSlice("terms", policy_resourceMappingsTerms, cli.FlagHelperStringSliceOptions{}) + + resourceMapping, err := h.UpdateResourceMapping(id, attrValueId, terms) + if err != nil { + cli.ExitWithError("Failed to update resource mapping", err) + } + + fmt.Println(cli.SuccessMessage("Resource mapping updated")) + fmt.Println(cli.NewTabular().Rows([][]string{ + {"Id", resourceMapping.Id}, + {"Attribute Id", resourceMapping.AttributeValue.AttributeId}, + {"Attribute Value", resourceMapping.AttributeValue.Value}, + {"Terms", strings.Join(resourceMapping.Terms, ", ")}, + }...).Render()) + }, + } + + policy_resourceMappingsDeleteCmd = &cobra.Command{ + Use: "delete", + Short: "Delete resource mappings", + Run: func(cmd *cobra.Command, args []string) { + h := cli.NewHandler(cmd) + defer h.Close() + + flagHelper := cli.NewFlagHelper(cmd) + id := flagHelper.GetRequiredString("id") + + cli.ConfirmDelete("resource-mapping", id) + + resourceMapping, err := h.DeleteResourceMapping(id) + if err != nil { + cli.ExitWithError("Failed to delete resource mapping", err) + } + + fmt.Println(cli.SuccessMessage("Resource mapping deleted")) + fmt.Println(cli.NewTabular().Rows([][]string{ + {"Id", resourceMapping.Id}, + {"Attribute Id", resourceMapping.AttributeValue.AttributeId}, + {"Attribute Value", resourceMapping.AttributeValue.Value}, + {"Terms", strings.Join(resourceMapping.Terms, ", ")}, + }...).Render()) + }, + } +) + +func init() { + policyCmd.AddCommand(policy_resourceMappingsCmd) + + policy_resourceMappingsCmd.AddCommand(policy_resourceMappingsCreateCmd) + policy_resourceMappingsCreateCmd.Flags().String("attribute-value-id", "", "Attribute Value ID") + policy_resourceMappingsCreateCmd.Flags().StringSliceVar(&policy_resourceMappingsTerms, "terms", []string{}, "Synonym terms") + + policy_resourceMappingsCmd.AddCommand(policy_resourceMappingsGetCmd) + policy_resourceMappingsGetCmd.Flags().String("id", "", "Resource Mapping ID") + + policy_resourceMappingsCmd.AddCommand(policy_resourceMappingsListCmd) + + policy_resourceMappingsCmd.AddCommand(policy_resourceMappingsUpdateCmd) + policy_resourceMappingsUpdateCmd.Flags().String("id", "", "Resource Mapping ID") + policy_resourceMappingsUpdateCmd.Flags().String("attribute-value-id", "", "Attribute Value ID") + policy_resourceMappingsUpdateCmd.Flags().StringSliceVar(&policy_resourceMappingsTerms, "terms", []string{}, "Synonym terms") + + policy_resourceMappingsCmd.AddCommand(policy_resourceMappingsDeleteCmd) + policy_resourceMappingsDeleteCmd.Flags().String("id", "", "Resource Mapping ID") +} diff --git a/docs/man/man.go b/docs/man/man.go new file mode 100644 index 00000000..c20d296d --- /dev/null +++ b/docs/man/man.go @@ -0,0 +1,47 @@ +package man + +import ( + _ "embed" + "fmt" + "strings" + + "github.com/adrg/frontmatter" +) + +//go:embed policy-resourceMappings.md +var policyResourceMappingsEn string + +//go:embed policy-resourceMappings.fr.md +var policyResourceMappingsFr string + +var PolicyResourceMappings = map[string]Doc{} + +type Doc struct { + Command string `yaml:"command"` + Aliases []string `yaml:"aliases"` + Short string `yaml:"short"` + Long string +} + +func (m Doc) ShortWithSubCommands(subcommands []string) string { + return m.Short + " [" + strings.Join(subcommands, ", ") + "]" +} + +func init() { + PolicyResourceMappings["en"] = ProcessDoc(policyResourceMappingsEn) + PolicyResourceMappings["fr"] = ProcessDoc(policyResourceMappingsFr) +} + +func ProcessDoc(doc string) Doc { + if len(doc) <= 0 { + return Doc{} + } + var matter Doc + rest, err := frontmatter.Parse(strings.NewReader(doc), &matter) + if err != nil { + fmt.Print(err) + return Doc{} + } + matter.Long = string(rest) + return matter +} diff --git a/docs/man/policy-resourceMappings.fr.md b/docs/man/policy-resourceMappings.fr.md new file mode 100644 index 00000000..4b177022 --- /dev/null +++ b/docs/man/policy-resourceMappings.fr.md @@ -0,0 +1,11 @@ +--- +command: "resource-mappings" +aliases: ["resource-mapping", "resource-encodings", "resource-encoding"] +short: "Gérer les mappages de ressources" +--- + +# Gérer les mappages de ressources + +Les mappages de ressources sont utilisés pour mapper les ressources à leurs valeurs d'attribut respectives en fonction des termes qui sont liés aux données. Seul, ce service n'est pas très utile, mais lorsqu'il est combiné avec un PEP ou un PDP qui peut utiliser les mappages de ressources, il devient un outil puissant pour automatiser le contrôle d'accès. + +Par exemple, le PDP de marquage utilise des mappages de ressources pour mapper les ressources en fonction des termes trouvés dans les métadonnées et les documents qui lui sont envoyés. Combiné avec les mappages de ressources, il peut alors déterminer quels attributs doivent être appliqués au TDF et renvoyer ces attributs au PEP. diff --git a/docs/man/policy-resourceMappings.md b/docs/man/policy-resourceMappings.md new file mode 100644 index 00000000..5a509ace --- /dev/null +++ b/docs/man/policy-resourceMappings.md @@ -0,0 +1,15 @@ +--- +command: "resource-mappings" +aliases: ["resource-mapping", "resource-encodings", "resource-encoding"] +short: "Manage resource mappings" +--- + +# Manage Resource Mappings + +Resource mappings are used to map resources to their respective attribute values based on the terms +that are related to the data. Alone, this service is not very useful, but when combined with a PEP +or PDP that can use the resource mappings it becomes a powerful tool for automating access control. + +As an example, Tagging PDP uses resource mappings to map resources based on the terms found within +the metadata and documents which are sent to it. Combined with the resource mappings it can then +determine which attributes should be applied to the TDF and return those attributes to the PEP. diff --git a/go.mod b/go.mod index 2f65655f..791bca27 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,8 @@ require ( require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1 // indirect + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/adrg/frontmatter v0.2.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/catppuccin/go v0.2.0 // indirect @@ -44,4 +46,5 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect google.golang.org/protobuf v1.32.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 9a086a8f..fa24a168 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1 h1:u0olL4yf2p7Tl5jfsAK5keaFi+JFJuv1CDHrbiXkxkk= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1/go.mod h1:tiTMKD8j6Pd/D2WzREoweufjzaJKHZg35f/VGcZ2v3I= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/adrg/frontmatter v0.2.0 h1:/DgnNe82o03riBd1S+ZDjd43wAmC6W35q67NHeLkPd4= +github.com/adrg/frontmatter v0.2.0/go.mod h1:93rQCj3z3ZlwyxxpQioRKC1wDLto4aXHrbqIsnH9wmE= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -31,8 +35,6 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -91,4 +93,7 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/handlers/resourceMappings.go b/pkg/handlers/resourceMappings.go new file mode 100644 index 00000000..815b0de9 --- /dev/null +++ b/pkg/handlers/resourceMappings.go @@ -0,0 +1,74 @@ +package handlers + +import ( + "context" + + "github.com/opentdf/opentdf-v2-poc/sdk/resourcemapping" +) + +type ResourceMapping struct { + Id string + AttributeId string + Terms []string +} + +func (h *Handler) CreateResourceMapping(attributeId string, terms []string) (*resourcemapping.ResourceMapping, error) { + res, err := h.sdk.ResourceMapping.CreateResourceMapping(context.Background(), &resourcemapping.CreateResourceMappingRequest{ + ResourceMapping: &resourcemapping.ResourceMappingCreateUpdate{ + AttributeValueId: attributeId, + Terms: terms, + }, + }) + if err != nil { + return nil, err + } + + return res.ResourceMapping, nil +} + +func (h *Handler) GetResourceMapping(id string) (*resourcemapping.ResourceMapping, error) { + res, err := h.sdk.ResourceMapping.GetResourceMapping(context.Background(), &resourcemapping.GetResourceMappingRequest{ + Id: id, + }) + if err != nil { + return nil, err + } + + return res.ResourceMapping, nil +} + +func (h *Handler) ListResourceMappings() ([]*resourcemapping.ResourceMapping, error) { + res, err := h.sdk.ResourceMapping.ListResourceMappings(context.Background(), &resourcemapping.ListResourceMappingsRequest{}) + if err != nil { + return nil, err + } + + return res.ResourceMappings, nil +} + +func (h *Handler) UpdateResourceMapping(id string, attrValueId string, terms []string) (*resourcemapping.ResourceMapping, error) { + res, err := h.sdk.ResourceMapping.UpdateResourceMapping(context.Background(), &resourcemapping.UpdateResourceMappingRequest{ + Id: id, + ResourceMapping: &resourcemapping.ResourceMappingCreateUpdate{ + AttributeValueId: attrValueId, + Terms: terms, + }, + }) + if err != nil { + return nil, err + } + + return res.ResourceMapping, nil + +} + +func (h *Handler) DeleteResourceMapping(id string) (*resourcemapping.ResourceMapping, error) { + resp, err := h.sdk.ResourceMapping.DeleteResourceMapping(context.Background(), &resourcemapping.DeleteResourceMappingRequest{ + Id: id, + }) + if err != nil { + return nil, err + } + + return resp.ResourceMapping, nil +}