~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
146 lines · 4.1 KB Raw History
package guestidentity

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"sort"

	"github.com/johnandersen777/socialweb-computer-kcp/api/v1alpha1"
)

type RBACRecord struct {
	Roles map[string]RBACRole `json:"roles,omitempty"`
}

type RBACRole struct {
	RoleName string `json:"role_name,omitempty"`

	Definition RBACRoleDefinition `json:"definition"`
}

type RBACRoleDefinition struct {
	Iss string `json:"iss,omitempty"`

	Aud string `json:"aud,omitempty"`

	Sub string `json:"sub"`

	Policies []string `json:"policies,omitempty"`
}

type GrantWish struct {
	API string

	Actx string

	Scopes []string

	Claims []v1alpha1.GuestIdentityClaim
}

func (w GrantWish) Audience() string { return GuestAudience(w.API, w.Actx) }

func OwnWish(computevm string) GrantWish {
	return GrantWish{API: KCPAPI, Actx: computevm, Scopes: []string{OnNetworkScope}}
}

func Wishes(computevm string, records []RBACRecord, issuer, subject string) []GrantWish {
	byAudience := map[string]GrantWish{}
	add := func(w GrantWish) {
		key := w.Audience()
		held, ok := byAudience[key]
		if !ok {
			held = GrantWish{API: w.API, Actx: w.Actx, Claims: append([]v1alpha1.GuestIdentityClaim(nil), w.Claims...)}
		}
		for _, scope := range w.Scopes {
			if !contains(held.Scopes, scope) {
				held.Scopes = append(held.Scopes, scope)
			}
		}
		byAudience[key] = held
	}
	add(OwnWish(computevm))
	for _, wish := range WishesFromRBAC(records, issuer, subject) {
		add(wish)
	}
	out := make([]GrantWish, 0, len(byAudience))
	for _, wish := range byAudience {
		sort.Strings(wish.Scopes)
		sort.Slice(wish.Claims, func(i, j int) bool { return wish.Claims[i].Name < wish.Claims[j].Name })
		out = append(out, wish)
	}
	sort.Slice(out, func(i, j int) bool { return out[i].Audience() < out[j].Audience() })
	return out
}

func WishesFromRBAC(records []RBACRecord, issuer, subject string) []GrantWish {
	byAudience := map[string]GrantWish{}
	for _, record := range records {
		for _, role := range record.Roles {
			def := role.Definition
			if def.Sub != subject {
				continue
			}
			if def.Iss != "" && def.Iss != issuer {
				continue
			}
			audience, err := ParseAudience(def.Aud)
			if err != nil {
				continue
			}
			key := audience.String()
			wish, ok := byAudience[key]
			if !ok {
				wish = GrantWish{API: audience.API, Actx: audience.Actx,
					Claims: []v1alpha1.GuestIdentityClaim{{Name: OIDCSubjectClaim, Value: subject}}}
			}
			for _, policy := range def.Policies {
				if !contains(wish.Scopes, policy) {
					wish.Scopes = append(wish.Scopes, policy)
				}
			}
			byAudience[key] = wish
		}
	}
	out := make([]GrantWish, 0, len(byAudience))
	for _, wish := range byAudience {
		sort.Strings(wish.Scopes)
		out = append(out, wish)
	}
	sort.Slice(out, func(i, j int) bool { return out[i].Audience() < out[j].Audience() })
	return out
}

func GrantName(computevm string, wish GrantWish) string {
	sum := sha256.Sum256([]byte(wish.Audience()))
	return computevm + "-" + hex.EncodeToString(sum[:])[:12]
}

func WishKey(wish GrantWish) string {
	scopes := append([]string(nil), wish.Scopes...)
	sort.Strings(scopes)
	claims := append([]v1alpha1.GuestIdentityClaim(nil), wish.Claims...)
	sort.Slice(claims, func(i, j int) bool { return claims[i].Name < claims[j].Name })
	raw, _ := json.Marshal(struct {
		API    string                        `json:"api"`
		Actx   string                        `json:"actx"`
		Scopes []string                      `json:"scopes"`
		Claims []v1alpha1.GuestIdentityClaim `json:"claims"`
	}{wish.API, wish.Actx, scopes, claims})
	return string(raw)
}

func HeldKey(g v1alpha1.GuestIdentityGrant) string {
	scopes := append([]string(nil), g.Spec.Scopes...)
	sort.Strings(scopes)
	claims := append([]v1alpha1.GuestIdentityClaim(nil), g.Spec.Claims...)
	sort.Slice(claims, func(i, j int) bool { return claims[i].Name < claims[j].Name })
	raw, _ := json.Marshal(struct {
		API    string                        `json:"api"`
		Actx   string                        `json:"actx"`
		Scopes []string                      `json:"scopes"`
		Claims []v1alpha1.GuestIdentityClaim `json:"claims"`
	}{g.Spec.Api, g.Spec.Actx, scopes, claims})
	return string(raw)
}