Skip to content

Commit

Permalink
Merge pull request #25 from sev-2/feature/storage-acl
Browse files Browse the repository at this point in the history
feature : add storage acl
  • Loading branch information
toopay authored May 31, 2024
2 parents c5e0a8a + c095b5e commit e5d2a7a
Show file tree
Hide file tree
Showing 24 changed files with 918 additions and 252 deletions.
40 changes: 0 additions & 40 deletions model.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package raiden
import (
"regexp"
"strings"

"github.com/sev-2/raiden/pkg/utils"
)

type (
Expand Down Expand Up @@ -40,17 +38,6 @@ type (
TargetForeignKey string
}

Acl struct {
Roles []string
Check *string
Using string
}

AclTag struct {
Read Acl
Write Acl
}

RelationType string
)

Expand Down Expand Up @@ -148,30 +135,3 @@ func UnmarshalJoinTag(tag string) JoinTag {

return joinTag
}

func UnmarshalAclTag(tag string) AclTag {
var aclTag AclTag

aclTagMap := utils.ParseTag(tag)
if readTag, exist := aclTagMap["read"]; exist && len(readTag) > 0 {
aclTag.Read.Roles = strings.Split(readTag, ",")
}

if writeTag, exist := aclTagMap["write"]; exist && len(writeTag) > 0 {
aclTag.Write.Roles = strings.Split(writeTag, ",")
}

if readTagUsing, exist := aclTagMap["readUsing"]; exist && len(readTagUsing) > 0 {
aclTag.Read.Using = readTagUsing
}

if writeTagCheck, exist := aclTagMap["writeCheck"]; exist && len(writeTagCheck) > 0 {
aclTag.Write.Check = &writeTagCheck
}

if writeTagUsing, exist := aclTagMap["writeUsing"]; exist && len(writeTagUsing) > 0 {
aclTag.Write.Using = writeTagUsing
}

return aclTag
}
82 changes: 2 additions & 80 deletions pkg/generator/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/sev-2/raiden/pkg/logger"
"github.com/sev-2/raiden/pkg/postgres"
"github.com/sev-2/raiden/pkg/state"
"github.com/sev-2/raiden/pkg/supabase"
"github.com/sev-2/raiden/pkg/supabase/objects"
"github.com/sev-2/raiden/pkg/utils"
)
Expand All @@ -19,11 +20,6 @@ var ModelLogger hclog.Logger = logger.HcLog().Named("generator.model")

