Skip to content

Commit

Permalink
Add Resource Relations resource
Browse files Browse the repository at this point in the history
  • Loading branch information
orishavit committed Nov 23, 2023
1 parent 32ead6f commit 7d17e32
Show file tree
Hide file tree
Showing 9 changed files with 304 additions and 2 deletions.
11 changes: 10 additions & 1 deletion examples/rebac/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ resource "permitio_resource" "file" {
"name" = "delete"
}
}
attributes = {}
}

resource "permitio_resource" "folder" {
Expand All @@ -46,4 +47,12 @@ resource "permitio_resource" "folder" {
"name" = "modify"
}
}
}
attributes = {}
}

resource "permitio_relation" "parent" {
key = "parent"
name = "parent of"
subject_resource = permitio_resource.folder.key
object_resource = permitio_resource.file.key
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/hashicorp/terraform-plugin-go v0.18.0
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-testing v1.4.0
github.com/permitio/permit-golang v1.0.1
github.com/permitio/permit-golang v1.0.5-0.20231120113331-62793fdfd980
)

require (
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/permitio/permit-golang v1.0.1 h1:duRb8lmIhVTAa5oGCNU9KnIQgnKmyn0iZ7PVpCXOx8k=
github.com/permitio/permit-golang v1.0.1/go.mod h1:phP2AVSL3bgDKfhhmhPt/VJAN8UUDJoQtVjUKRfY5Ck=
github.com/permitio/permit-golang v1.0.5-0.20231120113331-62793fdfd980 h1:ANfH8WXL62ZuKUXKTQoUokKu5J6ad4LF5aFETfeiam8=
github.com/permitio/permit-golang v1.0.5-0.20231120113331-62793fdfd980/go.mod h1:phP2AVSL3bgDKfhhmhPt/VJAN8UUDJoQtVjUKRfY5Ck=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down
26 changes: 26 additions & 0 deletions internal/provider/common/resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package common

import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/permitio/permit-golang/pkg/permit"
)

func Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) *permit.Client {
if request.ProviderData == nil {
return nil
}

permitClient, ok := request.ProviderData.(*permit.Client)

if !ok {
response.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected *permit.Client, got: %T. Please report this issue to the provider developers.", request.ProviderData),
)
return nil
}

return permitClient
}
49 changes: 49 additions & 0 deletions internal/provider/common/schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package common

import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
)

func CreateBaseResourceSchema() map[string]schema.Attribute {
return map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"key": schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"name": schema.StringAttribute{
Required: true,
},
"description": schema.StringAttribute{
Optional: true,
Computed: true,
},
"organization_id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"project_id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"environment_id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
}
}
2 changes: 2 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
conditionsetrules "github.com/permitio/terraform-provider-permit-io/internal/provider/conditionset_rules"
"github.com/permitio/terraform-provider-permit-io/internal/provider/conditionsets"
"github.com/permitio/terraform-provider-permit-io/internal/provider/proxy_configs"
"github.com/permitio/terraform-provider-permit-io/internal/provider/relations"
"github.com/permitio/terraform-provider-permit-io/internal/provider/resources"
"github.com/permitio/terraform-provider-permit-io/internal/provider/roles"
"os"
Expand Down Expand Up @@ -150,6 +151,7 @@ func (p *PermitProvider) Resources(_ context.Context) []func() resource.Resource
conditionsets.NewResourceSetResource,
conditionsetrules.NewConditionSetRuleResource,
proxy_configs.NewProxyConfigResource,
relations.NewRelationResource,
}
}

Expand Down
42 changes: 42 additions & 0 deletions internal/provider/relations/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package relations

import (
"context"
"github.com/permitio/permit-golang/pkg/models"
"github.com/permitio/permit-golang/pkg/permit"
)

type relationClient struct {
client *permit.Client
}

func (c *relationClient) Create(ctx context.Context, plan relationModel) (relationModel, error) {
relationCreate := models.RelationCreate{
Key: plan.Key.ValueString(),
Name: plan.Name.ValueString(),
Description: plan.Description.ValueStringPointer(),
SubjectResource: plan.SubjectResource.ValueString(),
}

createdRelation, err := c.client.Api.ResourceRelations.Create(ctx, plan.ObjectResource.ValueString(), relationCreate)

if err != nil {
return invalidModel, err
}

return tfModelFromSDK(*createdRelation), nil
}

func (c *relationClient) Read(ctx context.Context, objectResourceKey, key string) (relationModel, error) {
readRelation, err := c.client.Api.ResourceRelations.Get(ctx, objectResourceKey, key)

if err != nil {
return invalidModel, err
}

return tfModelFromSDK(*readRelation), nil
}

