289 lines
6.1 KiB
Go
289 lines
6.1 KiB
Go
|
package app
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"os/user"
|
||
|
"sort"
|
||
|
"strings"
|
||
|
|
||
|
"go.sour.is/pkg/mercury"
|
||
|
"go.sour.is/pkg/ident"
|
||
|
"go.sour.is/pkg/rsql"
|
||
|
"go.sour.is/pkg/set"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
mercurySource = "mercury.source.*"
|
||
|
mercuryPriority = "mercury.priority"
|
||
|
mercuryHost = "mercury.host"
|
||
|
appDotEnviron = "mercury.environ"
|
||
|
)
|
||
|
var (
|
||
|
mercuryPolicy = func(id string) string { return "mercury.@" + id + ".policy" }
|
||
|
)
|
||
|
|
||
|
func Register(name string, cfg mercury.SpaceMap) {
|
||
|
for _, c := range cfg {
|
||
|
c.Tags = append(c.Tags, "RO")
|
||
|
}
|
||
|
mercury.Registry.Register("mercury-default", func(s *mercury.Space) any { return &mercuryDefault{name: name, cfg: cfg} })
|
||
|
mercury.Registry.Register("mercury-environ", func(s *mercury.Space) any { return &mercuryEnviron{cfg: cfg, lookup: mercury.Registry.GetRules} })
|
||
|
}
|
||
|
|
||
|
type hasRole interface {
|
||
|
HasRole(r ...string) bool
|
||
|
}
|
||
|
|
||
|
type mercuryEnviron struct {
|
||
|
cfg mercury.SpaceMap
|
||
|
lookup func(context.Context, ident.Ident) (mercury.Rules, error)
|
||
|
}
|
||
|
|
||
|
// Index returns nil
|
||
|
func (app *mercuryEnviron) GetIndex(ctx context.Context, search mercury.NamespaceSearch, _ *rsql.Program) (lis mercury.Config, err error) {
|
||
|
|
||
|
if search.Match(mercurySource) {
|
||
|
for _, s := range app.cfg.ToArray() {
|
||
|
if search.Match(s.Space) {
|
||
|
lis = append(lis, &mercury.Space{Space: s.Space, Tags: []string{"RO"}})
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if search.Match(mercuryPriority) {
|
||
|
lis = append(lis, &mercury.Space{Space: mercuryPriority, Tags: []string{"RO"}})
|
||
|
}
|
||
|
|
||
|
if search.Match(mercuryHost) {
|
||
|
lis = append(lis, &mercury.Space{Space: mercuryHost, Tags: []string{"RO"}})
|
||
|
}
|
||
|
|
||
|
if search.Match(appDotEnviron) {
|
||
|
lis = append(lis, &mercury.Space{Space: appDotEnviron, Tags: []string{"RO"}})
|
||
|
}
|
||
|
if id := ident.FromContext(ctx); id != nil {
|
||
|
identity := id.Identity()
|
||
|
match := mercuryPolicy(identity)
|
||
|
if search.Match(match) {
|
||
|
lis = append(lis, &mercury.Space{Space: match, Tags: []string{"RO"}})
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Objects returns nil
|
||
|
func (app *mercuryEnviron) GetConfig(ctx context.Context, search mercury.NamespaceSearch, _ *rsql.Program, _ []string) (lis mercury.Config, err error) {
|
||
|
if search.Match(mercurySource) {
|
||
|
for _, s := range app.cfg.ToArray() {
|
||
|
if search.Match(s.Space) {
|
||
|
lis = append(lis, s)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if search.Match(mercuryPriority) {
|
||
|
space := mercury.Space{
|
||
|
Space: mercuryPriority,
|
||
|
Tags: []string{"RO"},
|
||
|
}
|
||
|
|
||
|
// for i, key := range mercury.Registry {
|
||
|
// space.List = append(space.List, mercury.Value{
|
||
|
// Space: appDotPriority,
|
||
|
// Seq: uint64(i),
|
||
|
// Name: key.Match,
|
||
|
// Values: []string{fmt.Sprint(key.Priority)},
|
||
|
// })
|
||
|
// }
|
||
|
|
||
|
lis = append(lis, &space)
|
||
|
}
|
||
|
|
||
|
if search.Match(mercuryHost) {
|
||
|
if usr, err := user.Current(); err == nil {
|
||
|
space := mercury.Space{
|
||
|
Space: mercuryHost,
|
||
|
Tags: []string{"RO"},
|
||
|
}
|
||
|
|
||
|
hostname, _ := os.Hostname()
|
||
|
wd, _ := os.Getwd()
|
||
|
grp, _ := usr.GroupIds()
|
||
|
space.List = []mercury.Value{
|
||
|
{
|
||
|
Space: mercuryHost,
|
||
|
Seq: 1,
|
||
|
Name: "hostname",
|
||
|
Values: []string{hostname},
|
||
|
},
|
||
|
{
|
||
|
Space: mercuryHost,
|
||
|
Seq: 2,
|
||
|
Name: "username",
|
||
|
Values: []string{usr.Username},
|
||
|
},
|
||
|
{
|
||
|
Space: mercuryHost,
|
||
|
Seq: 3,
|
||
|
Name: "uid",
|
||
|
Values: []string{usr.Uid},
|
||
|
},
|
||
|
{
|
||
|
Space: mercuryHost,
|
||
|
Seq: 4,
|
||
|
Name: "gid",
|
||
|
Values: []string{usr.Gid},
|
||
|
},
|
||
|
{
|
||
|
Space: mercuryHost,
|
||
|
Seq: 5,
|
||
|
Name: "display",
|
||
|
Values: []string{usr.Name},
|
||
|
},
|
||
|
{
|
||
|
Space: mercuryHost,
|
||
|
Seq: 6,
|
||
|
Name: "home",
|
||
|
Values: []string{usr.HomeDir},
|
||
|
},
|
||
|
{
|
||
|
Space: mercuryHost,
|
||
|
Seq: 7,
|
||
|
Name: "groups",
|
||
|
Values: grp,
|
||
|
},
|
||
|
{
|
||
|
Space: mercuryHost,
|
||
|
Seq: 8,
|
||
|
Name: "pid",
|
||
|
Values: []string{fmt.Sprintf("%v", os.Getpid())},
|
||
|
},
|
||
|
{
|
||
|
Space: mercuryHost,
|
||
|
Seq: 9,
|
||
|
Name: "wd",
|
||
|
Values: []string{wd},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
lis = append(lis, &space)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if search.Match(appDotEnviron) {
|
||
|
env := os.Environ()
|
||
|
space := mercury.Space{
|
||
|
Space: appDotEnviron,
|
||
|
Tags: []string{"RO"},
|
||
|
}
|
||
|
|
||
|
sort.Strings(env)
|
||
|
for i, s := range env {
|
||
|
key, val, _ := strings.Cut(s, "=")
|
||
|
|
||
|
vals := []string{val}
|
||
|
if strings.Contains(key, "PATH") || strings.Contains(key, "XDG") {
|
||
|
vals = strings.Split(val, ":")
|
||
|
}
|
||
|
|
||
|
space.List = append(space.List, mercury.Value{
|
||
|
Space: appDotEnviron,
|
||
|
Seq: uint64(i),
|
||
|
Name: key,
|
||
|
Values: vals,
|
||
|
})
|
||
|
}
|
||
|
lis = append(lis, &space)
|
||
|
}
|
||
|
|
||
|
if id := ident.FromContext(ctx); id != nil {
|
||
|
identity := id.Identity()
|
||
|
groups := groups(identity, &app.cfg)
|
||
|
match := mercuryPolicy(identity)
|
||
|
if search.Match(match) {
|
||
|
space := &mercury.Space{
|
||
|
Space: match,
|
||
|
Tags: []string{"RO"},
|
||
|
}
|
||
|
|
||
|
lis = append(lis, space)
|
||
|
rules, err := app.lookup(ctx, id)
|
||
|
if err != nil {
|
||
|
space.AddNotes(err.Error())
|
||
|
} else {
|
||
|
k := mercury.NewValue("groups")
|
||
|
k.AddValues(strings.Join(groups.Values(), " "))
|
||
|
space.AddKeys(k)
|
||
|
|
||
|
k = mercury.NewValue("rules")
|
||
|
for _, r := range rules {
|
||
|
k.AddValues(strings.Join([]string{r.Role, r.Type, r.Match}, " "))
|
||
|
}
|
||
|
space.AddKeys(k)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Rules returns nil
|
||
|
func (app *mercuryEnviron) GetRules(ctx context.Context, id ident.Ident) (lis mercury.Rules, err error) {
|
||
|
identity := id.Identity()
|
||
|
|
||
|
lis = append(lis,
|
||
|
mercury.Rule{
|
||
|
Role: "read",
|
||
|
Type: "NS",
|
||
|
Match: mercuryPolicy(identity),
|
||
|
},
|
||
|
)
|
||
|
|
||
|
groups := groups(identity, &app.cfg)
|
||
|
|
||
|
if u, ok := id.(hasRole); groups.Has("admin") || ok && u.HasRole("admin") {
|
||
|
lis = append(lis,
|
||
|
mercury.Rule{
|
||
|
Role: "read",
|
||
|
Type: "NS",
|
||
|
Match: mercurySource,
|
||
|
},
|
||
|
mercury.Rule{
|
||
|
Role: "read",
|
||
|
Type: "NS",
|
||
|
Match: mercuryPriority,
|
||
|
},
|
||
|
mercury.Rule{
|
||
|
Role: "read",
|
||
|
Type: "NS",
|
||
|
Match: mercuryHost,
|
||
|
},
|
||
|
mercury.Rule{
|
||
|
Role: "read",
|
||
|
Type: "NS",
|
||
|
Match: appDotEnviron,
|
||
|
},
|
||
|
)
|
||
|
}
|
||
|
|
||
|
return lis, nil
|
||
|
}
|
||
|
|
||
|
func groups(identity string, cfg *mercury.SpaceMap) set.Set[string] {
|
||
|
groups := set.New[string]()
|
||
|
if s, ok := cfg.Space("mercury.groups"); ok {
|
||
|
for _, g := range s.List {
|
||
|
for _, v := range g.Values {
|
||
|
for _, u := range strings.Fields(v) {
|
||
|
if u == identity {
|
||
|
groups.Add(g.Name)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return groups
|
||
|
}
|