mirror of
https://github.com/roddhjav/apparmor.d.git
synced 2024-11-14 23:43:56 +01:00
feat(aa): add a string method to all rule struct.
This commit is contained in:
parent
e9fa0660f8
commit
5483668574
@ -42,14 +42,9 @@ func NewAppArmorProfile() *AppArmorProfileFile {
|
||||
return &AppArmorProfileFile{}
|
||||
}
|
||||
|
||||
// String returns the formatted representation of a profile as a string
|
||||
// String returns the formatted representation of a profile file as a string
|
||||
func (f *AppArmorProfileFile) String() string {
|
||||
var res bytes.Buffer
|
||||
err := tmpl["apparmor"].Execute(&res, f)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return res.String()
|
||||
return renderTemplate("apparmor", f)
|
||||
}
|
||||
|
||||
// GetDefaultProfile ensure a profile is always present in the profile file and
|
||||
|
@ -5,6 +5,8 @@
|
||||
package aa
|
||||
|
||||
|
||||
const tokCAPABILITY = "capability"
|
||||
|
||||
type Capability struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
@ -36,4 +38,6 @@ func (r *Capability) Equals(other any) bool {
|
||||
return slices.Equal(r.Names, o.Names) && r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *Capability) String() string {
|
||||
return renderTemplate(tokCAPABILITY, r)
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
package aa
|
||||
|
||||
const tokCHANGEPROFILE = "change_profile"
|
||||
|
||||
type ChangeProfile struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
@ -41,3 +43,7 @@ func (r *ChangeProfile) Equals(other any) bool {
|
||||
return r.ExecMode == o.ExecMode && r.Exec == o.Exec &&
|
||||
r.ProfileName == o.ProfileName && r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *ChangeProfile) String() string {
|
||||
return renderTemplate(tokCHANGEPROFILE, r)
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
package aa
|
||||
|
||||
const tokDBUS = "dbus"
|
||||
|
||||
type Dbus struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
@ -77,3 +79,7 @@ func (r *Dbus) Equals(other any) bool {
|
||||
r.Member == o.Member && r.PeerName == o.PeerName &&
|
||||
r.PeerLabel == o.PeerLabel && r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *Dbus) String() string {
|
||||
return renderTemplate(tokDBUS, r)
|
||||
}
|
||||
|
@ -59,5 +59,9 @@ func (r *File) Equals(other any) bool {
|
||||
r.Target == o.Target && r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *File) String() string {
|
||||
return renderTemplate("file", r)
|
||||
}
|
||||
|
||||
r.Target == o.Target && r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
package aa
|
||||
|
||||
const tokIOURING = "io_uring"
|
||||
|
||||
|
||||
type IOUring struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
@ -36,4 +39,6 @@ func (r *IOUring) Equals(other any) bool {
|
||||
return slices.Equal(r.Access, o.Access) && r.Label == o.Label && r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *IOUring) String() string {
|
||||
return renderTemplate(tokIOURING, r)
|
||||
}
|
||||
|
@ -8,6 +8,13 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
tokMOUNT = "mount"
|
||||
tokREMOUNT = "remount"
|
||||
tokUMOUNT = "umount"
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
type MountConditions struct {
|
||||
@ -75,6 +82,10 @@ func (r *Mount) Equals(other any) bool {
|
||||
r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *Mount) String() string {
|
||||
return renderTemplate(tokMOUNT, r)
|
||||
}
|
||||
|
||||
type Umount struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
@ -109,6 +120,10 @@ func (r *Umount) Equals(other any) bool {
|
||||
r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *Umount) String() string {
|
||||
return renderTemplate(tokUMOUNT, r)
|
||||
}
|
||||
|
||||
type Remount struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
@ -142,3 +157,7 @@ func (r *Remount) Equals(other any) bool {
|
||||
r.MountConditions.Equals(o.MountConditions) &&
|
||||
r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *Remount) String() string {
|
||||
return renderTemplate(tokREMOUNT, r)
|
||||
}
|
||||
|
@ -8,6 +8,9 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const tokMQUEUE = "mqueue"
|
||||
|
||||
|
||||
type Mqueue struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
@ -53,3 +56,7 @@ func (r *Mqueue) Equals(other any) bool {
|
||||
return slices.Equal(r.Access, o.Access) && r.Type == o.Type && r.Label == o.Label &&
|
||||
r.Name == o.Name && r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *Mqueue) String() string {
|
||||
return renderTemplate(tokMQUEUE, r)
|
||||
}
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
package aa
|
||||
|
||||
const tokNETWORK = "network"
|
||||
|
||||
|
||||
type AddressExpr struct {
|
||||
Source string
|
||||
Destination string
|
||||
@ -76,3 +79,7 @@ func (r *Network) Equals(other any) bool {
|
||||
r.Protocol == o.Protocol && r.AddressExpr.Equals(o.AddressExpr) &&
|
||||
r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *Network) String() string {
|
||||
return renderTemplate(tokNETWORK, r)
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
package aa
|
||||
|
||||
const tokPIVOTROOT = "pivot_root"
|
||||
|
||||
type PivotRoot struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
@ -42,3 +44,7 @@ func (r *PivotRoot) Equals(other any) bool {
|
||||
r.TargetProfile == o.TargetProfile &&
|
||||
r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *PivotRoot) String() string {
|
||||
return renderTemplate(tokPIVOTROOT, r)
|
||||
}
|
||||
|
@ -6,6 +6,12 @@ package aa
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
const (
|
||||
tokABI = "abi"
|
||||
tokALIAS = "alias"
|
||||
tokINCLUDE = "include"
|
||||
tokIFEXISTS = "if exists"
|
||||
)
|
||||
|
||||
type Abi struct {
|
||||
@ -27,6 +33,10 @@ func (r *Abi) Equals(other any) bool {
|
||||
return r.Path == o.Path && r.IsMagic == o.IsMagic
|
||||
}
|
||||
|
||||
func (r *Abi) String() string {
|
||||
return renderTemplate(tokABI, r)
|
||||
}
|
||||
|
||||
type Alias struct {
|
||||
RuleBase
|
||||
Path string
|
||||
@ -46,6 +56,10 @@ func (r Alias) Equals(other any) bool {
|
||||
return r.Path == o.Path && r.RewrittenPath == o.RewrittenPath
|
||||
}
|
||||
|
||||
func (r *Alias) String() string {
|
||||
return renderTemplate(tokALIAS, r)
|
||||
}
|
||||
|
||||
type Include struct {
|
||||
RuleBase
|
||||
IfExists bool
|
||||
@ -69,6 +83,10 @@ func (r *Include) Equals(other any) bool {
|
||||
return r.Path == o.Path && r.IsMagic == o.IsMagic && r.IfExists == o.IfExists
|
||||
}
|
||||
|
||||
func (r *Include) String() string {
|
||||
return renderTemplate(tokINCLUDE, r)
|
||||
}
|
||||
|
||||
type Variable struct {
|
||||
RuleBase
|
||||
Name string
|
||||
@ -90,3 +108,7 @@ func (r *Variable) Equals(other any) bool {
|
||||
o, _ := other.(*Variable)
|
||||
return r.Name == o.Name && slices.Equal(r.Values, o.Values)
|
||||
}
|
||||
|
||||
func (r *Variable) String() string {
|
||||
return renderTemplate("variable", r)
|
||||
}
|
||||
|
@ -10,6 +10,12 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
tokATTRIBUTES = "xattrs"
|
||||
tokFLAGS = "flags"
|
||||
tokPROFILE = "profile"
|
||||
)
|
||||
|
||||
// Profile represents a single AppArmor profile.
|
||||
type Profile struct {
|
||||
RuleBase
|
||||
@ -40,6 +46,11 @@ func (p *Profile) Equals(other any) bool {
|
||||
slices.Equal(p.Flags, o.Flags)
|
||||
}
|
||||
|
||||
func (p *Profile) String() string {
|
||||
return renderTemplate(tokPROFILE, p)
|
||||
}
|
||||
|
||||
|
||||
// AddRule adds a new rule to the profile from a log map.
|
||||
func (p *Profile) AddRule(log map[string]string) {
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
package aa
|
||||
|
||||
const tokPTRACE = "ptrace"
|
||||
|
||||
type Ptrace struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
@ -36,3 +38,7 @@ func (r *Ptrace) Equals(other any) bool {
|
||||
return slices.Equal(r.Access, o.Access) && r.Peer == o.Peer &&
|
||||
r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *Ptrace) String() string {
|
||||
return renderTemplate(tokPTRACE, r)
|
||||
}
|
||||
|
@ -4,6 +4,12 @@
|
||||
|
||||
package aa
|
||||
|
||||
const (
|
||||
tokRLIMIT = "rlimit"
|
||||
tokSET = "set"
|
||||
)
|
||||
|
||||
|
||||
type Rlimit struct {
|
||||
RuleBase
|
||||
Key string
|
||||
@ -35,3 +41,7 @@ func (r *Rlimit) Equals(other any) bool {
|
||||
o, _ := other.(*Rlimit)
|
||||
return r.Key == o.Key && r.Op == o.Op && r.Value == o.Value
|
||||
}
|
||||
|
||||
func (r *Rlimit) String() string {
|
||||
return renderTemplate(tokRLIMIT, r)
|
||||
}
|
||||
|
@ -8,14 +8,27 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
tokALL = "all"
|
||||
tokALLOW = "allow"
|
||||
tokAUDIT = "audit"
|
||||
tokDENY = "deny"
|
||||
)
|
||||
|
||||
// Rule generic interface for all AppArmor rules
|
||||
type Rule interface {
|
||||
Less(other any) bool
|
||||
Equals(other any) bool
|
||||
String() string
|
||||
}
|
||||
|
||||
type Rules []Rule
|
||||
|
||||
func (r Rules) String() string {
|
||||
return renderTemplate("rules", r)
|
||||
}
|
||||
|
||||
|
||||
type RuleBase struct {
|
||||
Comment string
|
||||
NoNewPrivs bool
|
||||
@ -69,6 +82,10 @@ func (r RuleBase) Equals(other any) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r RuleBase) String() string {
|
||||
return renderTemplate("comment", r)
|
||||
}
|
||||
|
||||
type Qualifier struct {
|
||||
Audit bool
|
||||
AccessType string
|
||||
@ -104,3 +121,7 @@ func (r *All) Less(other any) bool {
|
||||
func (r *All) Equals(other any) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *All) String() string {
|
||||
return renderTemplate(tokALL, r)
|
||||
}
|
||||
|
@ -367,3 +367,113 @@ func TestRule_Equals(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRule_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rule Rule
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "include1",
|
||||
rule: include1,
|
||||
want: "include <abstraction/base>",
|
||||
},
|
||||
{
|
||||
name: "include-local",
|
||||
rule: includeLocal1,
|
||||
want: "include if exists <local/foo>",
|
||||
},
|
||||
{
|
||||
name: "include-abs",
|
||||
rule: &Include{Path: "/usr/share/apparmor.d/", IsMagic: false},
|
||||
want: `include "/usr/share/apparmor.d/"`,
|
||||
},
|
||||
{
|
||||
name: "rlimit",
|
||||
rule: rlimit1,
|
||||
want: "set rlimit nproc <= 200,",
|
||||
},
|
||||
{
|
||||
name: "capability",
|
||||
rule: capability1,
|
||||
want: "capability net_admin,",
|
||||
},
|
||||
{
|
||||
name: "capability/multi",
|
||||
rule: &Capability{Names: []string{"dac_override", "dac_read_search"}},
|
||||
want: "capability dac_override dac_read_search,",
|
||||
},
|
||||
{
|
||||
name: "capability/all",
|
||||
rule: &Capability{},
|
||||
want: "capability,",
|
||||
},
|
||||
{
|
||||
name: "network",
|
||||
rule: network1,
|
||||
want: "network netlink raw,",
|
||||
},
|
||||
{
|
||||
name: "mount",
|
||||
rule: mount1,
|
||||
want: "mount fstype=overlay overlay -> /var/lib/docker/overlay2/opaque-bug-check1209538631/merged/, # failed perms check",
|
||||
},
|
||||
{
|
||||
name: "pivot_root",
|
||||
rule: pivotroot1,
|
||||
want: "pivot_root oldroot=@{run}/systemd/mount-rootfs/ @{run}/systemd/mount-rootfs/,",
|
||||
},
|
||||
{
|
||||
name: "change_profile",
|
||||
rule: changeprofile1,
|
||||
want: "change_profile -> systemd-user,",
|
||||
},
|
||||
{
|
||||
name: "signal",
|
||||
rule: signal1,
|
||||
want: "signal receive set=kill peer=firefox//&firejail-default,",
|
||||
},
|
||||
{
|
||||
name: "ptrace",
|
||||
rule: ptrace1,
|
||||
want: "ptrace read peer=nautilus,",
|
||||
},
|
||||
{
|
||||
name: "unix",
|
||||
rule: unix1,
|
||||
want: "unix (receive send) type=stream protocol=0 addr=none peer=(label=dbus-daemon, addr=@/tmp/dbus-AaKMpxzC4k),",
|
||||
},
|
||||
{
|
||||
name: "dbus",
|
||||
rule: dbus1,
|
||||
want: `dbus receive bus=session path=/org/gtk/vfs/metadata
|
||||
interface=org.gtk.vfs.Metadata
|
||||
member=Remove
|
||||
peer=(name=:1.15, label=tracker-extract),`,
|
||||
},
|
||||
{
|
||||
name: "dbus-bind",
|
||||
rule: &Dbus{Access: []string{"bind"}, Bus: "session", Name: "org.gnome.*"},
|
||||
want: `dbus bind bus=session name=org.gnome.*,`,
|
||||
},
|
||||
{
|
||||
name: "dbus-full",
|
||||
rule: &Dbus{Bus: "accessibility"},
|
||||
want: `dbus bus=accessibility,`,
|
||||
},
|
||||
{
|
||||
name: "file",
|
||||
rule: file1,
|
||||
want: "/usr/share/poppler/cMap/Identity-H r,",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := tt.rule
|
||||
if got := r.String(); got != tt.want {
|
||||
t.Errorf("Rule.String() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
package aa
|
||||
|
||||
|
||||
const tokSIGNAL = "signal"
|
||||
|
||||
type Signal struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
@ -41,3 +44,7 @@ func (r *Signal) Equals(other any) bool {
|
||||
return slices.Equal(r.Access, o.Access) && slices.Equal(r.Set, o.Set) &&
|
||||
r.Peer == o.Peer && r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *Signal) String() string {
|
||||
return renderTemplate(tokSIGNAL, r)
|
||||
}
|
||||
|
@ -8,29 +8,40 @@ import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// Default indentation for apparmor profile (2 spaces)
|
||||
const indentation = " "
|
||||
|
||||
var (
|
||||
// Default indentation for apparmor profile (2 spaces)
|
||||
TemplateIndentation = " "
|
||||
|
||||
// The current indentation level
|
||||
TemplateIndentationLevel = 0
|
||||
|
||||
//go:embed templates/*.j2
|
||||
//go:embed templates/rule/*.j2
|
||||
tmplFiles embed.FS
|
||||
|
||||
// The functions available in the template
|
||||
tmplFunctionMap = template.FuncMap{
|
||||
"typeof": typeOf,
|
||||
"join": join,
|
||||
"cjoin": cjoin,
|
||||
"indent": indent,
|
||||
"overindent": indentDbus,
|
||||
"setindent": setindent,
|
||||
}
|
||||
|
||||
// The apparmor templates
|
||||
tmpl = map[string]*template.Template{
|
||||
"apparmor": generateTemplate("apparmor.j2"),
|
||||
}
|
||||
tmpl = generateTemplates([]string{
|
||||
"apparmor", tokPROFILE, "rules", // Global templates
|
||||
tokINCLUDE, tokRLIMIT, tokCAPABILITY, tokNETWORK,
|
||||
tokMOUNT, tokPIVOTROOT, tokCHANGEPROFILE, tokSIGNAL,
|
||||
tokPTRACE, tokUNIX, tokUSERNS, tokIOURING,
|
||||
tokDBUS, "file",
|
||||
})
|
||||
|
||||
// convert apparmor requested mask to apparmor access mode
|
||||
maskToAccess = map[string]string{
|
||||
@ -90,30 +101,35 @@ var (
|
||||
fileWeights = map[string]int{}
|
||||
)
|
||||
|
||||
func generateTemplate(name string) *template.Template {
|
||||
res := template.New(name).Funcs(tmplFunctionMap)
|
||||
switch name {
|
||||
case "apparmor.j2":
|
||||
res = template.Must(res.ParseFS(tmplFiles,
|
||||
"templates/*.j2", "templates/rule/*.j2",
|
||||
))
|
||||
case "profile.j2":
|
||||
res = template.Must(res.Parse("{{ template \"profile\" . }}"))
|
||||
res = template.Must(res.ParseFS(tmplFiles,
|
||||
"templates/profile.j2", "templates/rule/*.j2",
|
||||
))
|
||||
default:
|
||||
res = template.Must(res.Parse(
|
||||
fmt.Sprintf("{{ template \"%s\" . }}", name),
|
||||
))
|
||||
res = template.Must(res.ParseFS(tmplFiles,
|
||||
fmt.Sprintf("templates/rule/%s.j2", name),
|
||||
"templates/rule/qualifier.j2", "templates/rule/comment.j2",
|
||||
func generateTemplates(names []string) map[string]*template.Template {
|
||||
res := make(map[string]*template.Template, len(names))
|
||||
base := template.New("").Funcs(tmplFunctionMap)
|
||||
base = template.Must(base.ParseFS(tmplFiles,
|
||||
"templates/*.j2", "templates/rule/*.j2",
|
||||
))
|
||||
for _, name := range names {
|
||||
t := template.Must(base.Clone())
|
||||
t = template.Must(t.Parse(
|
||||
fmt.Sprintf(`{{- template "%s" . -}}`, name),
|
||||
))
|
||||
res[name] = t
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func renderTemplate(name string, data any) string {
|
||||
var res strings.Builder
|
||||
template, ok := tmpl[name]
|
||||
if !ok {
|
||||
panic("template not found")
|
||||
}
|
||||
err := template.Execute(&res, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return res.String()
|
||||
}
|
||||
|
||||
func init() {
|
||||
for i, r := range fileAlphabet {
|
||||
fileWeights[r] = i
|
||||
@ -138,6 +154,25 @@ func join(i any) string {
|
||||
}
|
||||
}
|
||||
|
||||
func cjoin(i any) string {
|
||||
switch reflect.TypeOf(i).Kind() {
|
||||
case reflect.Slice:
|
||||
s := i.([]string)
|
||||
if len(s) == 1 {
|
||||
return s[0]
|
||||
}
|
||||
return "(" + strings.Join(s, " ") + ")"
|
||||
case reflect.Map:
|
||||
res := []string{}
|
||||
for k, v := range i.(map[string]string) {
|
||||
res = append(res, k+"="+v)
|
||||
}
|
||||
return "(" + strings.Join(res, " ") + ")"
|
||||
default:
|
||||
return i.(string)
|
||||
}
|
||||
}
|
||||
|
||||
func typeOf(i any) string {
|
||||
return strings.TrimPrefix(reflect.TypeOf(i).String(), "*aa.")
|
||||
}
|
||||
@ -146,12 +181,22 @@ func typeToValue(i reflect.Type) string {
|
||||
return strings.ToLower(strings.TrimPrefix(i.String(), "*aa."))
|
||||
}
|
||||
|
||||
func setindent(i string) string {
|
||||
switch i {
|
||||
case "++":
|
||||
TemplateIndentationLevel++
|
||||
case "--":
|
||||
TemplateIndentationLevel--
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func indent(s string) string {
|
||||
return indentation + s
|
||||
return strings.Repeat(TemplateIndentation, TemplateIndentationLevel) + s
|
||||
}
|
||||
|
||||
func indentDbus(s string) string {
|
||||
return indentation + " " + s
|
||||
return strings.Join([]string{TemplateIndentation, s}, " ")
|
||||
}
|
||||
|
||||
func getLetterIn(alphabet []string, in string) string {
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
package aa
|
||||
|
||||
const tokUNIX = "unix"
|
||||
|
||||
type Unix struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
@ -74,3 +76,7 @@ func (r *Unix) Equals(other any) bool {
|
||||
r.PeerLabel == o.PeerLabel && r.PeerAddr == o.PeerAddr &&
|
||||
r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *Unix) String() string {
|
||||
return renderTemplate(tokUNIX, r)
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
package aa
|
||||
|
||||
const tokUSERNS = "userns"
|
||||
|
||||
type Userns struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
@ -30,3 +32,7 @@ func (r *Userns) Equals(other any) bool {
|
||||
o, _ := other.(*Userns)
|
||||
return r.Create == o.Create && r.Qualifier.Equals(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *Userns) String() string {
|
||||
return renderTemplate(tokUSERNS, r)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user