mirror of
https://github.com/postmannen/ctrl.git
synced 2025-03-05 06:46:48 +00:00
added auth concept code to actual code
This commit is contained in:
parent
59f2b137f5
commit
e782b27b09
4 changed files with 1177 additions and 0 deletions
|
@ -14,6 +14,7 @@ import (
|
||||||
// centralAuth holds the logic related to handling public keys and auth maps.
|
// centralAuth holds the logic related to handling public keys and auth maps.
|
||||||
type centralAuth struct {
|
type centralAuth struct {
|
||||||
// schema map[Node]map[argsString]signatureBase32
|
// schema map[Node]map[argsString]signatureBase32
|
||||||
|
authorization *authorization
|
||||||
nodePublicKeys *nodePublicKeys
|
nodePublicKeys *nodePublicKeys
|
||||||
nodeNotAckedPublicKeys *nodeNotAckedPublicKeys
|
nodeNotAckedPublicKeys *nodeNotAckedPublicKeys
|
||||||
configuration *Configuration
|
configuration *Configuration
|
||||||
|
@ -25,6 +26,7 @@ type centralAuth struct {
|
||||||
// newCentralAuth will return a prepared *centralAuth with input values set.
|
// newCentralAuth will return a prepared *centralAuth with input values set.
|
||||||
func newCentralAuth(configuration *Configuration, errorKernel *errorKernel) *centralAuth {
|
func newCentralAuth(configuration *Configuration, errorKernel *errorKernel) *centralAuth {
|
||||||
c := centralAuth{
|
c := centralAuth{
|
||||||
|
authorization: newAuthorization(),
|
||||||
// schema: make(map[Node]map[argsString]signatureBase32),
|
// schema: make(map[Node]map[argsString]signatureBase32),
|
||||||
nodePublicKeys: newNodePublicKeys(configuration),
|
nodePublicKeys: newNodePublicKeys(configuration),
|
||||||
nodeNotAckedPublicKeys: newNodeNotAckedPublicKeys(configuration),
|
nodeNotAckedPublicKeys: newNodeNotAckedPublicKeys(configuration),
|
||||||
|
|
543
central_auth_acl_handling.go
Normal file
543
central_auth_acl_handling.go
Normal file
|
@ -0,0 +1,543 @@
|
||||||
|
package steward
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/fxamacker/cbor/v2"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
// // centralAuth
|
||||||
|
// type centralAuth struct {
|
||||||
|
// authorization *authorization
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // newCentralAuth
|
||||||
|
// func newCentralAuth() *centralAuth {
|
||||||
|
// c := centralAuth{
|
||||||
|
// authorization: newAuthorization(),
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return &c
|
||||||
|
// }
|
||||||
|
|
||||||
|
// --------------------------------------
|
||||||
|
|
||||||
|
type authorization struct {
|
||||||
|
authSchema *authSchema
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAuthorization() *authorization {
|
||||||
|
a := authorization{
|
||||||
|
authSchema: newAuthSchema(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
// authSchema holds both the main schema to update by operators,
|
||||||
|
// and also the indvidual node generated data based on the main schema.
|
||||||
|
type authSchema struct {
|
||||||
|
// Holds the editable structures for ACL handling.
|
||||||
|
schemaMain *schemaMain
|
||||||
|
// Holds the generated based on the editable structures for ACL handling.
|
||||||
|
schemaGenerated *schemaGenerated
|
||||||
|
validator *validator.Validate
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAuthSchema() *authSchema {
|
||||||
|
a := authSchema{
|
||||||
|
schemaMain: newSchemaMain(),
|
||||||
|
schemaGenerated: newSchemaGenerated(),
|
||||||
|
validator: validator.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
type node string
|
||||||
|
type command string
|
||||||
|
type nodeGroup string
|
||||||
|
type commandGroup string
|
||||||
|
|
||||||
|
// schemaMain is the structure that holds the user editable parts for creating ACL's.
|
||||||
|
type schemaMain struct {
|
||||||
|
ACLMap map[node]map[node]map[command]struct{}
|
||||||
|
NodeGroupMap map[nodeGroup]map[node]struct{}
|
||||||
|
CommandGroupMap map[commandGroup]map[command]struct{}
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSchemaMain() *schemaMain {
|
||||||
|
s := schemaMain{
|
||||||
|
ACLMap: make(map[node]map[node]map[command]struct{}),
|
||||||
|
NodeGroupMap: make(map[nodeGroup]map[node]struct{}),
|
||||||
|
CommandGroupMap: make(map[commandGroup]map[command]struct{}),
|
||||||
|
}
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
// schemaGenerated is the structure that holds all the generated ACL's
|
||||||
|
// to be sent to nodes.
|
||||||
|
// The ACL's here are generated from the schemaMain.ACLMap.
|
||||||
|
type schemaGenerated struct {
|
||||||
|
ACLsToConvert map[node]map[node]map[command]struct{}
|
||||||
|
GeneratedACLsMap map[node]HostACLsSerializedWithHash
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSchemaGenerated() *schemaGenerated {
|
||||||
|
s := schemaGenerated{
|
||||||
|
ACLsToConvert: map[node]map[node]map[command]struct{}{},
|
||||||
|
GeneratedACLsMap: make(map[node]HostACLsSerializedWithHash),
|
||||||
|
}
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostACLsSerializedWithHash holds the serialized representation node specific ACL's in the authSchema.
|
||||||
|
// There is also a sha256 hash of the data.
|
||||||
|
type HostACLsSerializedWithHash struct {
|
||||||
|
// data is all the ACL's for a specific node serialized.
|
||||||
|
Data []byte
|
||||||
|
// hash is the sha256 hash of the ACL's.
|
||||||
|
// With maps the order are not guaranteed, so A sorted appearance
|
||||||
|
// of the ACL map for a host node is used when creating the hash,
|
||||||
|
// so the hash stays the same unless the ACL is changed.
|
||||||
|
Hash [32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// commandAsSlice will convert the given argument into a slice representation.
|
||||||
|
// If the argument is a group, then all the members of that group will be expanded into
|
||||||
|
// the slice.
|
||||||
|
// If the argument is not a group kind of value, then only a slice with that single
|
||||||
|
// value is returned.
|
||||||
|
func (a *authSchema) nodeAsSlice(n node) []node {
|
||||||
|
nodes := []node{}
|
||||||
|
|
||||||
|
// Check if we are given a nodeGroup variable, and if we are, get all the
|
||||||
|
// nodes for that group.
|
||||||
|
if strings.HasPrefix(string(n), "grp_nodes_") {
|
||||||
|
for nd := range a.schemaMain.NodeGroupMap[nodeGroup(n)] {
|
||||||
|
nodes = append(nodes, nd)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No group found meaning a single node was given as an argument.
|
||||||
|
nodes = []node{n}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// commandAsSlice will convert the given argument into a slice representation.
|
||||||
|
// If the argument is a group, then all the members of that group will be expanded into
|
||||||
|
// the slice.
|
||||||
|
// If the argument is not a group kind of value, then only a slice with that single
|
||||||
|
// value is returned.
|
||||||
|
func (a *authSchema) commandAsSlice(c command) []command {
|
||||||
|
commands := []command{}
|
||||||
|
|
||||||
|
// Check if we are given a nodeGroup variable, and if we are, get all the
|
||||||
|
// nodes for that group.
|
||||||
|
if strings.HasPrefix(string(c), "grp_commands_") {
|
||||||
|
for cmd := range a.schemaMain.CommandGroupMap[commandGroup(c)] {
|
||||||
|
commands = append(commands, cmd)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No group found meaning a single node was given as an argument, so we
|
||||||
|
// just put the single node given as the only value in the slice.
|
||||||
|
commands = []command{c}
|
||||||
|
}
|
||||||
|
|
||||||
|
return commands
|
||||||
|
}
|
||||||
|
|
||||||
|
// aclAdd will add a command for a fromNode.
|
||||||
|
// If the node or the fromNode do not exist they will be created.
|
||||||
|
// The json encoded schema for a node and the hash of those data
|
||||||
|
// will also be generated.
|
||||||
|
func (a *authSchema) aclAdd(host node, source node, cmd command) {
|
||||||
|
a.schemaMain.mu.Lock()
|
||||||
|
defer a.schemaMain.mu.Unlock()
|
||||||
|
|
||||||
|
// Check if node exists in map.
|
||||||
|
if _, ok := a.schemaMain.ACLMap[host]; !ok {
|
||||||
|
// log.Printf("info: did not find node=%v in map, creating map[fromnode]map[command]struct{}\n", n)
|
||||||
|
a.schemaMain.ACLMap[host] = make(map[node]map[command]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if also source node exists in map
|
||||||
|
if _, ok := a.schemaMain.ACLMap[host][source]; !ok {
|
||||||
|
// log.Printf("info: did not find node=%v in map, creating map[fromnode]map[command]struct{}\n", fn)
|
||||||
|
a.schemaMain.ACLMap[host][source] = make(map[command]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
a.schemaMain.ACLMap[host][source][cmd] = struct{}{}
|
||||||
|
// err := a.generateJSONForHostOrGroup(n)
|
||||||
|
err := a.generateACLsForAllNodes()
|
||||||
|
if err != nil {
|
||||||
|
er := fmt.Errorf("error: addCommandForFromNode: %v", err)
|
||||||
|
log.Printf("%v\n", er)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf(" * DEBUG: aclNodeFromnodeCommandAdd: a.schemaMain.ACLMap=%v\n", a.schemaMain.ACLMap)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// aclDeleteCommand will delete the specified command from the fromnode.
|
||||||
|
func (a *authSchema) aclDeleteCommand(host node, source node, cmd command) error {
|
||||||
|
a.schemaMain.mu.Lock()
|
||||||
|
defer a.schemaMain.mu.Unlock()
|
||||||
|
|
||||||
|
// Check if node exists in map.
|
||||||
|
if _, ok := a.schemaMain.ACLMap[host]; !ok {
|
||||||
|
return fmt.Errorf("authSchema: no such node=%v to delete on in schema exists", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := a.schemaMain.ACLMap[host][source]; !ok {
|
||||||
|
return fmt.Errorf("authSchema: no such fromnode=%v to delete on in schema for node=%v exists", source, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := a.schemaMain.ACLMap[host][source][cmd]; !ok {
|
||||||
|
return fmt.Errorf("authSchema: no such command=%v from fromnode=%v to delete on in schema for node=%v exists", cmd, source, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(a.schemaMain.ACLMap[host][source], cmd)
|
||||||
|
|
||||||
|
err := a.generateACLsForAllNodes()
|
||||||
|
if err != nil {
|
||||||
|
er := fmt.Errorf("error: aclNodeFromNodeCommandDelete: %v", err)
|
||||||
|
log.Printf("%v\n", er)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// aclDeleteSource will delete specified source node and all commands specified for it.
|
||||||
|
func (a *authSchema) aclDeleteSource(host node, source node) error {
|
||||||
|
a.schemaMain.mu.Lock()
|
||||||
|
defer a.schemaMain.mu.Unlock()
|
||||||
|
|
||||||
|
// Check if node exists in map.
|
||||||
|
if _, ok := a.schemaMain.ACLMap[host]; !ok {
|
||||||
|
return fmt.Errorf("authSchema: no such node=%v to delete on in schema exists", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := a.schemaMain.ACLMap[host][source]; !ok {
|
||||||
|
return fmt.Errorf("authSchema: no such fromnode=%v to delete on in schema for node=%v exists", source, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(a.schemaMain.ACLMap[host], source)
|
||||||
|
|
||||||
|
err := a.generateACLsForAllNodes()
|
||||||
|
if err != nil {
|
||||||
|
er := fmt.Errorf("error: aclNodeFromnodeDelete: %v", err)
|
||||||
|
log.Printf("%v\n", er)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateACLsForAllNodes will generate a json encoded representation of the node specific
|
||||||
|
// map values of authSchema, along with a hash of the data.
|
||||||
|
//
|
||||||
|
// Will range over all the host elements defined in the ACL, create a new authParser for each one,
|
||||||
|
// and run a small state machine on each element to create the final ACL result to be used at host
|
||||||
|
// nodes.
|
||||||
|
// The result will be written to the schemaGenerated.ACLsToConvert map.
|
||||||
|
func (a *authSchema) generateACLsForAllNodes() error {
|
||||||
|
a.schemaGenerated.mu.Lock()
|
||||||
|
defer a.schemaGenerated.mu.Unlock()
|
||||||
|
|
||||||
|
a.schemaGenerated.ACLsToConvert = make(map[node]map[node]map[command]struct{})
|
||||||
|
|
||||||
|
// Rangle all ACL's. Both for single hosts, and group of hosts.
|
||||||
|
// ACL's that are for a group of hosts will be generated split
|
||||||
|
// out in it's indivial host name, and that current ACL will
|
||||||
|
// be added to the individual host in the ACLsToConvert map to
|
||||||
|
// built a complete picture of what the ACL's looks like for each
|
||||||
|
// individual hosts.
|
||||||
|
for n := range a.schemaMain.ACLMap {
|
||||||
|
//a.schemaGenerated.ACLsToConvert = make(map[node]map[node]map[command]struct{})
|
||||||
|
ap := newAuthParser(n, a)
|
||||||
|
ap.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACLsToConvert got the complete picture of what ACL's that
|
||||||
|
// are defined for each individual host node.
|
||||||
|
// Range this map, and generate a JSON representation of all
|
||||||
|
// the ACL's each host.
|
||||||
|
func() {
|
||||||
|
for n, m := range a.schemaGenerated.ACLsToConvert {
|
||||||
|
|
||||||
|
// cbor marshal the data of the ACL map to store for the host node.
|
||||||
|
cb, err := cbor.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
er := fmt.Errorf("error: failed to generate json for host in schemaGenerated: %v", err)
|
||||||
|
log.Printf("%v\n", er)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the hash for the data for the host node.
|
||||||
|
hash := func() [32]byte {
|
||||||
|
sns := a.nodeMapToSlice(n)
|
||||||
|
|
||||||
|
b, err := cbor.Marshal(sns)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("error: authSchema, json for hash: %v", err)
|
||||||
|
log.Printf("%v\n", err)
|
||||||
|
return [32]byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := sha256.Sum256(b)
|
||||||
|
return hash
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Store both the cbor marshaled data and the hash in a structure.
|
||||||
|
hostSerialized := HostACLsSerializedWithHash{
|
||||||
|
Data: cb,
|
||||||
|
Hash: hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
// and then store the cbor encoded data and the hash in the generated map.
|
||||||
|
a.schemaGenerated.GeneratedACLsMap[n] = hostSerialized
|
||||||
|
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourceNode is used to convert the ACL map structure of a host into a slice,
|
||||||
|
// and we then use the slice representation of the ACL to create the hash for
|
||||||
|
// a specific host node.
|
||||||
|
type sourceNode struct {
|
||||||
|
HostNode node
|
||||||
|
SourceCommands []sourceNodeCommands
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourceNodeCommand is used to convert the ACL map structure of a host into a slice,
|
||||||
|
// and we then use the slice representation of the ACL to create the hash for
|
||||||
|
// a specific host node.
|
||||||
|
type sourceNodeCommands struct {
|
||||||
|
Source node
|
||||||
|
Commands []command
|
||||||
|
}
|
||||||
|
|
||||||
|
// nodeMapToSlice will return a sourceNode structure, with the map sourceNode part
|
||||||
|
// of the map converted into a slice. Both the from node, and the commands
|
||||||
|
// defined for each sourceNode are sorted.
|
||||||
|
// This function is used when creating the hash of the nodeMap since we can not
|
||||||
|
// guarantee the order of a hash map, but we can with a slice.
|
||||||
|
func (a *authSchema) nodeMapToSlice(host node) sourceNode {
|
||||||
|
srcNodes := sourceNode{
|
||||||
|
HostNode: host,
|
||||||
|
}
|
||||||
|
|
||||||
|
for sn, commandMap := range a.schemaGenerated.ACLsToConvert[host] {
|
||||||
|
srcC := sourceNodeCommands{
|
||||||
|
Source: sn,
|
||||||
|
}
|
||||||
|
|
||||||
|
for cmd := range commandMap {
|
||||||
|
srcC.Commands = append(srcC.Commands, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort all the commands.
|
||||||
|
sort.SliceStable(srcC.Commands, func(i, j int) bool {
|
||||||
|
return srcC.Commands[i] < srcC.Commands[j]
|
||||||
|
})
|
||||||
|
|
||||||
|
srcNodes.SourceCommands = append(srcNodes.SourceCommands, srcC)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort all the source nodes.
|
||||||
|
sort.SliceStable(srcNodes.SourceCommands, func(i, j int) bool {
|
||||||
|
return srcNodes.SourceCommands[i].Source < srcNodes.SourceCommands[j].Source
|
||||||
|
})
|
||||||
|
|
||||||
|
// fmt.Printf(" * nodeMapToSlice: fromNodes: %#v\n", fns)
|
||||||
|
|
||||||
|
return srcNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// groupNodesAddNode adds a node to a group. If the group does
|
||||||
|
// not exist it will be created.
|
||||||
|
func (a *authSchema) groupNodesAddNode(ng nodeGroup, n node) {
|
||||||
|
err := a.validator.Var(ng, "startswith=grp_nodes_")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error: group name do not start with grp_nodes_: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a.schemaMain.mu.Lock()
|
||||||
|
defer a.schemaMain.mu.Unlock()
|
||||||
|
if _, ok := a.schemaMain.NodeGroupMap[ng]; !ok {
|
||||||
|
a.schemaMain.NodeGroupMap[ng] = make(map[node]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
a.schemaMain.NodeGroupMap[ng][n] = struct{}{}
|
||||||
|
|
||||||
|
// fmt.Printf(" * groupNodesAddNode: After adding to group node looks like: %+v\n", a.schemaMain.NodeGroupMap)
|
||||||
|
|
||||||
|
err = a.generateACLsForAllNodes()
|
||||||
|
if err != nil {
|
||||||
|
er := fmt.Errorf("error: groupNodesAddNode: %v", err)
|
||||||
|
log.Printf("%v\n", er)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// groupNodesDeleteNode deletes a node from a group in the map.
|
||||||
|
func (a *authSchema) groupNodesDeleteNode(ng nodeGroup, n node) {
|
||||||
|
a.schemaMain.mu.Lock()
|
||||||
|
defer a.schemaMain.mu.Unlock()
|
||||||
|
if _, ok := a.schemaMain.NodeGroupMap[ng][n]; !ok {
|
||||||
|
log.Printf("info: no such node with name=%v found in group=%v\n", ng, n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(a.schemaMain.NodeGroupMap[ng], n)
|
||||||
|
|
||||||
|
//fmt.Printf(" * After deleting nodeGroup map looks like: %+v\n", a.schemaMain.NodeGroupMap)
|
||||||
|
|
||||||
|
err := a.generateACLsForAllNodes()
|
||||||
|
if err != nil {
|
||||||
|
er := fmt.Errorf("error: groupNodesDeleteNode: %v", err)
|
||||||
|
log.Printf("%v\n", er)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// groupNodesDeleteGroup deletes a nodeGroup from map.
|
||||||
|
func (a *authSchema) groupNodesDeleteGroup(ng nodeGroup) {
|
||||||
|
a.schemaMain.mu.Lock()
|
||||||
|
defer a.schemaMain.mu.Unlock()
|
||||||
|
if _, ok := a.schemaMain.NodeGroupMap[ng]; !ok {
|
||||||
|
log.Printf("info: no such group found: %v\n", ng)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(a.schemaMain.NodeGroupMap, ng)
|
||||||
|
|
||||||
|
//fmt.Printf(" * After deleting nodeGroup map looks like: %+v\n", a.schemaMain.NodeGroupMap)
|
||||||
|
|
||||||
|
err := a.generateACLsForAllNodes()
|
||||||
|
if err != nil {
|
||||||
|
er := fmt.Errorf("error: groupNodesDeleteGroup: %v", err)
|
||||||
|
log.Printf("%v\n", er)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----
|
||||||
|
|
||||||
|
// groupCommandsAddCommand adds a command to a group. If the group does
|
||||||
|
// not exist it will be created.
|
||||||
|
func (a *authSchema) groupCommandsAddCommand(cg commandGroup, c command) {
|
||||||
|
err := a.validator.Var(cg, "startswith=grp_commands_")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error: group name do not start with grp_commands_ : %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a.schemaMain.mu.Lock()
|
||||||
|
defer a.schemaMain.mu.Unlock()
|
||||||
|
if _, ok := a.schemaMain.CommandGroupMap[cg]; !ok {
|
||||||
|
a.schemaMain.CommandGroupMap[cg] = make(map[command]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
a.schemaMain.CommandGroupMap[cg][c] = struct{}{}
|
||||||
|
|
||||||
|
//fmt.Printf(" * groupCommandsAddCommand: After adding command=%v to command group=%v map looks like: %+v\n", c, cg, a.schemaMain.CommandGroupMap)
|
||||||
|
|
||||||
|
err = a.generateACLsForAllNodes()
|
||||||
|
if err != nil {
|
||||||
|
er := fmt.Errorf("error: groupCommandsAddCommand: %v", err)
|
||||||
|
log.Printf("%v\n", er)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// groupCommandsDeleteCommand deletes a command from a group in the map.
|
||||||
|
func (a *authSchema) groupCommandsDeleteCommand(cg commandGroup, c command) {
|
||||||
|
a.schemaMain.mu.Lock()
|
||||||
|
defer a.schemaMain.mu.Unlock()
|
||||||
|
if _, ok := a.schemaMain.CommandGroupMap[cg][c]; !ok {
|
||||||
|
log.Printf("info: no such command with name=%v found in group=%v\n", c, cg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(a.schemaMain.CommandGroupMap[cg], c)
|
||||||
|
|
||||||
|
//fmt.Printf(" * After deleting command=%v from group=%v map looks like: %+v\n", c, cg, a.schemaMain.CommandGroupMap)
|
||||||
|
|
||||||
|
err := a.generateACLsForAllNodes()
|
||||||
|
if err != nil {
|
||||||
|
er := fmt.Errorf("error: groupCommandsDeleteCommand: %v", err)
|
||||||
|
log.Printf("%v\n", er)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// groupCommandDeleteGroup deletes a commandGroup map.
|
||||||
|
func (a *authSchema) groupCommandDeleteGroup(cg commandGroup) {
|
||||||
|
a.schemaMain.mu.Lock()
|
||||||
|
defer a.schemaMain.mu.Unlock()
|
||||||
|
if _, ok := a.schemaMain.CommandGroupMap[cg]; !ok {
|
||||||
|
log.Printf("info: no such group found: %v\n", cg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(a.schemaMain.CommandGroupMap, cg)
|
||||||
|
|
||||||
|
//fmt.Printf(" * After deleting commandGroup=%v map looks like: %+v\n", cg, a.schemaMain.CommandGroupMap)
|
||||||
|
|
||||||
|
err := a.generateACLsForAllNodes()
|
||||||
|
if err != nil {
|
||||||
|
er := fmt.Errorf("error: groupCommandDeleteGroup: %v", err)
|
||||||
|
log.Printf("%v\n", er)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// exportACLs will export the current content of the main ACLMap in JSON format.
|
||||||
|
func (a *authSchema) exportACLs() ([]byte, error) {
|
||||||
|
|
||||||
|
a.schemaMain.mu.Lock()
|
||||||
|
defer a.schemaMain.mu.Unlock()
|
||||||
|
|
||||||
|
js, err := json.Marshal(a.schemaMain.ACLMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error: failed to marshal schemaMain.ACLMap: %v", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return js, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// importACLs will import and replace all current ACL's with the ACL's provided as input.
|
||||||
|
func (a *authSchema) importACLs(js []byte) error {
|
||||||
|
|
||||||
|
a.schemaMain.mu.Lock()
|
||||||
|
defer a.schemaMain.mu.Unlock()
|
||||||
|
|
||||||
|
m := make(map[node]map[node]map[command]struct{})
|
||||||
|
|
||||||
|
err := json.Unmarshal(js, &m)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error: failed to unmarshal into ACLMap: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.schemaMain.ACLMap = m
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
118
central_auth_parser.go
Normal file
118
central_auth_parser.go
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package steward
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type authParser struct {
|
||||||
|
currentHost node
|
||||||
|
authSchema *authSchema
|
||||||
|
//ACLsToConvert map[node]map[node]map[command]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAuthParser returns a new authParser, with the current host node set.
|
||||||
|
func newAuthParser(n node, authSchema *authSchema) *authParser {
|
||||||
|
a := authParser{
|
||||||
|
currentHost: n,
|
||||||
|
authSchema: authSchema,
|
||||||
|
//ACLsToConvert: make(map[node]map[node]map[command]struct{}),
|
||||||
|
}
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
type parseFn func() parseFn
|
||||||
|
|
||||||
|
// parse will parse one host or one host group.
|
||||||
|
func (a *authParser) parse() {
|
||||||
|
fn := a.hostGroupOrSingle()
|
||||||
|
for {
|
||||||
|
fn = fn()
|
||||||
|
if fn == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// hostGroupOrSingle checks if host grp or single node.
|
||||||
|
func (a *authParser) hostGroupOrSingle() parseFn {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(string(a.currentHost), "grp_nodes_"):
|
||||||
|
// Is group
|
||||||
|
return a.hostIsGroup
|
||||||
|
default:
|
||||||
|
// Is single node
|
||||||
|
return a.hostIsNotGroup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hostIsGroup
|
||||||
|
func (a *authParser) hostIsGroup() parseFn {
|
||||||
|
// fmt.Printf("%v is a grp type\n", a.currentHost)
|
||||||
|
|
||||||
|
hosts := a.authSchema.nodeAsSlice(a.currentHost)
|
||||||
|
|
||||||
|
for source, cmdMap := range a.authSchema.schemaMain.ACLMap[a.currentHost] {
|
||||||
|
|
||||||
|
for cmd, emptyStruct := range cmdMap {
|
||||||
|
cmdSlice := a.authSchema.commandAsSlice(cmd)
|
||||||
|
|
||||||
|
// Expand eventual groups, so we use real fromNode nodenames in ACL for nodes.
|
||||||
|
sourceNodes := a.authSchema.nodeAsSlice(source)
|
||||||
|
for _, sourceNode := range sourceNodes {
|
||||||
|
for _, host := range hosts {
|
||||||
|
|
||||||
|
for _, cm := range cmdSlice {
|
||||||
|
if a.authSchema.schemaGenerated.ACLsToConvert[host] == nil {
|
||||||
|
a.authSchema.schemaGenerated.ACLsToConvert[host] = make(map[node]map[command]struct{})
|
||||||
|
}
|
||||||
|
if a.authSchema.schemaGenerated.ACLsToConvert[host][sourceNode] == nil {
|
||||||
|
a.authSchema.schemaGenerated.ACLsToConvert[host][sourceNode] = make(map[command]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
a.authSchema.schemaGenerated.ACLsToConvert[host][sourceNode][cm] = emptyStruct
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf(" * ACLsToConvert=%+v\n", a.authSchema.schemaGenerated.ACLsToConvert)
|
||||||
|
// Done with host. Return nil will make the main loop take the next host in the main for loop.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hostIsNotGroup
|
||||||
|
func (a *authParser) hostIsNotGroup() parseFn {
|
||||||
|
// fmt.Printf("%v is a single node type\n", a.currentHost)
|
||||||
|
|
||||||
|
host := a.currentHost
|
||||||
|
|
||||||
|
for source, cmdMap := range a.authSchema.schemaMain.ACLMap[a.currentHost] {
|
||||||
|
|
||||||
|
for cmd, emptyStruct := range cmdMap {
|
||||||
|
cmdSlice := a.authSchema.commandAsSlice(cmd)
|
||||||
|
|
||||||
|
// Expand eventual groups, so we use real fromNode nodenames in ACL for nodes.
|
||||||
|
sourceNodes := a.authSchema.nodeAsSlice(source)
|
||||||
|
for _, sourceNode := range sourceNodes {
|
||||||
|
|
||||||
|
for _, cm := range cmdSlice {
|
||||||
|
if a.authSchema.schemaGenerated.ACLsToConvert[host] == nil {
|
||||||
|
a.authSchema.schemaGenerated.ACLsToConvert[host] = make(map[node]map[command]struct{})
|
||||||
|
}
|
||||||
|
if a.authSchema.schemaGenerated.ACLsToConvert[host][sourceNode] == nil {
|
||||||
|
a.authSchema.schemaGenerated.ACLsToConvert[host][sourceNode] = make(map[command]struct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
a.authSchema.schemaGenerated.ACLsToConvert[host][sourceNode][cm] = emptyStruct
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf(" * ACLsToConvert contains: %+v\n", a.authSchema.schemaGenerated.ACLsToConvert)
|
||||||
|
|
||||||
|
// Done with host. Return nil will make the main loop take the next host in the main for loop.
|
||||||
|
return nil
|
||||||
|
}
|
514
central_auth_test.go
Normal file
514
central_auth_test.go
Normal file
|
@ -0,0 +1,514 @@
|
||||||
|
package steward
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/fxamacker/cbor/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestACLSingleNode(t *testing.T) {
|
||||||
|
if !*logging {
|
||||||
|
log.SetOutput(io.Discard)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := newAuthSchema()
|
||||||
|
a.aclAdd("ship101", "admin", "HORSE")
|
||||||
|
a.aclAdd("ship101", "admin", "PIG")
|
||||||
|
|
||||||
|
// --- TESTS ---
|
||||||
|
|
||||||
|
mapOfFromNodeCommands := make(map[node]map[command]struct{})
|
||||||
|
err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["HORSE"]; !ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: missing map entry")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["PIG"]; !ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: missing map entry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestACLWithGroups(t *testing.T) {
|
||||||
|
if !*logging {
|
||||||
|
log.SetOutput(io.Discard)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := newAuthSchema()
|
||||||
|
|
||||||
|
const (
|
||||||
|
grp_nodes_operators = "grp_nodes_operators"
|
||||||
|
grp_nodes_ships = "grp_nodes_ships"
|
||||||
|
grp_commands_commandset1 = "grp_commands_commandset1"
|
||||||
|
)
|
||||||
|
|
||||||
|
a.groupNodesAddNode(grp_nodes_operators, "operator1")
|
||||||
|
a.groupNodesAddNode(grp_nodes_operators, "operator2")
|
||||||
|
|
||||||
|
a.groupNodesAddNode(grp_nodes_ships, "ship100")
|
||||||
|
a.groupNodesAddNode(grp_nodes_ships, "ship101")
|
||||||
|
|
||||||
|
a.groupCommandsAddCommand(grp_commands_commandset1, "dmesg")
|
||||||
|
a.groupCommandsAddCommand(grp_commands_commandset1, "date")
|
||||||
|
|
||||||
|
a.aclAdd(grp_nodes_ships, "admin", "useradd -m kongen")
|
||||||
|
a.aclAdd("ship101", "admin", "HORSE")
|
||||||
|
|
||||||
|
a.aclAdd(grp_nodes_ships, grp_nodes_operators, grp_commands_commandset1)
|
||||||
|
|
||||||
|
mapOfFromNodeCommands := make(map[node]map[command]struct{})
|
||||||
|
err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["useradd -m kongen"]; !ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: missing map entry")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["operator1"]["dmesg"]; !ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: missing map entry")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["operator1"]["date"]; !ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: missing map entry")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["operator2"]["dmesg"]; !ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: missing map entry")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["operator2"]["date"]; !ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: missing map entry")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["HORSE"]; !ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: missing map entry")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestACLNodesGroupDeleteNode(t *testing.T) {
|
||||||
|
if !*logging {
|
||||||
|
log.SetOutput(io.Discard)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := newAuthSchema()
|
||||||
|
|
||||||
|
const (
|
||||||
|
grp_nodes_operators = "grp_nodes_operators"
|
||||||
|
grp_nodes_ships = "grp_nodes_ships"
|
||||||
|
grp_commands_commandset1 = "grp_commands_commandset1"
|
||||||
|
)
|
||||||
|
|
||||||
|
a.groupNodesAddNode(grp_nodes_operators, "operator1")
|
||||||
|
a.groupNodesAddNode(grp_nodes_operators, "operator2")
|
||||||
|
|
||||||
|
a.groupNodesAddNode(grp_nodes_ships, "ship100")
|
||||||
|
a.groupNodesAddNode(grp_nodes_ships, "ship101")
|
||||||
|
|
||||||
|
a.groupCommandsAddCommand(grp_commands_commandset1, "dmesg")
|
||||||
|
a.groupCommandsAddCommand(grp_commands_commandset1, "date")
|
||||||
|
|
||||||
|
a.aclAdd(grp_nodes_ships, "admin", "useradd -m kongen")
|
||||||
|
a.aclAdd("ship101", "admin", "HORSE")
|
||||||
|
|
||||||
|
a.aclAdd(grp_nodes_ships, grp_nodes_operators, grp_commands_commandset1)
|
||||||
|
|
||||||
|
a.groupNodesDeleteNode(grp_nodes_ships, "ship101")
|
||||||
|
|
||||||
|
// Check that we still got the data for ship100.
|
||||||
|
{
|
||||||
|
mapOfFromNodeCommands := make(map[node]map[command]struct{})
|
||||||
|
err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship100"].Data, &mapOfFromNodeCommands)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["useradd -m kongen"]; !ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: missing map entry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we don't have any data for ship101.
|
||||||
|
{
|
||||||
|
mapOfFromNodeCommands := make(map[node]map[command]struct{})
|
||||||
|
err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["useradd -m kongen"]; ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: missing map entry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupNodesDeleteGroup(t *testing.T) {
|
||||||
|
if !*logging {
|
||||||
|
log.SetOutput(io.Discard)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := newAuthSchema()
|
||||||
|
|
||||||
|
const (
|
||||||
|
grp_nodes_operators = "grp_nodes_operators"
|
||||||
|
grp_nodes_ships = "grp_nodes_ships"
|
||||||
|
grp_commands_commandset1 = "grp_commands_commandset1"
|
||||||
|
)
|
||||||
|
|
||||||
|
a.groupNodesAddNode(grp_nodes_operators, "operator1")
|
||||||
|
a.groupNodesAddNode(grp_nodes_operators, "operator2")
|
||||||
|
|
||||||
|
a.groupNodesAddNode(grp_nodes_ships, "ship100")
|
||||||
|
a.groupNodesAddNode(grp_nodes_ships, "ship101")
|
||||||
|
|
||||||
|
a.groupCommandsAddCommand(grp_commands_commandset1, "dmesg")
|
||||||
|
a.groupCommandsAddCommand(grp_commands_commandset1, "date")
|
||||||
|
|
||||||
|
a.aclAdd(grp_nodes_ships, "admin", "useradd -m kongen")
|
||||||
|
a.aclAdd("ship101", "admin", "HORSE")
|
||||||
|
|
||||||
|
a.aclAdd(grp_nodes_ships, grp_nodes_operators, grp_commands_commandset1)
|
||||||
|
|
||||||
|
a.groupNodesDeleteGroup(grp_nodes_operators)
|
||||||
|
|
||||||
|
// Check that we still got the data for other ACL's.
|
||||||
|
{
|
||||||
|
mapOfFromNodeCommands := make(map[node]map[command]struct{})
|
||||||
|
err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["HORSE"]; !ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: missing map entry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we don't have any data for grp_nodes_operators
|
||||||
|
{
|
||||||
|
mapOfFromNodeCommands := make(map[node]map[command]struct{})
|
||||||
|
err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["dmesg"]; ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: foud map entry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupCommandDeleteGroup(t *testing.T) {
|
||||||
|
if !*logging {
|
||||||
|
log.SetOutput(io.Discard)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := newAuthSchema()
|
||||||
|
|
||||||
|
const (
|
||||||
|
grp_nodes_operators = "grp_nodes_operators"
|
||||||
|
grp_nodes_ships = "grp_nodes_ships"
|
||||||
|
grp_commands_commandset1 = "grp_commands_commandset1"
|
||||||
|
)
|
||||||
|
|
||||||
|
a.groupNodesAddNode(grp_nodes_operators, "operator1")
|
||||||
|
a.groupNodesAddNode(grp_nodes_operators, "operator2")
|
||||||
|
|
||||||
|
a.groupNodesAddNode(grp_nodes_ships, "ship100")
|
||||||
|
a.groupNodesAddNode(grp_nodes_ships, "ship101")
|
||||||
|
|
||||||
|
a.groupCommandsAddCommand(grp_commands_commandset1, "dmesg")
|
||||||
|
a.groupCommandsAddCommand(grp_commands_commandset1, "date")
|
||||||
|
|
||||||
|
a.aclAdd(grp_nodes_ships, "admin", "useradd -m kongen")
|
||||||
|
a.aclAdd("ship101", "admin", "HORSE")
|
||||||
|
|
||||||
|
a.aclAdd(grp_nodes_ships, grp_nodes_operators, grp_commands_commandset1)
|
||||||
|
|
||||||
|
a.groupCommandDeleteGroup(grp_commands_commandset1)
|
||||||
|
|
||||||
|
// Check that we still got the data for other ACL's.
|
||||||
|
{
|
||||||
|
mapOfFromNodeCommands := make(map[node]map[command]struct{})
|
||||||
|
err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["HORSE"]; !ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: missing map entry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we don't have any data for grp_nodes_operators
|
||||||
|
{
|
||||||
|
mapOfFromNodeCommands := make(map[node]map[command]struct{})
|
||||||
|
err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["dmesg"]; ok {
|
||||||
|
t.Fatal(" \U0001F631 [FAILED]: foud map entry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestACLGenerated(t *testing.T) {
|
||||||
|
if !*logging {
|
||||||
|
log.SetOutput(io.Discard)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := newAuthSchema()
|
||||||
|
|
||||||
|
a.aclAdd("ship101", "admin", "HORSE")
|
||||||
|
|
||||||
|
a.groupNodesAddNode("grp_nodes_ships", "ship101")
|
||||||
|
a.aclAdd("grp_nodes_ships", "admin", "HEN")
|
||||||
|
|
||||||
|
a.groupCommandsAddCommand("grp_commands_test", "echo")
|
||||||
|
a.groupCommandsAddCommand("grp_commands_test", "dmesg")
|
||||||
|
a.aclAdd("grp_nodes_ships", "admin", "grp_commands_test")
|
||||||
|
|
||||||
|
a.groupCommandsDeleteCommand("grp_commands_test", "echo")
|
||||||
|
|
||||||
|
// --- TESTS ---
|
||||||
|
|
||||||
|
mapOfFromNodeCommands := make(map[node]map[command]struct{})
|
||||||
|
err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//if _, ok := mapOfFromNodeCommands["admin"]["PIG"]; !ok {
|
||||||
|
// t.Fatalf(" \U0001F631 [FAILED]: missing map entry: PIG: Content of Map: %v", mapOfFromNodeCommands)
|
||||||
|
//}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["HORSE"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: HORSE: Content of Map: %v", mapOfFromNodeCommands)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["HEN"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: HEN: Content of Map: %v", mapOfFromNodeCommands)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["echo"]; ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: should not contain map entry: echo: Content of Map: %v", mapOfFromNodeCommands)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := mapOfFromNodeCommands["admin"]["dmesg"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: echo: Content of Map: %v", mapOfFromNodeCommands)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestACLSchemaMainACLMap(t *testing.T) {
|
||||||
|
if !*logging {
|
||||||
|
log.SetOutput(io.Discard)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := newAuthSchema()
|
||||||
|
|
||||||
|
//a.aclNodeFromnodeCommandAdd("ship101", "admin", "PIG")
|
||||||
|
// fmt.Printf("---------------ADDING COMMAND-------------\n")
|
||||||
|
a.aclAdd("ship0", "admin", "systemctl")
|
||||||
|
a.aclAdd("ship1", "admin", "tcpdump")
|
||||||
|
|
||||||
|
if _, ok := a.schemaMain.ACLMap["ship0"]["admin"]["systemctl"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: ship0, admin, systemctl")
|
||||||
|
}
|
||||||
|
if _, ok := a.schemaMain.ACLMap["ship1"]["admin"]["tcpdump"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: ship1, admin, tcpdump")
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("---------------ADDING COMMAND-------------\n")
|
||||||
|
a.groupNodesAddNode("grp_nodes_ships", "ship1")
|
||||||
|
a.groupNodesAddNode("grp_nodes_ships", "ship2")
|
||||||
|
a.aclAdd("grp_nodes_ships", "admin", "dmesg")
|
||||||
|
|
||||||
|
if _, ok := a.schemaMain.ACLMap["grp_nodes_ships"]["admin"]["dmesg"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: ship1, admin, tcpdump")
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("---------------ADDING COMMAND-------------\n")
|
||||||
|
a.aclAdd("ship2", "admin", "echo")
|
||||||
|
|
||||||
|
if _, ok := a.schemaMain.ACLMap["ship2"]["admin"]["echo"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: ship1, admin, tcpdump")
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("---------------DELETING COMMAND grp_nodes_ships, admin, dmesg-------------\n")
|
||||||
|
a.aclDeleteCommand("grp_nodes_ships", "admin", "dmesg")
|
||||||
|
|
||||||
|
if _, ok := a.schemaMain.ACLMap["grp_nodes_ships"]["admin"]["dmesg"]; ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: found map entry: grp_nodes_ships, admin, dmesg")
|
||||||
|
}
|
||||||
|
// Check that the remaining are still ok.
|
||||||
|
if _, ok := a.schemaMain.ACLMap["ship0"]["admin"]["systemctl"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: ship0, admin, systemctl")
|
||||||
|
}
|
||||||
|
if _, ok := a.schemaMain.ACLMap["ship1"]["admin"]["tcpdump"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: ship1, admin, tcpdump")
|
||||||
|
}
|
||||||
|
if _, ok := a.schemaMain.ACLMap["ship2"]["admin"]["echo"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: ship1, admin, tcpdump")
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("---------------DELETING COMMAND ship0, admin, systemctl-------------\n")
|
||||||
|
a.aclDeleteCommand("ship0", "admin", "systemctl")
|
||||||
|
|
||||||
|
if _, ok := a.schemaMain.ACLMap["ship0"]["admin"]["systemctl"]; ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: ship0, admin, systemctl")
|
||||||
|
}
|
||||||
|
// Check that the remaining are ok.
|
||||||
|
if _, ok := a.schemaMain.ACLMap["ship1"]["admin"]["tcpdump"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: ship1, admin, tcpdump")
|
||||||
|
}
|
||||||
|
if _, ok := a.schemaMain.ACLMap["ship2"]["admin"]["echo"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: ship1, admin, tcpdump")
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("---------------DELETING SOURCE ship1, admin-------------\n")
|
||||||
|
a.aclDeleteSource("ship1", "admin")
|
||||||
|
|
||||||
|
if _, ok := a.schemaMain.ACLMap["ship1"]["admin"]; ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: ship1, admin, tcpdump")
|
||||||
|
}
|
||||||
|
// Check that the remaining are ok.
|
||||||
|
if _, ok := a.schemaMain.ACLMap["ship2"]["admin"]["echo"]; !ok {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: missing map entry: ship1, admin, tcpdump")
|
||||||
|
}
|
||||||
|
// --- TESTS ---
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestACLHash(t *testing.T) {
|
||||||
|
if !*logging {
|
||||||
|
log.SetOutput(io.Discard)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := newAuthSchema()
|
||||||
|
|
||||||
|
a.aclAdd("ship101", "admin", "HORSE")
|
||||||
|
|
||||||
|
a.groupNodesAddNode("grp_nodes_ships", "ship101")
|
||||||
|
a.aclAdd("grp_nodes_ships", "admin", "HEN")
|
||||||
|
|
||||||
|
hash := [32]uint8{0xa4, 0x99, 0xbd, 0xa3, 0x18, 0x26, 0x52, 0xc2, 0x92, 0x60, 0x23, 0x19, 0x3c, 0xa, 0x7, 0xa9, 0xb7, 0x77, 0x4f, 0x11, 0x34, 0xd5, 0x2d, 0xd1, 0x8d, 0xab, 0x6c, 0x4b, 0x2, 0xfa, 0x5c, 0x7a}
|
||||||
|
value := a.schemaGenerated.GeneratedACLsMap["ship101"].Hash
|
||||||
|
// fmt.Printf("%#v\n", a.schemaGenerated.GeneratedACLsMap["ship101"].Hash)
|
||||||
|
|
||||||
|
if bytes.Equal(hash[:], value[:]) == false {
|
||||||
|
t.Fatalf(" \U0001F631 [FAILED]: hash mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestACLConcurrent(t *testing.T) {
|
||||||
|
a := newAuthSchema()
|
||||||
|
|
||||||
|
// -----------General testing and creation of some data----------------
|
||||||
|
|
||||||
|
// Start concurrent updating of the schema.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 4000; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
a.aclAdd("ship1", "operator2", "rm -rf")
|
||||||
|
a.aclAdd("ship1", "operator1", "ls -lt")
|
||||||
|
a.aclAdd("ship1", "operator1", "ls -lt")
|
||||||
|
a.aclAdd("ship1", "operator2", "ls -l")
|
||||||
|
a.aclAdd("ship3", "operator3", "ls -lt")
|
||||||
|
a.aclAdd("ship3", "operator3", "vi /etc/hostname")
|
||||||
|
a.aclDeleteCommand("ship3", "operator2", "ls -lt")
|
||||||
|
a.aclDeleteSource("ship3", "operator3")
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
// fmt.Println("----schemaMain------")
|
||||||
|
a.schemaMain.mu.Lock()
|
||||||
|
for _, v := range a.schemaMain.ACLMap {
|
||||||
|
_ = fmt.Sprintf("%+v\n", v)
|
||||||
|
}
|
||||||
|
a.schemaMain.mu.Unlock()
|
||||||
|
|
||||||
|
// fmt.Println("----schemaGenerated------")
|
||||||
|
a.schemaGenerated.mu.Lock()
|
||||||
|
for k, v := range a.schemaGenerated.GeneratedACLsMap {
|
||||||
|
_ = fmt.Sprintf("node: %v, NodeDataSerialized: %v\n", k, string(v.Data))
|
||||||
|
_ = fmt.Sprintf("node: %v, Hash: %v\n", k, v.Hash)
|
||||||
|
}
|
||||||
|
a.schemaGenerated.mu.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExportACLs(t *testing.T) {
|
||||||
|
const (
|
||||||
|
grp_nodes_operators = "grp_nodes_operators"
|
||||||
|
grp_nodes_ships = "grp_nodes_ships"
|
||||||
|
grp_commands_commandset1 = "grp_commands_commandset1"
|
||||||
|
)
|
||||||
|
|
||||||
|
a := newAuthSchema()
|
||||||
|
|
||||||
|
a.groupNodesAddNode(grp_nodes_operators, "operator1")
|
||||||
|
a.groupNodesAddNode(grp_nodes_operators, "operator2")
|
||||||
|
|
||||||
|
a.groupNodesAddNode(grp_nodes_ships, "ship100")
|
||||||
|
a.groupNodesAddNode(grp_nodes_ships, "ship101")
|
||||||
|
|
||||||
|
a.groupCommandsAddCommand(grp_commands_commandset1, "dmesg")
|
||||||
|
a.groupCommandsAddCommand(grp_commands_commandset1, "date")
|
||||||
|
|
||||||
|
a.aclAdd(grp_nodes_ships, "admin", "useradd -m kongen")
|
||||||
|
a.aclAdd("ship101", "admin", "HORSE")
|
||||||
|
|
||||||
|
a.aclAdd(grp_nodes_ships, grp_nodes_operators, grp_commands_commandset1)
|
||||||
|
|
||||||
|
js, err := a.exportACLs()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := `{"grp_nodes_ships":{"admin":{"useradd -m kongen":{}},"grp_nodes_operators":{"grp_commands_commandset1":{}}},"ship101":{"admin":{"HORSE":{}}}}`
|
||||||
|
|
||||||
|
if string(js) != string(want) {
|
||||||
|
t.Fatalf("error: export does not match with what we want\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestImportACLs(t *testing.T) {
|
||||||
|
// js := `{"grp_nodes_ships":{"admin":{"useradd -m kongen":{}},"grp_nodes_operators":{"grp_commands_commandset1":{}}},"ship101":{"admin":{"HORSE":{}}}`
|
||||||
|
|
||||||
|
js := []byte{0x7b, 0x22, 0x67, 0x72, 0x70, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x73, 0x68, 0x69, 0x70, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x22, 0x3a, 0x7b, 0x22, 0x75, 0x73, 0x65, 0x72, 0x61, 0x64, 0x64, 0x20, 0x2d, 0x6d, 0x20, 0x6b, 0x6f, 0x6e, 0x67, 0x65, 0x6e, 0x22, 0x3a, 0x7b, 0x7d, 0x7d, 0x2c, 0x22, 0x67, 0x72, 0x70, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x3a, 0x7b, 0x22, 0x67, 0x72, 0x70, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x65, 0x74, 0x31, 0x22, 0x3a, 0x7b, 0x7d, 0x7d, 0x7d, 0x2c, 0x22, 0x73, 0x68, 0x69, 0x70, 0x31, 0x30, 0x31, 0x22, 0x3a, 0x7b, 0x22, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x22, 0x3a, 0x7b, 0x22, 0x48, 0x4f, 0x52, 0x53, 0x45, 0x22, 0x3a, 0x7b, 0x7d, 0x7d, 0x7d, 0x7d}
|
||||||
|
|
||||||
|
want := `map[grp_nodes_ships:map[admin:map[useradd -m kongen:{}] grp_nodes_operators:map[grp_commands_commandset1:{}]] ship101:map[admin:map[HORSE:{}]]]`
|
||||||
|
|
||||||
|
a := newAuthSchema()
|
||||||
|
|
||||||
|
err := a.importACLs(js)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fmt.Sprintf("%v", a.schemaMain.ACLMap) != want {
|
||||||
|
t.Fatalf("error: import does not match with what we want\n")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue