Skip to content

Instantly share code, notes, and snippets.

@rsperl
Last active July 25, 2022 15:55
Show Gist options
  • Save rsperl/d3e63b07b47d37af28b992f5d51cefc5 to your computer and use it in GitHub Desktop.
Save rsperl/d3e63b07b47d37af28b992f5d51cefc5 to your computer and use it in GitHub Desktop.
manage bitmasks #go #ldap #snippet
package bitmask
import "sort"
type Decoder struct {
Flags map[string]int
}
func NewDecoder(flags map[string]int) *Decoder {
return &Decoder{Flags: flags}
}
func (d *Decoder) GetFlags(b int) []string {
var flags []string
for n, v := range d.Flags {
if b&v != 0 {
flags = append(flags, n)
}
}
sort.Strings(flags)
return flags
}
func (d *Decoder) HasFlag(b int, flag string) (bool, bool) {
if val, ok := d.Flags[flag]; ok {
return b&val != 0, ok
}
return false, false
}
func (d *Decoder) HasFlags(b int, flags []string) (bool, bool) {
for _, f := range flags {
if val, ok := d.HasFlag(b, f); !val || !ok {
return val, ok
}
}
return true, true
}
func (d *Decoder) AddFlagsByName(b int, flags []string) (int, bool) {
for _, f := range flags {
if val, ok := d.Flags[f]; ok {
b |= val
} else {
return -1, false
}
}
return b, true
}
func (d *Decoder) RemoveFlagsByName(b int, flags []string) (int, bool) {
for _, f := range flags {
if val, ok := d.Flags[f]; ok {
b &^= val
} else {
return -1, false
}
}
return b, true
}
package bitmask_test
import (
"fmt"
"testing"
"gitlab.sas.com/risugg/go-bitmask"
)
var flags = make(map[string]int)
func init() {
flags["Flag01"] = 1
flags["Flag02"] = 2
flags["Flag04"] = 4
flags["Flag08"] = 8
flags["Flag16"] = 16
flags["Flag32"] = 32
flags["Flag64"] = 64
}
type GetFlagsCase struct {
UacValue int
Flags []string
}
var casesGotFlags = []GetFlagsCase{
GetFlagsCase{
UacValue: 3,
Flags: []string{"Flag01", "Flag02"},
},
GetFlagsCase{
UacValue: 25,
Flags: []string{"Flag01", "Flag08", "Flag16"},
},
GetFlagsCase{
UacValue: 57,
Flags: []string{"Flag01", "Flag08", "Flag16", "Flag32"},
},
}
func TestGetFlags(t *testing.T) {
decoder := bitmask.NewDecoder(flags)
for _, c := range casesGotFlags {
gotFlags := decoder.GetFlags(c.UacValue)
if !cmpSlices(gotFlags, c.Flags) {
t.Error(getError("got flags doesn't match exp flags", gotFlags, c.Flags))
}
if yes, ok := decoder.HasFlags(c.UacValue, c.Flags); !ok || !yes {
t.Error("HasFlags failed")
}
}
}
type FlagsByNameCase struct {
OriginalBitmask int
Flags []string
NewBitmask int
}
var casesAddFlagsByName = []FlagsByNameCase{
FlagsByNameCase{
OriginalBitmask: 25,
Flags: []string{"Flag32", "Flag64"},
NewBitmask: 121,
},
FlagsByNameCase{
OriginalBitmask: 1,
Flags: []string{"Flag32", "Flag32", "Flag64", "Flag02"},
NewBitmask: 99,
},
}
func TestNewDecoder(t *testing.T) {
decoder := bitmask.NewDecoder(flags)
fmt.Printf("%v\n", decoder)
}
func TestHasFlag(t *testing.T) {
decoder := bitmask.NewDecoder(flags)
if val, ok := decoder.HasFlag(24, "Flag08"); !ok || !val {
t.Error("HasFlag failed")
}
if val, ok := decoder.HasFlag(24, "Flag01"); !ok || val {
t.Error("HasFlag failed")
}
}
func TestHasFlags(t *testing.T) {
decoder := bitmask.NewDecoder(flags)
if val, ok := decoder.HasFlags(24, []string{"Flag08", "Flag16"}); !ok || !val {
t.Errorf("HasFlags expected true, true: val=%v, ok=%v", val, ok)
}
if val, ok := decoder.HasFlags(24, []string{"Flag02", "Flag08", "Flag16"}); !ok || val {
t.Error("HasFlags expected false, true: val=%v, ok=%v", val, ok)
}
}
func TestAddFlagsByName(t *testing.T) {
decoder := bitmask.NewDecoder(flags)
for _, c := range casesAddFlagsByName {
gotBitmask, ok := decoder.AddFlagsByName(c.OriginalBitmask, c.Flags)
if !ok {
t.Error("invalid flag passed")
}
if gotBitmask != c.NewBitmask {
t.Error(getError("did not add bitmasks", gotBitmask, c.NewBitmask))
}
if val, ok := decoder.HasFlag(gotBitmask, c.Flags[0]); !ok || !val {
t.Error("HasFlag failed")
}
if yes, ok := decoder.HasFlags(gotBitmask, c.Flags); !ok || !yes {
t.Error("HasFlags failed")
}
gotBitmask, ok = decoder.RemoveFlagsByName(gotBitmask, c.Flags)
if !ok {
t.Error("tried to remove an invalid flag")
}
if gotBitmask != c.OriginalBitmask {
t.Error(getError("did not remove bitmasks", gotBitmask, c.OriginalBitmask))
}
}
}
func TestRemoveFlagsByName(t *testing.T) {
decoder := bitmask.NewDecoder(flags)
val, ok := decoder.RemoveFlagsByName(24, []string{"Flag08"})
if !ok || val != 16 {
t.Errorf("failed to remove flag by name: val=%d, ok=%v", val, ok)
}
}
func getError(msg string, got interface{}, exp interface{}) string {
msg = msg + "\ngot: %v\nexp: %v\n"
return fmt.Sprintf(msg, got, exp)
}
func cmpSlices(a []string, b []string) bool {
if a == nil && b == nil {
return true
}
if len(a) != len(b) {
return false
}
for i, _ := range a {
if a[i] != b[i] {
return false
}
}
return true
}
package uac
import "gitlab.sas.com/risugg/go-bitmask"
type UacDecoder struct {
bitmask.Decoder
}
func NewUacDecoder() *UacDecoder {
f := make(map[string]int)
f["NO_AUTH_DATA_REQUIRED"] = 33554432
f["PARTIAL_SECRETS_ACCOUNT"] = 67108864
f["TRUSTED_TO_AUTH_FOR_DELEGATION"] = 16777216
f["PASSWORD_EXPIRED"] = 8388608
f["DONT_REQ_PREAUTH"] = 4194304
f["USE_DES_KEY_ONLY"] = 2097152
f["NOT_DELEGATED"] = 1048576
f["TRUSTED_FOR_DELEGATION"] = 524288
f["SMARTCARD_REQUIRED"] = 262144
f["MNS_LOGON_ACCOUNT"] = 131072
f["DONT_EXPIRE_PASSWORD"] = 65536
f["SERVER_TRUST_ACCOUNT"] = 8192
f["WORKSTATION_TRUST_ACCOUNT"] = 4096
f["INTERDOMAIN_TRUST_ACCOUNT"] = 2048
f["NORMAL_ACCOUNT"] = 512
f["TEMP_DUPLICATE_ACCOUNT"] = 256
f["ENCRYPTED_TEXT_PWD_ALLOWED"] = 128
f["PASSWD_CANT_CHANGE"] = 64
f["PASSWD_NOTREQD"] = 32
f["LOCKOUT"] = 16
f["HOMEDIR_REQUIRED"] = 8
f["ACCOUNTDISABLE"] = 2
f["SCRIPT"] = 1
return &UacDecoder{*bitmask.NewDecoder(f)}
}
func (u *UacDecoder) IsDisabled(b int) (bool, bool) {
return u.HasFlag(b, "ACCOUNTDISABLE")
}
package uac_test
import (
"testing"
"gitlab.sas.com/risugg/go-uac"
)
func TestIsDisabled(t *testing.T) {
decoder := uac.NewUacDecoder()
if val, ok := decoder.IsDisabled(512); !ok || val {
t.Error("512 should not be reported as disabled")
}
if val, ok := decoder.IsDisabled(514); !ok || !val {
t.Error("514 should be reported as disabled")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment