policy-engine/iam/converter_s3.go
Denis Kirillov a0a35bf4bf [#22] iam: Fix converters
Validate that actions and resources contain wildcard only at the end

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-11-28 17:56:36 +03:00

173 lines
4.3 KiB
Go

package iam
import (
"fmt"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/s3"
)
type S3Resolver interface {
GetUserAddress(account, user string) (string, error)
}
func ConvertToS3Chain(p Policy, resolver S3Resolver) (*chain.Chain, error) {
if err := p.Validate(ResourceBasedPolicyType); err != nil {
return nil, err
}
var engineChain chain.Chain
for _, statement := range p.Statement {
status := formStatus(statement)
action, actionInverted := statement.action()
s3Actions, err := formS3ActionNames(action)
if err != nil {
return nil, err
}
ruleAction := chain.Actions{Inverted: actionInverted, Names: s3Actions}
resource, resourceInverted := statement.resource()
s3Resources, err := formS3ResourceNames(resource)
if err != nil {
return nil, err
}
ruleResource := chain.Resources{Inverted: resourceInverted, Names: s3Resources}
groupedConditions, err := convertToS3ChainCondition(statement.Conditions, resolver)
if err != nil {
return nil, err
}
splitConditions := splitGroupedConditions(groupedConditions)
principals, principalCondFn, err := getS3PrincipalsAndConditionFunc(statement, resolver)
if err != nil {
return nil, err
}
for _, principal := range principals {
for _, conditions := range splitConditions {
r := chain.Rule{
Status: status,
Actions: ruleAction,
Resources: ruleResource,
Condition: append([]chain.Condition{principalCondFn(principal)}, conditions...),
}
engineChain.Rules = append(engineChain.Rules, r)
}
}
}
return &engineChain, nil
}
func getS3PrincipalsAndConditionFunc(statement Statement, resolver S3Resolver) ([]string, formPrincipalConditionFunc, error) {
var principals []string
var op chain.ConditionType
statementPrincipal, inverted := statement.principal()
if _, ok := statementPrincipal[Wildcard]; ok { // this can be true only if 'inverted' false
principals = []string{Wildcard}
op = chain.CondStringLike
} else {
for principalType, principal := range statementPrincipal {
if principalType != AWSPrincipalType {
return nil, nil, fmt.Errorf("unsupported principal type '%s'", principalType)
}
parsedPrincipal, err := formS3Principal(principal, resolver)
if err != nil {
return nil, nil, fmt.Errorf("parse principal: %w", err)
}
principals = append(principals, parsedPrincipal...)
}
op = chain.CondStringEquals
if inverted {
op = chain.CondStringNotEquals
}
}
return principals, func(principal string) chain.Condition {
return chain.Condition{
Op: op,
Object: chain.ObjectRequest,
Key: s3.PropertyKeyOwner,
Value: principal,
}
}, nil
}
func convertToS3ChainCondition(c Conditions, resolver S3Resolver) ([]GroupedConditions, error) {
return convertToChainConditions(c, func(gr GroupedConditions) (GroupedConditions, error) {
for i := range gr.Conditions {
if gr.Conditions[i].Key == condKeyAWSPrincipalARN {
gr.Conditions[i].Key = s3.PropertyKeyOwner
val, err := formPrincipalOwner(gr.Conditions[i].Value, resolver)
if err != nil {
return GroupedConditions{}, err
}
gr.Conditions[i].Value = val
}
}
return gr, nil
})
}
func formS3Principal(principal []string, resolver S3Resolver) ([]string, error) {
res := make([]string, len(principal))
var err error
for i := range principal {
if res[i], err = formPrincipalOwner(principal[i], resolver); err != nil {
return nil, err
}
}
return res, nil
}
func formPrincipalOwner(principal string, resolver S3Resolver) (string, error) {
account, user, err := parsePrincipalAsIAMUser(principal)
if err != nil {
return "", err
}
address, err := resolver.GetUserAddress(account, user)
if err != nil {
return "", fmt.Errorf("get user address: %w", err)
}
return address, nil
}
func formS3ResourceNames(names []string) ([]string, error) {
res := make([]string, len(names))
for i := range names {
bkt, obj, err := parseResourceAsS3ARN(names[i])
if err != nil {
return nil, err
}
if bkt == Wildcard {
res[i] = bkt
continue
}
res[i] = bkt + "/" + obj
}
return res, nil
}
func formS3ActionNames(names []string) ([]string, error) {
var err error
res := make([]string, len(names))
for i := range names {
if res[i], err = parseActionAsS3Action(names[i]); err != nil {
return nil, err
}
}
return res, nil
}