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  }