From 0ba5f6ea5da9845ac5c27c10d5ecd2e86cebd9be Mon Sep 17 00:00:00 2001 From: postmannen Date: Wed, 18 May 2022 11:26:06 +0200 Subject: [PATCH] added initial REQ for adding acl --- central_auth_acl_handling.go | 48 ++++++++++---------- central_auth_parser.go | 42 ++++++++--------- central_auth_test.go | 18 ++++---- processes.go | 8 ++++ requests-acl.go | 87 ++++++++++++++++++++++++++++++++++++ requests.go | 5 +++ 6 files changed, 154 insertions(+), 54 deletions(-) create mode 100644 requests-acl.go diff --git a/central_auth_acl_handling.go b/central_auth_acl_handling.go index 2e71e25..da58f81 100644 --- a/central_auth_acl_handling.go +++ b/central_auth_acl_handling.go @@ -48,23 +48,23 @@ func newAccessLists() *accessLists { return &a } -type node string +// 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{} + 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{}), + 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 @@ -74,15 +74,15 @@ func newSchemaMain() *schemaMain { // 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 + 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), + ACLsToConvert: map[Node]map[Node]map[command]struct{}{}, + GeneratedACLsMap: make(map[Node]HostACLsSerializedWithHash), } return &s } @@ -104,8 +104,8 @@ type HostACLsSerializedWithHash struct { // the slice. // If the argument is not a group kind of value, then only a slice with that single // value is returned. -func (a *accessLists) nodeAsSlice(n node) []node { - nodes := []node{} +func (a *accessLists) 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. @@ -115,7 +115,7 @@ func (a *accessLists) nodeAsSlice(n node) []node { } } else { // No group found meaning a single node was given as an argument. - nodes = []node{n} + nodes = []Node{n} } return nodes @@ -148,14 +148,14 @@ func (a *accessLists) commandAsSlice(c command) []command { // 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 *accessLists) aclAdd(host node, source node, cmd command) { +func (a *accessLists) 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{}) + a.schemaMain.ACLMap[host] = make(map[Node]map[command]struct{}) } // Check if also source node exists in map @@ -177,7 +177,7 @@ func (a *accessLists) aclAdd(host node, source node, cmd command) { } // aclDeleteCommand will delete the specified command from the fromnode. -func (a *accessLists) aclDeleteCommand(host node, source node, cmd command) error { +func (a *accessLists) aclDeleteCommand(host Node, source Node, cmd command) error { a.schemaMain.mu.Lock() defer a.schemaMain.mu.Unlock() @@ -206,7 +206,7 @@ func (a *accessLists) aclDeleteCommand(host node, source node, cmd command) erro } // aclDeleteSource will delete specified source node and all commands specified for it. -func (a *accessLists) aclDeleteSource(host node, source node) error { +func (a *accessLists) aclDeleteSource(host Node, source Node) error { a.schemaMain.mu.Lock() defer a.schemaMain.mu.Unlock() @@ -241,7 +241,7 @@ func (a *accessLists) generateACLsForAllNodes() error { a.schemaGenerated.mu.Lock() defer a.schemaGenerated.mu.Unlock() - a.schemaGenerated.ACLsToConvert = make(map[node]map[node]map[command]struct{}) + 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 @@ -304,7 +304,7 @@ func (a *accessLists) generateACLsForAllNodes() error { // and we then use the slice representation of the ACL to create the hash for // a specific host node. type sourceNode struct { - HostNode node + HostNode Node SourceCommands []sourceNodeCommands } @@ -312,7 +312,7 @@ type sourceNode struct { // and we then use the slice representation of the ACL to create the hash for // a specific host node. type sourceNodeCommands struct { - Source node + Source Node Commands []command } @@ -321,7 +321,7 @@ type sourceNodeCommands struct { // 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 *accessLists) nodeMapToSlice(host node) sourceNode { +func (a *accessLists) nodeMapToSlice(host Node) sourceNode { srcNodes := sourceNode{ HostNode: host, } @@ -355,7 +355,7 @@ func (a *accessLists) nodeMapToSlice(host node) sourceNode { // groupNodesAddNode adds a node to a group. If the group does // not exist it will be created. -func (a *accessLists) groupNodesAddNode(ng nodeGroup, n node) { +func (a *accessLists) 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) @@ -365,7 +365,7 @@ func (a *accessLists) groupNodesAddNode(ng nodeGroup, n node) { 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] = make(map[Node]struct{}) } a.schemaMain.NodeGroupMap[ng][n] = struct{}{} @@ -381,7 +381,7 @@ func (a *accessLists) groupNodesAddNode(ng nodeGroup, n node) { } // groupNodesDeleteNode deletes a node from a group in the map. -func (a *accessLists) groupNodesDeleteNode(ng nodeGroup, n node) { +func (a *accessLists) groupNodesDeleteNode(ng nodeGroup, n Node) { a.schemaMain.mu.Lock() defer a.schemaMain.mu.Unlock() if _, ok := a.schemaMain.NodeGroupMap[ng][n]; !ok { @@ -515,7 +515,7 @@ func (a *accessLists) importACLs(js []byte) error { a.schemaMain.mu.Lock() defer a.schemaMain.mu.Unlock() - m := make(map[node]map[node]map[command]struct{}) + m := make(map[Node]map[Node]map[command]struct{}) err := json.Unmarshal(js, &m) if err != nil { diff --git a/central_auth_parser.go b/central_auth_parser.go index 3432f7b..ba61d31 100644 --- a/central_auth_parser.go +++ b/central_auth_parser.go @@ -5,16 +5,16 @@ import ( ) type authParser struct { - currentHost node - authSchema *accessLists + currentHost Node + accessLists *accessLists //ACLsToConvert map[node]map[node]map[command]struct{} } // newAuthParser returns a new authParser, with the current host node set. -func newAuthParser(n node, accessLists *accessLists) *authParser { +func newAuthParser(n Node, accessLists *accessLists) *authParser { a := authParser{ currentHost: n, - authSchema: accessLists, + accessLists: accessLists, //ACLsToConvert: make(map[node]map[node]map[command]struct{}), } return &a @@ -50,27 +50,27 @@ func (a *authParser) hostGroupOrSingle() parseFn { func (a *authParser) hostIsGroup() parseFn { // fmt.Printf("%v is a grp type\n", a.currentHost) - hosts := a.authSchema.nodeAsSlice(a.currentHost) + hosts := a.accessLists.nodeAsSlice(a.currentHost) - for source, cmdMap := range a.authSchema.schemaMain.ACLMap[a.currentHost] { + for source, cmdMap := range a.accessLists.schemaMain.ACLMap[a.currentHost] { for cmd, emptyStruct := range cmdMap { - cmdSlice := a.authSchema.commandAsSlice(cmd) + cmdSlice := a.accessLists.commandAsSlice(cmd) // Expand eventual groups, so we use real fromNode nodenames in ACL for nodes. - sourceNodes := a.authSchema.nodeAsSlice(source) + sourceNodes := a.accessLists.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.accessLists.schemaGenerated.ACLsToConvert[host] == nil { + a.accessLists.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{}) + if a.accessLists.schemaGenerated.ACLsToConvert[host][sourceNode] == nil { + a.accessLists.schemaGenerated.ACLsToConvert[host][sourceNode] = make(map[command]struct{}) } - a.authSchema.schemaGenerated.ACLsToConvert[host][sourceNode][cm] = emptyStruct + a.accessLists.schemaGenerated.ACLsToConvert[host][sourceNode][cm] = emptyStruct } } } @@ -88,24 +88,24 @@ func (a *authParser) hostIsNotGroup() parseFn { host := a.currentHost - for source, cmdMap := range a.authSchema.schemaMain.ACLMap[a.currentHost] { + for source, cmdMap := range a.accessLists.schemaMain.ACLMap[a.currentHost] { for cmd, emptyStruct := range cmdMap { - cmdSlice := a.authSchema.commandAsSlice(cmd) + cmdSlice := a.accessLists.commandAsSlice(cmd) // Expand eventual groups, so we use real fromNode nodenames in ACL for nodes. - sourceNodes := a.authSchema.nodeAsSlice(source) + sourceNodes := a.accessLists.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.accessLists.schemaGenerated.ACLsToConvert[host] == nil { + a.accessLists.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{}) + if a.accessLists.schemaGenerated.ACLsToConvert[host][sourceNode] == nil { + a.accessLists.schemaGenerated.ACLsToConvert[host][sourceNode] = make(map[command]struct{}) } - a.authSchema.schemaGenerated.ACLsToConvert[host][sourceNode][cm] = emptyStruct + a.accessLists.schemaGenerated.ACLsToConvert[host][sourceNode][cm] = emptyStruct } } } diff --git a/central_auth_test.go b/central_auth_test.go index 7a454cd..fb4ef40 100644 --- a/central_auth_test.go +++ b/central_auth_test.go @@ -22,7 +22,7 @@ func TestACLSingleNode(t *testing.T) { // --- TESTS --- - mapOfFromNodeCommands := make(map[node]map[command]struct{}) + mapOfFromNodeCommands := make(map[Node]map[command]struct{}) err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands) if err != nil { t.Fatal(err) @@ -64,7 +64,7 @@ func TestACLWithGroups(t *testing.T) { a.aclAdd(grp_nodes_ships, grp_nodes_operators, grp_commands_commandset1) - mapOfFromNodeCommands := make(map[node]map[command]struct{}) + mapOfFromNodeCommands := make(map[Node]map[command]struct{}) err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands) if err != nil { t.Fatal(err) @@ -127,7 +127,7 @@ func TestACLNodesGroupDeleteNode(t *testing.T) { // Check that we still got the data for ship100. { - mapOfFromNodeCommands := make(map[node]map[command]struct{}) + mapOfFromNodeCommands := make(map[Node]map[command]struct{}) err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship100"].Data, &mapOfFromNodeCommands) if err != nil { t.Fatal(err) @@ -140,7 +140,7 @@ func TestACLNodesGroupDeleteNode(t *testing.T) { // Check that we don't have any data for ship101. { - mapOfFromNodeCommands := make(map[node]map[command]struct{}) + mapOfFromNodeCommands := make(map[Node]map[command]struct{}) err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands) if err != nil { t.Fatal(err) @@ -184,7 +184,7 @@ func TestGroupNodesDeleteGroup(t *testing.T) { // Check that we still got the data for other ACL's. { - mapOfFromNodeCommands := make(map[node]map[command]struct{}) + mapOfFromNodeCommands := make(map[Node]map[command]struct{}) err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands) if err != nil { t.Fatal(err) @@ -197,7 +197,7 @@ func TestGroupNodesDeleteGroup(t *testing.T) { // Check that we don't have any data for grp_nodes_operators { - mapOfFromNodeCommands := make(map[node]map[command]struct{}) + mapOfFromNodeCommands := make(map[Node]map[command]struct{}) err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands) if err != nil { t.Fatal(err) @@ -241,7 +241,7 @@ func TestGroupCommandDeleteGroup(t *testing.T) { // Check that we still got the data for other ACL's. { - mapOfFromNodeCommands := make(map[node]map[command]struct{}) + mapOfFromNodeCommands := make(map[Node]map[command]struct{}) err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands) if err != nil { t.Fatal(err) @@ -254,7 +254,7 @@ func TestGroupCommandDeleteGroup(t *testing.T) { // Check that we don't have any data for grp_nodes_operators { - mapOfFromNodeCommands := make(map[node]map[command]struct{}) + mapOfFromNodeCommands := make(map[Node]map[command]struct{}) err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands) if err != nil { t.Fatal(err) @@ -287,7 +287,7 @@ func TestACLGenerated(t *testing.T) { // --- TESTS --- - mapOfFromNodeCommands := make(map[node]map[command]struct{}) + mapOfFromNodeCommands := make(map[Node]map[command]struct{}) err := cbor.Unmarshal(a.schemaGenerated.GeneratedACLsMap["ship101"].Data, &mapOfFromNodeCommands) if err != nil { t.Fatal(err) diff --git a/processes.go b/processes.go index 82c3acb..cc64aeb 100644 --- a/processes.go +++ b/processes.go @@ -173,6 +173,7 @@ func (p *processes) Start(proc process) { if proc.configuration.IsCentralAuth { proc.startup.subREQPublicKeysGet(proc) proc.startup.subREQPublicKeysAllow(proc) + proc.startup.subREQAclAddAccessList(proc) } if proc.configuration.StartSubREQPublicKeysToNode { @@ -375,6 +376,13 @@ func (s startup) subREQPublicKeysToNode(p process) { go proc.spawnWorker() } +func (s startup) subREQAclAddAccessList(p process) { + log.Printf("Starting Add Access List subscriber: %#v\n", p.node) + sub := newSubject(REQAclAddAccessList, string(p.node)) + proc := newProcess(p.ctx, s.server, sub, processKindSubscriber, nil) + go proc.spawnWorker() +} + func (s startup) subREQToConsole(p process) { log.Printf("Starting Text To Console subscriber: %#v\n", p.node) sub := newSubject(REQToConsole, string(p.node)) diff --git a/requests-acl.go b/requests-acl.go new file mode 100644 index 0000000..648be37 --- /dev/null +++ b/requests-acl.go @@ -0,0 +1,87 @@ +package steward + +import ( + "fmt" +) + +// --- + +type methodREQAclAddAccessList struct { + event Event +} + +func (m methodREQAclAddAccessList) getKind() Event { + return m.event +} + +func (m methodREQAclAddAccessList) handler(proc process, message Message, node string) ([]byte, error) { + inf := fmt.Errorf("<--- methodREQAclAddAccessList received from: %v, containing: %v", message.FromNode, message.MethodArgs) + proc.errorKernel.logConsoleOnlyIfDebug(inf, proc.configuration) + + proc.processes.wg.Add(1) + go func() { + defer proc.processes.wg.Done() + + switch { + case len(message.MethodArgs) < 3: + er := fmt.Errorf("error: methodREQAclAddAccessList: got <3 number methodArgs, want 3") + proc.errorKernel.errSend(proc, message, er) + + return + } + + // Get a context with the timeout specified in message.MethodTimeout. + ctx, cancel := getContextForMethodTimeout(proc.ctx, message) + + outCh := make(chan []byte) + + proc.processes.wg.Add(1) + go func() { + defer proc.processes.wg.Done() + + host := message.MethodArgs[0] + source := message.MethodArgs[1] + cmd := message.MethodArgs[2] + + proc.centralAuth.accessLists.aclAdd(Node(host), Node(source), command(cmd)) + + // Just print out for testing. + proc.centralAuth.accessLists.schemaMain.mu.Lock() + fmt.Printf("\n ---------- content of main acl map: %v-----------\n", proc.centralAuth.accessLists.schemaMain.ACLMap) + proc.centralAuth.accessLists.schemaMain.mu.Unlock() + + proc.centralAuth.accessLists.schemaGenerated.mu.Lock() + fmt.Printf("\n ---------- content of generated acl map: %s-----------\n", proc.centralAuth.accessLists.schemaGenerated.GeneratedACLsMap) + proc.centralAuth.accessLists.schemaGenerated.mu.Unlock() + + outString := fmt.Sprintf("acl added: host=%v, source=%v, command=%v\n", host, source, cmd) + out := []byte(outString) + + select { + case outCh <- out: + case <-ctx.Done(): + return + } + }() + + select { + case <-ctx.Done(): + + cancel() + er := fmt.Errorf("error: methodREQAclAddAccessList: method timed out: %v", message.MethodArgs) + proc.errorKernel.errSend(proc, message, er) + + case out := <-outCh: + + // Prepare and queue for sending a new message with the output + // of the action executed. + newReplyMessage(proc, message, out) + } + + }() + + ackMsg := []byte("confirmed from: " + node + ": " + fmt.Sprint(message.ID)) + return ackMsg, nil +} + +// --- diff --git a/requests.go b/requests.go index edc41bf..e51b2b9 100644 --- a/requests.go +++ b/requests.go @@ -144,6 +144,8 @@ const ( REQPublicKeysToNode Method = "REQPublicKeysToNode" // REQAuthPublicKeysAllow REQPublicKeysAllow Method = "REQPublicKeysAllow" + // REQAclAddAcl + REQAclAddAccessList = "REQAclAddAccessList" ) // The mapping of all the method constants specified, what type @@ -234,6 +236,9 @@ func (m Method) GetMethodsAvailable() MethodsAvailable { REQPublicKeysAllow: methodREQPublicKeysAllow{ event: EventACK, }, + REQAclAddAccessList: methodREQAclAddAccessList{ + event: EventACK, + }, }, }