// ----- Define type, variable and constant -----
type (
Rls struct {
CanWrite []string
CanRead []string
}

GenerateModelColumn struct {
Name string
Type string
Expand Down Expand Up @@ -112,15 +108,14 @@ func GenerateModel(folderPath string, input *GenerateModelInput, generateFn Gene

// map column data
columns, importsPath := MapTableAttributes(input.Table)
rlsTag := BuildRlsTag(input.Policies)
rlsTag := BuildRlsTag(input.Policies, input.Table.Name, supabase.RlsTypeModel)
raidenPath := "github.com/sev-2/raiden"
importsPath = append(importsPath, raidenPath)

// define file path
filePath := filepath.Join(folderPath, fmt.Sprintf("%s.%s", input.Table.Name, "go"))

// build relation tag

mapRelationName := make(map[string]bool)
relation := make([]state.Relation, 0)

Expand Down Expand Up @@ -261,75 +256,6 @@ func buildColumnTag(c objects.Column, mapPk map[string]bool) string {
return strings.Join(tags, " ")
}

func BuildRlsTag(rlsList objects.Policies) string {
var rls Rls

var readUsingTag, writeCheckTag, writeUsingTag string
for _, v := range rlsList {
switch v.Command {
case objects.PolicyCommandSelect:
if v.Name == getPolicyName(objects.PolicyCommandSelect, v.Table) {
rls.CanRead = append(rls.CanRead, v.Roles...)
if v.Definition != "" {
readUsingTag = v.Definition
}
}
case objects.PolicyCommandInsert, objects.PolicyCommandUpdate, objects.PolicyCommandDelete:
if v.Name == getPolicyName(objects.PolicyCommandInsert, v.Table) {
if len(rls.CanWrite) == 0 {
rls.CanWrite = append(rls.CanWrite, v.Roles...)
}

if len(writeCheckTag) == 0 && v.Check != nil {
writeCheckTag = *v.Check
}
}

if v.Name == getPolicyName(objects.PolicyCommandUpdate, v.Table) && len(rls.CanWrite) == 0 {
if len(rls.CanWrite) == 0 {
rls.CanWrite = append(rls.CanWrite, v.Roles...)
}

if len(writeCheckTag) == 0 && v.Check != nil {
writeCheckTag = *v.Check
}

if len(writeUsingTag) == 0 && v.Definition != "" {
writeUsingTag = v.Definition
}
}

if v.Name == getPolicyName(objects.PolicyCommandDelete, v.Table) && len(rls.CanWrite) == 0 {
if len(rls.CanWrite) == 0 {
rls.CanWrite = append(rls.CanWrite, v.Roles...)
}

if len(writeUsingTag) == 0 && v.Definition != "" {
writeUsingTag = v.Definition
}
}
}
}

rlsTag := fmt.Sprintf("read:%q write:%q", strings.Join(rls.CanRead, ","), strings.Join(rls.CanWrite, ","))
if len(readUsingTag) > 0 {
cleanTag := strings.TrimLeft(strings.TrimRight(readUsingTag, ")"), "(")
rlsTag = fmt.Sprintf("%s readUsing:%q", rlsTag, cleanTag)
}

if len(writeCheckTag) > 0 {
cleanTag := strings.TrimLeft(strings.TrimRight(writeCheckTag, ")"), "(")
rlsTag = fmt.Sprintf("%s writeCheck:%q", rlsTag, cleanTag)
}

if len(writeUsingTag) > 0 {
cleanTag := strings.TrimLeft(strings.TrimRight(writeUsingTag, ")"), "(")
rlsTag = fmt.Sprintf("%s writeUsing:%q", rlsTag, cleanTag)
}

return rlsTag
}

func BuildJoinTag(r *state.Relation) string {
var tags []string
var joinTags []string
Expand Down Expand Up @@ -378,7 +304,3 @@ func BuildJoinTag(r *state.Relation) string {

return strings.Join(tags, " ")
}

func getPolicyName(command objects.PolicyCommand, tableName string) string {
return strings.ToLower(fmt.Sprintf("enable %s access for table %s", command, tableName))
}
111 changes: 111 additions & 0 deletions pkg/generator/policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package generator

import (
"fmt"
"strings"
"unicode"

"github.com/sev-2/raiden/pkg/supabase"
"github.com/sev-2/raiden/pkg/supabase/objects"
)

type Rls struct {
CanWrite []string
CanRead []string
}

func BuildRlsTag(rlsList objects.Policies, name string, rlsType supabase.RlsType) string {
var rls Rls

var readUsingTag, writeCheckTag, writeUsingTag string
for _, v := range rlsList {
switch v.Command {
case objects.PolicyCommandSelect:
if v.Name == supabase.GetPolicyName(objects.PolicyCommandSelect, strings.ToLower(string(rlsType)), name) {
rls.CanRead = append(rls.CanRead, v.Roles...)
if v.Definition != "" {
readUsingTag = v.Definition
}
}
case objects.PolicyCommandInsert, objects.PolicyCommandUpdate, objects.PolicyCommandDelete:
if v.Name == supabase.GetPolicyName(objects.PolicyCommandInsert, strings.ToLower(string(rlsType)), name) {
if len(rls.CanWrite) == 0 {
rls.CanWrite = append(rls.CanWrite, v.Roles...)
}

if len(writeCheckTag) == 0 && v.Check != nil {
writeCheckTag = *v.Check
}
}

if v.Name == supabase.GetPolicyName(objects.PolicyCommandUpdate, strings.ToLower(string(rlsType)), name) && len(rls.CanWrite) == 0 {
if len(rls.CanWrite) == 0 {
rls.CanWrite = append(rls.CanWrite, v.Roles...)
}

if len(writeCheckTag) == 0 && v.Check != nil {
writeCheckTag = *v.Check
}

if len(writeUsingTag) == 0 && v.Definition != "" {
writeUsingTag = v.Definition
}
}

if v.Name == supabase.GetPolicyName(objects.PolicyCommandDelete, strings.ToLower(string(rlsType)), name) && len(rls.CanWrite) == 0 {
if len(rls.CanWrite) == 0 {
rls.CanWrite = append(rls.CanWrite, v.Roles...)
}

if len(writeUsingTag) == 0 && v.Definition != "" {
writeUsingTag = v.Definition
}
}
}
}

rlsTag := fmt.Sprintf("read:%q write:%q", strings.Join(rls.CanRead, ","), strings.Join(rls.CanWrite, ","))
if len(readUsingTag) > 0 {
cleanTag := strings.TrimLeft(strings.TrimRight(readUsingTag, ")"), "(")
if rlsType == supabase.RlsTypeStorage {
cleanTag = cleanupRlsTagStorage(name, cleanTag)
}

if cleanTag != "" {
rlsTag = fmt.Sprintf("%s readUsing:%q", rlsTag, cleanTag)
}
}

if len(writeCheckTag) > 0 {
cleanTag := strings.TrimLeft(strings.TrimRight(writeCheckTag, ")"), "(")
if rlsType == supabase.RlsTypeStorage {
cleanTag = cleanupRlsTagStorage(name, cleanTag)
}

if cleanTag != "" {
rlsTag = fmt.Sprintf("%s writeCheck:%q", rlsTag, cleanTag)
}
}

if len(writeUsingTag) > 0 {
cleanTag := strings.TrimLeft(strings.TrimRight(writeUsingTag, ")"), "(")
if rlsType == supabase.RlsTypeStorage {
cleanTag = cleanupRlsTagStorage(name, cleanTag)
}

if cleanTag != "" {
rlsTag = fmt.Sprintf("%s writeUsing:%q", rlsTag, cleanTag)
}
}

return rlsTag
}

func cleanupRlsTagStorage(name, tag string) string {
// clean storage identifier
cleanTag := strings.Replace(tag, fmt.Sprintf("bucket_id = '%s'", name), "", 1)
cleanTag = strings.Replace(cleanTag, "AND", "", 1)
cleanTag = strings.Replace(cleanTag, "OR", "", 1)
cleanTag = strings.TrimLeftFunc(cleanTag, unicode.IsSpace)
return cleanTag
}
90 changes: 90 additions & 0 deletions pkg/generator/policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package generator_test

import (
"encoding/json"
"testing"

"github.com/sev-2/raiden/pkg/generator"
"github.com/sev-2/raiden/pkg/supabase"
"github.com/sev-2/raiden/pkg/supabase/objects"
"github.com/stretchr/testify/assert"
)

const dummyPolicies = `
[
{
"id": 30023,
"schema": "storage",
"table": "objects",
"table_id": 29647,
"name": "enable select access for storage my-storage",
"action": "PERMISSIVE",
"roles": [
"admin_scouter",
"anon",
"authenticated"
],
"command": "SELECT",
"definition": "(bucket_id = 'my-storage'::text)",
"check": null
},
{
"id": 30022,
"schema": "storage",
"table": "objects",
"table_id": 29647,
"name": "enable update access for storage my-storage",
"action": "PERMISSIVE",
"roles": [
"admin_scouter",
"authenticated"
],
"command": "UPDATE",
"definition": "(bucket_id = 'my-storage'::text)",
"check": null
},
{
"id": 30021,
"schema": "storage",
"table": "objects",
"table_id": 29647,
"name": "enable delete access for storage my-storage",
"action": "PERMISSIVE",
"roles": [
"admin_scouter",
"authenticated"
],
"command": "DELETE",
"definition": "(bucket_id = 'my-storage'::text)",
"check": null
},
{
"id": 30020,
"schema": "storage",
"table": "objects",
"table_id": 29647,
"name": "enable insert access for storage my-storage",
"action": "PERMISSIVE",
"roles": [
"admin_scouter",
"authenticated"
],
"command": "INSERT",
"definition": "",
"check": "(bucket_id = 'my-storage'::text)"
}
]
`

func TestBuildStorageRlsTag(t *testing.T) {
var bucket = objects.Bucket{Name: "my-storage"}

var policies objects.Policies
err := json.Unmarshal([]byte(dummyPolicies), &policies)
assert.NoError(t, err)

storagePolicies := policies.FilterByBucket(bucket)
rlsTag := generator.BuildRlsTag(storagePolicies, bucket.Name, supabase.RlsTypeStorage)
expectedTag := `read:"admin_scouter,anon,authenticated" write:"admin_scouter,authenticated" readUsing:"bucket_id = 'my-storage'::text" writeCheck:"bucket_id = 'my-storage'::text" writeUsing:"bucket_id = 'my-storage'::text"`
assert.Equal(t, expectedTag, rlsTag)
}
Loading

0 comments on commit e5d2a7a

Please sign in to comment.