2023-10-04 20:58:42 +00:00
|
|
|
package ctrl
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2022-05-26 05:13:34 +00:00
|
|
|
"io"
|
2022-05-11 05:13:13 +00:00
|
|
|
"os"
|
2022-05-26 05:13:34 +00:00
|
|
|
"path/filepath"
|
2022-05-11 05:13:13 +00:00
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/fxamacker/cbor/v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
// // centralAuth
|
|
|
|
// type centralAuth struct {
|
|
|
|
// authorization *authorization
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // newCentralAuth
|
|
|
|
// func newCentralAuth() *centralAuth {
|
|
|
|
// c := centralAuth{
|
|
|
|
// authorization: newAuthorization(),
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// return &c
|
|
|
|
// }
|
|
|
|
|
|
|
|
// --------------------------------------
|
|
|
|
|
2022-05-18 07:21:14 +00:00
|
|
|
type accessLists struct {
|
2022-05-11 05:13:13 +00:00
|
|
|
// Holds the editable structures for ACL handling.
|
|
|
|
schemaMain *schemaMain
|
|
|
|
// Holds the generated based on the editable structures for ACL handling.
|
|
|
|
schemaGenerated *schemaGenerated
|
2022-05-19 09:53:25 +00:00
|
|
|
errorKernel *errorKernel
|
2022-05-19 18:00:25 +00:00
|
|
|
configuration *Configuration
|
2022-06-01 11:04:34 +00:00
|
|
|
pki *pki
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
2022-06-01 15:35:23 +00:00
|
|
|
func newAccessLists(pki *pki, errorKernel *errorKernel, configuration *Configuration) *accessLists {
|
2022-05-18 07:21:14 +00:00
|
|
|
a := accessLists{
|
2023-01-12 11:01:01 +00:00
|
|
|
schemaMain: newSchemaMain(configuration, errorKernel),
|
2022-05-11 05:13:13 +00:00
|
|
|
schemaGenerated: newSchemaGenerated(),
|
2022-05-19 09:53:25 +00:00
|
|
|
errorKernel: errorKernel,
|
2022-05-19 18:00:25 +00:00
|
|
|
configuration: configuration,
|
2022-06-01 15:35:23 +00:00
|
|
|
pki: pki,
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return &a
|
|
|
|
}
|
|
|
|
|
2022-05-18 09:26:06 +00:00
|
|
|
// type node string
|
2022-05-11 05:13:13 +00:00
|
|
|
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 {
|
2022-05-18 09:26:06 +00:00
|
|
|
ACLMap map[Node]map[Node]map[command]struct{}
|
2022-05-26 05:13:34 +00:00
|
|
|
ACLMapFilePath string
|
2022-05-18 09:26:06 +00:00
|
|
|
NodeGroupMap map[nodeGroup]map[Node]struct{}
|
2022-05-11 05:13:13 +00:00
|
|
|
CommandGroupMap map[commandGroup]map[command]struct{}
|
|
|
|
mu sync.Mutex
|
2023-01-12 11:01:01 +00:00
|
|
|
configuration *Configuration
|
|
|
|
errorKernel *errorKernel
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
2023-01-12 11:01:01 +00:00
|
|
|
func newSchemaMain(configuration *Configuration, errorKernel *errorKernel) *schemaMain {
|
2022-05-11 05:13:13 +00:00
|
|
|
s := schemaMain{
|
2022-05-18 09:26:06 +00:00
|
|
|
ACLMap: make(map[Node]map[Node]map[command]struct{}),
|
2022-05-26 05:13:34 +00:00
|
|
|
ACLMapFilePath: filepath.Join(configuration.DatabaseFolder, "central_aclmap.txt"),
|
2022-05-18 09:26:06 +00:00
|
|
|
NodeGroupMap: make(map[nodeGroup]map[Node]struct{}),
|
2022-05-11 05:13:13 +00:00
|
|
|
CommandGroupMap: make(map[commandGroup]map[command]struct{}),
|
2023-01-12 11:01:01 +00:00
|
|
|
configuration: configuration,
|
|
|
|
errorKernel: errorKernel,
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
2022-05-26 05:13:34 +00:00
|
|
|
|
|
|
|
// Load ACLMap from disk if present.
|
|
|
|
func() {
|
|
|
|
if _, err := os.Stat(s.ACLMapFilePath); os.IsNotExist(err) {
|
2023-01-12 11:01:01 +00:00
|
|
|
er := fmt.Errorf("info: newSchemaMain: no file for ACLMap found, will create new one, %v: %v", s.ACLMapFilePath, err)
|
|
|
|
errorKernel.logInfo(er, configuration)
|
2022-05-26 05:13:34 +00:00
|
|
|
|
|
|
|
// If no aclmap is present on disk we just return from this
|
|
|
|
// function without loading any values.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
fh, err := os.Open(s.ACLMapFilePath)
|
|
|
|
if err != nil {
|
|
|
|
er := fmt.Errorf("error: newSchemaMain: failed to open file for reading %v: %v", s.ACLMapFilePath, err)
|
2023-01-12 11:01:01 +00:00
|
|
|
errorKernel.logError(er, configuration)
|
2022-05-26 05:13:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
b, err := io.ReadAll(fh)
|
|
|
|
if err != nil {
|
|
|
|
er := fmt.Errorf("error: newSchemaMain: failed to ReadAll file %v: %v", s.ACLMapFilePath, err)
|
2023-01-12 11:01:01 +00:00
|
|
|
errorKernel.logError(er, configuration)
|
2022-05-26 05:13:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Unmarshal the data read from disk.
|
|
|
|
err = json.Unmarshal(b, &s.ACLMap)
|
|
|
|
if err != nil {
|
|
|
|
er := fmt.Errorf("error: newSchemaMain: failed to unmarshal content from file %v: %v", s.ACLMapFilePath, err)
|
2023-01-12 11:01:01 +00:00
|
|
|
errorKernel.logError(er, configuration)
|
2022-05-26 05:13:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the aclGenerated map happens in the function where this function is called.
|
|
|
|
}()
|
2022-05-11 05:13:13 +00:00
|
|
|
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 {
|
2022-05-18 09:26:06 +00:00
|
|
|
ACLsToConvert map[Node]map[Node]map[command]struct{}
|
|
|
|
GeneratedACLsMap map[Node]HostACLsSerializedWithHash
|
2022-05-11 05:13:13 +00:00
|
|
|
mu sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
func newSchemaGenerated() *schemaGenerated {
|
|
|
|
s := schemaGenerated{
|
2022-05-18 09:26:06 +00:00
|
|
|
ACLsToConvert: map[Node]map[Node]map[command]struct{}{},
|
|
|
|
GeneratedACLsMap: make(map[Node]HostACLsSerializedWithHash),
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
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 {
|
2022-05-24 10:00:38 +00:00
|
|
|
// data is all the ACL's for a specific node serialized serialized into cbor.
|
2022-05-11 05:13:13 +00:00
|
|
|
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.
|
2022-05-18 09:26:06 +00:00
|
|
|
func (a *accessLists) nodeAsSlice(n Node) []Node {
|
|
|
|
nodes := []Node{}
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
// Check if we are given a nodeGroup variable, and if we are, get all the
|
|
|
|
// nodes for that group.
|
2022-06-01 12:30:30 +00:00
|
|
|
switch {
|
|
|
|
case strings.HasPrefix(string(n), "grp_nodes_"):
|
2022-05-11 05:13:13 +00:00
|
|
|
for nd := range a.schemaMain.NodeGroupMap[nodeGroup(n)] {
|
|
|
|
nodes = append(nodes, nd)
|
|
|
|
}
|
2022-06-01 12:30:30 +00:00
|
|
|
|
|
|
|
case string(n) == "*":
|
|
|
|
func() {
|
|
|
|
a.pki.nodesAcked.mu.Lock()
|
|
|
|
defer a.pki.nodesAcked.mu.Unlock()
|
|
|
|
|
|
|
|
for nd := range a.pki.nodesAcked.keysAndHash.Keys {
|
|
|
|
nodes = append(nodes, nd)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
default:
|
2022-05-11 05:13:13 +00:00
|
|
|
// No group found meaning a single node was given as an argument.
|
2022-05-18 09:26:06 +00:00
|
|
|
nodes = []Node{n}
|
2022-06-01 12:30:30 +00:00
|
|
|
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2022-05-18 07:21:14 +00:00
|
|
|
func (a *accessLists) commandAsSlice(c command) []command {
|
2022-05-11 05:13:13 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-05-18 12:43:35 +00:00
|
|
|
// aclAddCommand will add a command for a fromNode.
|
2022-05-11 05:13:13 +00:00
|
|
|
// 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.
|
2022-06-01 13:58:17 +00:00
|
|
|
func (c *centralAuth) aclAddCommand(host Node, source Node, cmd command) {
|
|
|
|
c.accessLists.schemaMain.mu.Lock()
|
|
|
|
defer c.accessLists.schemaMain.mu.Unlock()
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
// Check if node exists in map.
|
2022-06-01 13:58:17 +00:00
|
|
|
if _, ok := c.accessLists.schemaMain.ACLMap[host]; !ok {
|
2022-05-11 05:13:13 +00:00
|
|
|
// log.Printf("info: did not find node=%v in map, creating map[fromnode]map[command]struct{}\n", n)
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaMain.ACLMap[host] = make(map[Node]map[command]struct{})
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if also source node exists in map
|
2022-06-01 13:58:17 +00:00
|
|
|
if _, ok := c.accessLists.schemaMain.ACLMap[host][source]; !ok {
|
2022-05-11 05:13:13 +00:00
|
|
|
// log.Printf("info: did not find node=%v in map, creating map[fromnode]map[command]struct{}\n", fn)
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaMain.ACLMap[host][source] = make(map[command]struct{})
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaMain.ACLMap[host][source][cmd] = struct{}{}
|
2022-05-11 05:13:13 +00:00
|
|
|
// err := a.generateJSONForHostOrGroup(n)
|
2022-06-01 13:58:17 +00:00
|
|
|
err := c.generateACLsForAllNodes()
|
2022-05-11 05:13:13 +00:00
|
|
|
if err != nil {
|
|
|
|
er := fmt.Errorf("error: addCommandForFromNode: %v", err)
|
2023-01-12 11:01:01 +00:00
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// fmt.Printf(" * DEBUG: aclNodeFromnodeCommandAdd: a.schemaMain.ACLMap=%v\n", a.schemaMain.ACLMap)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// aclDeleteCommand will delete the specified command from the fromnode.
|
2022-06-01 13:58:17 +00:00
|
|
|
func (c *centralAuth) aclDeleteCommand(host Node, source Node, cmd command) error {
|
|
|
|
c.accessLists.schemaMain.mu.Lock()
|
|
|
|
defer c.accessLists.schemaMain.mu.Unlock()
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
// Check if node exists in map.
|
2022-06-01 13:58:17 +00:00
|
|
|
if _, ok := c.accessLists.schemaMain.ACLMap[host]; !ok {
|
2022-05-11 05:13:13 +00:00
|
|
|
return fmt.Errorf("authSchema: no such node=%v to delete on in schema exists", host)
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
if _, ok := c.accessLists.schemaMain.ACLMap[host][source]; !ok {
|
2022-05-11 05:13:13 +00:00
|
|
|
return fmt.Errorf("authSchema: no such fromnode=%v to delete on in schema for node=%v exists", source, host)
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
if _, ok := c.accessLists.schemaMain.ACLMap[host][source][cmd]; !ok {
|
2022-05-11 05:13:13 +00:00
|
|
|
return fmt.Errorf("authSchema: no such command=%v from fromnode=%v to delete on in schema for node=%v exists", cmd, source, host)
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
delete(c.accessLists.schemaMain.ACLMap[host][source], cmd)
|
2022-05-11 05:13:13 +00:00
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
err := c.generateACLsForAllNodes()
|
2022-05-11 05:13:13 +00:00
|
|
|
if err != nil {
|
|
|
|
er := fmt.Errorf("error: aclNodeFromNodeCommandDelete: %v", err)
|
2023-01-12 11:01:01 +00:00
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// aclDeleteSource will delete specified source node and all commands specified for it.
|
2022-06-01 13:58:17 +00:00
|
|
|
func (c *centralAuth) aclDeleteSource(host Node, source Node) error {
|
|
|
|
c.accessLists.schemaMain.mu.Lock()
|
|
|
|
defer c.accessLists.schemaMain.mu.Unlock()
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
// Check if node exists in map.
|
2022-06-01 13:58:17 +00:00
|
|
|
if _, ok := c.accessLists.schemaMain.ACLMap[host]; !ok {
|
2022-05-11 05:13:13 +00:00
|
|
|
return fmt.Errorf("authSchema: no such node=%v to delete on in schema exists", host)
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
if _, ok := c.accessLists.schemaMain.ACLMap[host][source]; !ok {
|
2022-05-11 05:13:13 +00:00
|
|
|
return fmt.Errorf("authSchema: no such fromnode=%v to delete on in schema for node=%v exists", source, host)
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
delete(c.accessLists.schemaMain.ACLMap[host], source)
|
2022-05-11 05:13:13 +00:00
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
err := c.generateACLsForAllNodes()
|
2022-05-11 05:13:13 +00:00
|
|
|
if err != nil {
|
|
|
|
er := fmt.Errorf("error: aclNodeFromnodeDelete: %v", err)
|
2023-01-12 11:01:01 +00:00
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2022-06-01 13:58:17 +00:00
|
|
|
func (c *centralAuth) generateACLsForAllNodes() error {
|
2022-05-26 05:13:34 +00:00
|
|
|
// We first one to save the current main ACLMap.
|
|
|
|
func() {
|
2023-01-10 05:50:28 +00:00
|
|
|
fh, err := os.OpenFile(c.accessLists.schemaMain.ACLMapFilePath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0660)
|
2022-05-26 05:13:34 +00:00
|
|
|
if err != nil {
|
2022-06-01 13:58:17 +00:00
|
|
|
er := fmt.Errorf("error: generateACLsForAllNodes: opening file for writing: %v, err: %v", c.accessLists.schemaMain.ACLMapFilePath, err)
|
2023-01-12 11:01:01 +00:00
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-26 05:13:34 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
defer fh.Close()
|
|
|
|
|
|
|
|
// a.schemaMain.mu.Lock()
|
|
|
|
// defer a.schemaMain.mu.Unlock()
|
|
|
|
enc := json.NewEncoder(fh)
|
2022-06-02 04:29:37 +00:00
|
|
|
enc.SetEscapeHTML(false)
|
2024-03-07 22:17:24 +00:00
|
|
|
err = enc.Encode(c.accessLists.schemaMain.ACLMap)
|
2022-05-26 05:13:34 +00:00
|
|
|
if err != nil {
|
2022-06-01 13:58:17 +00:00
|
|
|
er := fmt.Errorf("error: generateACLsForAllNodes: encoding json to file failed: %v, err: %v", c.accessLists.schemaMain.ACLMapFilePath, err)
|
2023-01-12 11:01:01 +00:00
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-26 05:13:34 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaGenerated.mu.Lock()
|
|
|
|
defer c.accessLists.schemaGenerated.mu.Unlock()
|
2022-05-11 05:13:13 +00:00
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaGenerated.ACLsToConvert = make(map[Node]map[Node]map[command]struct{})
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
// 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.
|
2022-06-01 13:58:17 +00:00
|
|
|
for n := range c.accessLists.schemaMain.ACLMap {
|
2022-05-11 05:13:13 +00:00
|
|
|
//a.schemaGenerated.ACLsToConvert = make(map[node]map[node]map[command]struct{})
|
2022-06-01 13:58:17 +00:00
|
|
|
ap := newAuthParser(n, c.accessLists)
|
2022-05-11 05:13:13 +00:00
|
|
|
ap.parse()
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
inf := fmt.Errorf("generateACLsFor all nodes, ACLsToConvert contains: %#v", c.accessLists.schemaGenerated.ACLsToConvert)
|
2023-01-12 11:03:10 +00:00
|
|
|
c.accessLists.errorKernel.logDebug(inf, c.accessLists.configuration)
|
2022-05-19 18:00:25 +00:00
|
|
|
|
2022-05-11 05:13:13 +00:00
|
|
|
// 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() {
|
2022-05-19 06:27:12 +00:00
|
|
|
// If the map to generate from map is empty we want to also set the generatedACLsMap
|
|
|
|
// to empty so we can make sure that no more generated ACL's exists to be distributed.
|
2022-06-01 13:58:17 +00:00
|
|
|
if len(c.accessLists.schemaGenerated.ACLsToConvert) == 0 {
|
|
|
|
c.accessLists.schemaGenerated.GeneratedACLsMap = make(map[Node]HostACLsSerializedWithHash)
|
2022-05-19 06:27:12 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
for n, m := range c.accessLists.schemaGenerated.ACLsToConvert {
|
2022-05-19 18:00:25 +00:00
|
|
|
//fmt.Printf("\n ################ DEBUG: RANGE in generate: n=%v, m=%v\n", n, m)
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
// cbor marshal the data of the ACL map to store for the host node.
|
|
|
|
cb, err := cbor.Marshal(m)
|
|
|
|
if err != nil {
|
2022-06-02 04:29:37 +00:00
|
|
|
er := fmt.Errorf("error: generateACLsForAllNodes: failed to generate cbor for host in schemaGenerated: %v", err)
|
2023-01-12 11:01:01 +00:00
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the hash for the data for the host node.
|
|
|
|
hash := func() [32]byte {
|
2022-06-01 13:58:17 +00:00
|
|
|
sns := c.accessLists.nodeMapToSlice(n)
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
b, err := cbor.Marshal(sns)
|
|
|
|
if err != nil {
|
2023-01-12 11:01:01 +00:00
|
|
|
er := fmt.Errorf("error: generateACLsForAllNodes: failed to generate cbor for hash: %v", err)
|
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
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.
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaGenerated.GeneratedACLsMap[n] = hostSerialized
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
}()
|
2022-05-19 18:00:25 +00:00
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
inf = fmt.Errorf("generateACLsFor all nodes, GeneratedACLsMap contains: %#v", c.accessLists.schemaGenerated.GeneratedACLsMap)
|
2023-01-12 11:03:10 +00:00
|
|
|
c.accessLists.errorKernel.logDebug(inf, c.accessLists.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
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 {
|
2022-05-18 09:26:06 +00:00
|
|
|
HostNode Node
|
2022-05-11 05:13:13 +00:00
|
|
|
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 {
|
2022-05-18 09:26:06 +00:00
|
|
|
Source Node
|
2022-05-11 05:13:13 +00:00
|
|
|
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.
|
2022-05-18 09:26:06 +00:00
|
|
|
func (a *accessLists) nodeMapToSlice(host Node) sourceNode {
|
2022-05-11 05:13:13 +00:00
|
|
|
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.
|
2022-06-01 13:58:17 +00:00
|
|
|
func (c *centralAuth) groupNodesAddNode(ng nodeGroup, n Node) {
|
2022-06-17 11:13:01 +00:00
|
|
|
//err := c.accessLists.validator.Var(ng, "startswith=grp_nodes_")
|
|
|
|
//if err != nil {
|
|
|
|
// log.Printf("error: group name do not start with grp_nodes_: %v\n", err)
|
|
|
|
// return
|
|
|
|
//}
|
|
|
|
|
|
|
|
if !strings.HasPrefix(string(ng), "grp_nodes_") {
|
2023-01-12 11:01:01 +00:00
|
|
|
er := fmt.Errorf("error: group name do not start with grp_nodes_")
|
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaMain.mu.Lock()
|
|
|
|
defer c.accessLists.schemaMain.mu.Unlock()
|
|
|
|
if _, ok := c.accessLists.schemaMain.NodeGroupMap[ng]; !ok {
|
|
|
|
c.accessLists.schemaMain.NodeGroupMap[ng] = make(map[Node]struct{})
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaMain.NodeGroupMap[ng][n] = struct{}{}
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
// fmt.Printf(" * groupNodesAddNode: After adding to group node looks like: %+v\n", a.schemaMain.NodeGroupMap)
|
|
|
|
|
2022-06-17 11:13:01 +00:00
|
|
|
err := c.generateACLsForAllNodes()
|
2022-05-11 05:13:13 +00:00
|
|
|
if err != nil {
|
|
|
|
er := fmt.Errorf("error: groupNodesAddNode: %v", err)
|
2023-01-12 11:01:01 +00:00
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// groupNodesDeleteNode deletes a node from a group in the map.
|
2022-06-01 13:58:17 +00:00
|
|
|
func (c *centralAuth) groupNodesDeleteNode(ng nodeGroup, n Node) {
|
|
|
|
c.accessLists.schemaMain.mu.Lock()
|
|
|
|
defer c.accessLists.schemaMain.mu.Unlock()
|
|
|
|
if _, ok := c.accessLists.schemaMain.NodeGroupMap[ng][n]; !ok {
|
2023-01-12 11:01:01 +00:00
|
|
|
er := fmt.Errorf("info: no such node with name=%v found in group=%v", ng, n)
|
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
delete(c.accessLists.schemaMain.NodeGroupMap[ng], n)
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
//fmt.Printf(" * After deleting nodeGroup map looks like: %+v\n", a.schemaMain.NodeGroupMap)
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
err := c.generateACLsForAllNodes()
|
2022-05-11 05:13:13 +00:00
|
|
|
if err != nil {
|
|
|
|
er := fmt.Errorf("error: groupNodesDeleteNode: %v", err)
|
2023-01-12 11:01:01 +00:00
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// groupNodesDeleteGroup deletes a nodeGroup from map.
|
2022-06-01 13:58:17 +00:00
|
|
|
func (c *centralAuth) groupNodesDeleteGroup(ng nodeGroup) {
|
|
|
|
c.accessLists.schemaMain.mu.Lock()
|
|
|
|
defer c.accessLists.schemaMain.mu.Unlock()
|
|
|
|
if _, ok := c.accessLists.schemaMain.NodeGroupMap[ng]; !ok {
|
2023-01-12 11:01:01 +00:00
|
|
|
er := fmt.Errorf("info: no such group found: %v", ng)
|
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
delete(c.accessLists.schemaMain.NodeGroupMap, ng)
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
//fmt.Printf(" * After deleting nodeGroup map looks like: %+v\n", a.schemaMain.NodeGroupMap)
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
err := c.generateACLsForAllNodes()
|
2022-05-11 05:13:13 +00:00
|
|
|
if err != nil {
|
|
|
|
er := fmt.Errorf("error: groupNodesDeleteGroup: %v", err)
|
2023-01-12 11:01:01 +00:00
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----
|
|
|
|
|
|
|
|
// groupCommandsAddCommand adds a command to a group. If the group does
|
|
|
|
// not exist it will be created.
|
2022-06-01 13:58:17 +00:00
|
|
|
func (c *centralAuth) groupCommandsAddCommand(cg commandGroup, cmd command) {
|
2022-06-17 11:13:01 +00:00
|
|
|
// err := c.accessLists.validator.Var(cg, "startswith=grp_commands_")
|
|
|
|
// if err != nil {
|
|
|
|
// log.Printf("error: group name do not start with grp_commands_ : %v\n", err)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
|
|
|
|
if !strings.HasPrefix(string(cg), "grp_commands_") {
|
2023-01-12 11:01:01 +00:00
|
|
|
er := fmt.Errorf("error: group name do not start with grp_commands_")
|
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaMain.mu.Lock()
|
|
|
|
defer c.accessLists.schemaMain.mu.Unlock()
|
|
|
|
if _, ok := c.accessLists.schemaMain.CommandGroupMap[cg]; !ok {
|
|
|
|
c.accessLists.schemaMain.CommandGroupMap[cg] = make(map[command]struct{})
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaMain.CommandGroupMap[cg][cmd] = struct{}{}
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
//fmt.Printf(" * groupCommandsAddCommand: After adding command=%v to command group=%v map looks like: %+v\n", c, cg, a.schemaMain.CommandGroupMap)
|
|
|
|
|
2022-06-17 11:13:01 +00:00
|
|
|
err := c.generateACLsForAllNodes()
|
2022-05-11 05:13:13 +00:00
|
|
|
if err != nil {
|
|
|
|
er := fmt.Errorf("error: groupCommandsAddCommand: %v", err)
|
2023-01-12 11:01:01 +00:00
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// groupCommandsDeleteCommand deletes a command from a group in the map.
|
2022-06-01 13:58:17 +00:00
|
|
|
func (c *centralAuth) groupCommandsDeleteCommand(cg commandGroup, cmd command) {
|
|
|
|
c.accessLists.schemaMain.mu.Lock()
|
|
|
|
defer c.accessLists.schemaMain.mu.Unlock()
|
|
|
|
if _, ok := c.accessLists.schemaMain.CommandGroupMap[cg][cmd]; !ok {
|
2023-01-12 11:01:01 +00:00
|
|
|
er := fmt.Errorf("info: no such command with name=%v found in group=%v", c, cg)
|
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
delete(c.accessLists.schemaMain.CommandGroupMap[cg], cmd)
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
//fmt.Printf(" * After deleting command=%v from group=%v map looks like: %+v\n", c, cg, a.schemaMain.CommandGroupMap)
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
err := c.generateACLsForAllNodes()
|
2022-05-11 05:13:13 +00:00
|
|
|
if err != nil {
|
|
|
|
er := fmt.Errorf("error: groupCommandsDeleteCommand: %v", err)
|
2023-01-12 11:01:01 +00:00
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// groupCommandDeleteGroup deletes a commandGroup map.
|
2022-06-01 13:58:17 +00:00
|
|
|
func (c *centralAuth) groupCommandDeleteGroup(cg commandGroup) {
|
|
|
|
c.accessLists.schemaMain.mu.Lock()
|
|
|
|
defer c.accessLists.schemaMain.mu.Unlock()
|
|
|
|
if _, ok := c.accessLists.schemaMain.CommandGroupMap[cg]; !ok {
|
2023-01-12 11:01:01 +00:00
|
|
|
er := fmt.Errorf("info: no such group found: %v", cg)
|
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
delete(c.accessLists.schemaMain.CommandGroupMap, cg)
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
//fmt.Printf(" * After deleting commandGroup=%v map looks like: %+v\n", cg, a.schemaMain.CommandGroupMap)
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
err := c.generateACLsForAllNodes()
|
2022-05-11 05:13:13 +00:00
|
|
|
if err != nil {
|
|
|
|
er := fmt.Errorf("error: groupCommandDeleteGroup: %v", err)
|
2023-01-12 11:01:01 +00:00
|
|
|
c.errorKernel.logError(er, c.configuration)
|
2022-05-11 05:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// exportACLs will export the current content of the main ACLMap in JSON format.
|
2022-06-01 13:58:17 +00:00
|
|
|
func (c *centralAuth) exportACLs() ([]byte, error) {
|
2022-05-11 05:13:13 +00:00
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaMain.mu.Lock()
|
|
|
|
defer c.accessLists.schemaMain.mu.Unlock()
|
2022-05-11 05:13:13 +00:00
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
js, err := json.Marshal(c.accessLists.schemaMain.ACLMap)
|
2022-05-11 05:13:13 +00:00
|
|
|
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.
|
2022-06-01 13:58:17 +00:00
|
|
|
func (c *centralAuth) importACLs(js []byte) error {
|
2022-05-11 05:13:13 +00:00
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaMain.mu.Lock()
|
|
|
|
defer c.accessLists.schemaMain.mu.Unlock()
|
2022-05-11 05:13:13 +00:00
|
|
|
|
2022-05-18 09:26:06 +00:00
|
|
|
m := make(map[Node]map[Node]map[command]struct{})
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
err := json.Unmarshal(js, &m)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error: failed to unmarshal into ACLMap: %v", err)
|
|
|
|
}
|
|
|
|
|
2022-06-01 13:58:17 +00:00
|
|
|
c.accessLists.schemaMain.ACLMap = m
|
2022-05-11 05:13:13 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|