github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/repo/tenant_access_m2m.go (about) 1 package repo 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/pkg/errors" 9 10 "github.com/jmoiron/sqlx" 11 "github.com/kyma-incubator/compass/components/director/pkg/log" 12 "github.com/kyma-incubator/compass/components/director/pkg/persistence" 13 "github.com/kyma-incubator/compass/components/director/pkg/resource" 14 ) 15 16 const ( 17 // M2MTenantIDColumn is the column name of the tenant_id in each tenant access table / view. 18 M2MTenantIDColumn = "tenant_id" 19 // M2MResourceIDColumn is the column name of the resource id in each tenant access table / view. 20 M2MResourceIDColumn = "id" 21 // M2MOwnerColumn is the column name of the owner in each tenant access table / view. 22 M2MOwnerColumn = "owner" 23 24 // CreateSingleTenantAccessQuery sda 25 CreateSingleTenantAccessQuery = `INSERT INTO %s ( %s ) VALUES ( %s ) ON CONFLICT ON CONSTRAINT tenant_applications_pkey DO NOTHING` 26 27 // RecursiveCreateTenantAccessCTEQuery is a recursive SQL query that creates a tenant access record for a tenant and all its parents. 28 RecursiveCreateTenantAccessCTEQuery = `WITH RECURSIVE parents AS 29 (SELECT t1.id, t1.parent 30 FROM business_tenant_mappings t1 31 WHERE id = :tenant_id 32 UNION ALL 33 SELECT t2.id, t2.parent 34 FROM business_tenant_mappings t2 35 INNER JOIN parents t on t2.id = t.parent) 36 INSERT INTO %s ( %s ) (SELECT parents.id AS tenant_id, :id as id, :owner AS owner FROM parents)` 37 38 // RecursiveUpsertTenantAccessCTEQuery is a recursive SQL query that creates a tenant access record for a tenant and all its parents. 39 RecursiveUpsertTenantAccessCTEQuery = RecursiveCreateTenantAccessCTEQuery + " ON CONFLICT ( tenant_id, id ) DO NOTHING" 40 41 // RecursiveDeleteTenantAccessCTEQuery is a recursive SQL query that deletes tenant accesses based on given conditions for a tenant and all its parents. 42 RecursiveDeleteTenantAccessCTEQuery = `WITH RECURSIVE parents AS 43 (SELECT t1.id, t1.parent 44 FROM business_tenant_mappings t1 45 WHERE id = ? 46 UNION ALL 47 SELECT t2.id, t2.parent 48 FROM business_tenant_mappings t2 49 INNER JOIN parents t on t2.id = t.parent) 50 DELETE FROM %s WHERE %s AND tenant_id IN (SELECT id FROM parents)` 51 ) 52 53 // M2MColumns are the column names of the tenant access tables / views. 54 var M2MColumns = []string{M2MTenantIDColumn, M2MResourceIDColumn, M2MOwnerColumn} 55 56 // TenantAccess represents the tenant access table/views that are used for tenant isolation queries. 57 type TenantAccess struct { 58 TenantID string `db:"tenant_id"` 59 ResourceID string `db:"id"` 60 Owner bool `db:"owner"` 61 } 62 63 // TenantAccessCollection is a wrapper type for slice of entities. 64 type TenantAccessCollection []TenantAccess 65 66 // Len returns the current number of entities in the collection. 67 func (tc TenantAccessCollection) Len() int { 68 return len(tc) 69 } 70 71 // GetSingleTenantAccess gets a tenant access record for tenant with ID tenantID and resource with ID resourceID 72 func GetSingleTenantAccess(ctx context.Context, m2mTable string, tenantID, resourceID string) (*TenantAccess, error) { 73 getter := NewSingleGetterGlobal(resource.TenantAccess, m2mTable, M2MColumns) 74 75 tenantAccess := &TenantAccess{} 76 err := getter.GetGlobal(ctx, Conditions{NewEqualCondition(M2MTenantIDColumn, tenantID), NewEqualCondition(M2MResourceIDColumn, resourceID)}, NoOrderBy, tenantAccess) 77 if err != nil { 78 log.C(ctx).Error(persistence.MapSQLError(ctx, err, resource.TenantAccess, resource.Get, "while fetching tenant access record from '%s' table", m2mTable)) 79 return nil, err 80 } 81 82 return tenantAccess, nil 83 } 84 85 // CreateSingleTenantAccess create a tenant access for a single entity 86 func CreateSingleTenantAccess(ctx context.Context, m2mTable string, tenantAccess *TenantAccess) error { 87 values := make([]string, 0, len(M2MColumns)) 88 for _, c := range M2MColumns { 89 values = append(values, fmt.Sprintf(":%s", c)) 90 } 91 92 persist, err := persistence.FromCtx(ctx) 93 if err != nil { 94 return err 95 } 96 97 insertTenantAccessStmt := fmt.Sprintf(CreateSingleTenantAccessQuery, m2mTable, strings.Join(M2MColumns, ", "), strings.Join(values, ", ")) 98 99 log.C(ctx).Debugf("Executing DB query: %s", insertTenantAccessStmt) 100 _, err = persist.NamedExecContext(ctx, insertTenantAccessStmt, *tenantAccess) 101 102 return persistence.MapSQLError(ctx, err, resource.TenantAccess, resource.Create, "while inserting tenant access record to '%s' table", m2mTable) 103 } 104 105 // CreateTenantAccessRecursively creates the given tenantAccess in the provided m2mTable while making sure to recursively 106 // add it to all the parents of the given tenant. 107 func CreateTenantAccessRecursively(ctx context.Context, m2mTable string, tenantAccess *TenantAccess) error { 108 persist, err := persistence.FromCtx(ctx) 109 if err != nil { 110 return err 111 } 112 113 insertTenantAccessStmt := fmt.Sprintf(RecursiveCreateTenantAccessCTEQuery, m2mTable, strings.Join(M2MColumns, ", ")) 114 115 log.C(ctx).Debugf("Executing DB query: %s", insertTenantAccessStmt) 116 _, err = persist.NamedExecContext(ctx, insertTenantAccessStmt, tenantAccess) 117 118 return persistence.MapSQLError(ctx, err, resource.TenantAccess, resource.Create, "while inserting tenant access record to '%s' table", m2mTable) 119 } 120 121 // UpsertTenantAccessRecursively upserts the given tenantAccess in the provided m2mTable while making sure to recursively 122 // add it to all the parents of the given tenant. 123 func UpsertTenantAccessRecursively(ctx context.Context, m2mTable string, tenantAccess *TenantAccess) error { 124 persist, err := persistence.FromCtx(ctx) 125 if err != nil { 126 return err 127 } 128 129 insertTenantAccessStmt := fmt.Sprintf(RecursiveUpsertTenantAccessCTEQuery, m2mTable, strings.Join(M2MColumns, ", ")) 130 131 log.C(ctx).Debugf("Executing DB query: %s", insertTenantAccessStmt) 132 _, err = persist.NamedExecContext(ctx, insertTenantAccessStmt, tenantAccess) 133 134 return persistence.MapSQLError(ctx, err, resource.TenantAccess, resource.Create, "while upserting tenant access record to '%s' table", m2mTable) 135 } 136 137 // DeleteTenantAccessRecursively deletes all the accesses to the provided resource IDs for the given tenant and all its parents. 138 func DeleteTenantAccessRecursively(ctx context.Context, m2mTable string, tenant string, resourceIDs []string) error { 139 if len(resourceIDs) == 0 { 140 return errors.New("resourceIDs cannot be empty") 141 } 142 143 persist, err := persistence.FromCtx(ctx) 144 if err != nil { 145 return err 146 } 147 148 args := make([]interface{}, 0, len(resourceIDs)+1) 149 args = append(args, tenant) 150 151 inCond := NewInConditionForStringValues(M2MResourceIDColumn, resourceIDs) 152 if inArgs, ok := inCond.GetQueryArgs(); ok { 153 args = append(args, inArgs...) 154 } 155 156 deleteTenantAccessStmt := fmt.Sprintf(RecursiveDeleteTenantAccessCTEQuery, m2mTable, inCond.GetQueryPart()) 157 deleteTenantAccessStmt = sqlx.Rebind(sqlx.DOLLAR, deleteTenantAccessStmt) 158 159 log.C(ctx).Debugf("Executing DB query: %s", deleteTenantAccessStmt) 160 _, err = persist.ExecContext(ctx, deleteTenantAccessStmt, args...) 161 162 return persistence.MapSQLError(ctx, err, resource.TenantAccess, resource.Delete, "while deleting tenant access record from '%s' table", m2mTable) 163 }