func (c *relationClient) Delete(ctx context.Context, objectResourceKey, key string) error {
return c.client.Api.ResourceRelations.Delete(ctx, objectResourceKey, key)
}
40 changes: 40 additions & 0 deletions internal/provider/relations/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package relations

import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/permitio/permit-golang/pkg/models"
)

type relationModel struct {
Id types.String `tfsdk:"id"`
Key types.String `tfsdk:"key"`
Name types.String `tfsdk:"name"`
Description types.String `tfsdk:"description"`
EnvironmentId types.String `tfsdk:"environment_id"`
ProjectId types.String `tfsdk:"project_id"`
OrganizationId types.String `tfsdk:"organization_id"`

SubjectResource types.String `tfsdk:"subject_resource"`
ObjectResource types.String `tfsdk:"object_resource"`
SubjectResourceId types.String `tfsdk:"subject_resource_id"`
ObjectResourceId types.String `tfsdk:"object_resource_id"`
}

var invalidModel = relationModel{}

func tfModelFromSDK(m models.RelationRead) relationModel {
r := relationModel{}
r.Id = types.StringValue(m.Id)
r.Key = types.StringValue(m.Key)
r.Name = types.StringValue(m.Name)
r.Description = types.StringPointerValue(m.Description)
r.SubjectResourceId = types.StringValue(m.SubjectResourceId)
r.SubjectResource = types.StringValue(m.SubjectResource)
r.ObjectResourceId = types.StringValue(m.ObjectResourceId)
r.ObjectResource = types.StringValue(m.ObjectResource)
r.EnvironmentId = types.StringValue(m.EnvironmentId)
r.ProjectId = types.StringValue(m.ProjectId)
r.OrganizationId = types.StringValue(m.OrganizationId)

return r
}
132 changes: 132 additions & 0 deletions internal/provider/relations/resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package relations

import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/permitio/terraform-provider-permit-io/internal/provider/common"
)

// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &RelationResource{}
_ resource.ResourceWithConfigure = &RelationResource{}
)

func NewRelationResource() resource.Resource {
return &RelationResource{}
}

type RelationResource struct {
client relationClient
}

func (c *RelationResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) {
response.TypeName = request.ProviderTypeName + "_relation"
}

func (c *RelationResource) Configure(ctx context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) {
permitClient := common.Configure(ctx, request, response)
c.client = relationClient{client: permitClient}
}

func (c *RelationResource) Schema(_ context.Context, _ resource.SchemaRequest, response *resource.SchemaResponse) {
attributes := common.CreateBaseResourceSchema()

attributes["subject_resource"] = schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
}
attributes["object_resource"] = schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
}

attributes["subject_resource_id"] = schema.StringAttribute{
Computed: true,
}
attributes["object_resource_id"] = schema.StringAttribute{
Computed: true,
}

response.Schema = schema.Schema{
Attributes: attributes,
}
}

func (c *RelationResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) {
var plan relationModel

response.Diagnostics.Append(request.Plan.Get(ctx, &plan)...)

if response.Diagnostics.HasError() {
return
}

reality, err := c.client.Create(ctx, plan)

if err != nil {
response.Diagnostics.AddError(
"Failed creating relation",
fmt.Errorf("unable to create relation: %w", err).Error(),
)
return
}

response.Diagnostics.Append(response.State.Set(ctx, reality)...)
}

func (c *RelationResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) {
var model relationModel

response.Diagnostics.Append(request.State.Get(ctx, &model)...)

if response.Diagnostics.HasError() {
return
}

reality, err := c.client.Read(ctx, model.ObjectResourceId.ValueString(), model.Key.ValueString())

if err != nil {
response.Diagnostics.AddError(
"Failed reading relation",
fmt.Errorf("unable to read relation %s/%s: %w", model.ObjectResourceId, model.Key, err).Error(),
)
return
}

response.Diagnostics.Append(response.State.Set(ctx, &reality)...)
}

func (c *RelationResource) Update(_ context.Context, _ resource.UpdateRequest, response *resource.UpdateResponse) {
response.Diagnostics.AddError(
"Unsupported operation",
"resource relations must be replaced, and cannot be updated",
)
}

func (c *RelationResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) {
var model relationModel
response.Diagnostics.Append(request.State.Get(ctx, &model)...)

if response.Diagnostics.HasError() {
return
}

err := c.client.Delete(ctx, model.ObjectResource.ValueString(), model.Key.ValueString())

if err != nil {
response.Diagnostics.AddError(
"Failed deleting relation",
fmt.Errorf("unable to delete relation %s/%s: %w", model.ObjectResource.ValueString(), model.Key.ValueString(), err).Error(),
)
return
}
}

0 comments on commit 7d17e32

Please sign in to comment.