Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/convertor/device/device_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestGenerateconfigsSystem(t *testing.T) {
{
name: "NTP and TACACS+",
ntp: &ntp.NTP{ServerList: []ntp.Server{{Name: "ntp1", ServerAddress: "10.0.0.1"}}},
tacacs: &tacacs.Tacacs{Passkey: "secret", ServerList: []tacacs.Server{{ServerAddress: "10.10.10.10", Priority: 1, TCPPort: 49}}},
tacacs: &tacacs.Tacacs{ServerList: []tacacs.Server{{ServerAddress: "10.10.10.10", Priority: 1, TCPPort: 49, Passkey: "secret"}}},
wantKeys: []string{"aaa", "config", "ntp"},
},
{
Expand Down
13 changes: 4 additions & 9 deletions internal/convertor/tacacs/tacacs.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,12 @@ const tacacsServerGroupName = "TACACS+"
// TacacsToOpenConfigAAA converts a CMDB TACACS configuration into the aaa
// container of openconfig-system, under a single TACACS+ server group.
//
// It returns nil when there is no server to advertise: the CMDB holds a
// single passkey for the whole device, and the model has no place for it
// outside of a server entry.
// It returns nil when there is no server to advertise.
//
// OpenConfig's server list has no ordering or priority leaf of its own, so
// the CMDB priority is carried by the criteo-aaa-ext priority leaf instead of
// the position in the list. Servers are still appended by descending
// priority, ties broken on the address, to keep the emitted JSON stable.
//
// The CMDB holds one passkey per device where the model has one shared
// secret per server, so it is set on each of them.
func TacacsToOpenConfigAAA(config *tacacs.Tacacs) (*openconfig.System_Aaa, error) {
if config == nil || len(config.ServerList) == 0 {
return nil, nil
Expand Down Expand Up @@ -59,13 +54,13 @@ func TacacsToOpenConfigAAA(config *tacacs.Tacacs) (*openconfig.System_Aaa, error
entry.Priority = ygot.Uint32(server.Priority)
}

if server.TCPPort != 0 || config.Passkey != "" {
if server.TCPPort != 0 || server.Passkey != "" {
entry.Tacacs = &openconfig.System_Aaa_ServerGroup_Server_Tacacs{}
if server.TCPPort != 0 {
entry.Tacacs.Port = ygot.Uint16(server.TCPPort)
}
if config.Passkey != "" {
entry.Tacacs.SecretKey = ygot.String(config.Passkey)
if server.Passkey != "" {
entry.Tacacs.SecretKey = ygot.String(server.Passkey)
}
}
}
Expand Down
21 changes: 8 additions & 13 deletions internal/convertor/tacacs/tacacs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,19 @@ func TestTacacsToOpenConfigAAA(t *testing.T) {
{
name: "one server",
args: &tacacs.Tacacs{
Passkey: "Mypasword1",
ServerList: []tacacs.Server{
{ServerAddress: "1.1.1.1", Priority: 1, TCPPort: 49},
{ServerAddress: "1.1.1.1", Priority: 1, TCPPort: 49, Passkey: "Mypasword1"},
},
},
want: map[string]uint32{"1.1.1.1": 1},
},
{
name: "several servers, each keeps its own priority",
args: &tacacs.Tacacs{
Passkey: "Mypasword1",
ServerList: []tacacs.Server{
{ServerAddress: "1.1.1.3", Priority: 1, TCPPort: 49},
{ServerAddress: "1.1.1.1", Priority: 3, TCPPort: 49},
{ServerAddress: "1.1.1.2", Priority: 2, TCPPort: 4949},
{ServerAddress: "1.1.1.3", Priority: 1, TCPPort: 49, Passkey: "Mypasword1"},
{ServerAddress: "1.1.1.1", Priority: 3, TCPPort: 49, Passkey: "Mypasword1"},
{ServerAddress: "1.1.1.2", Priority: 2, TCPPort: 4949, Passkey: "Mypasword1"},
},
},
want: map[string]uint32{"1.1.1.3": 1, "1.1.1.1": 3, "1.1.1.2": 2},
Expand Down Expand Up @@ -91,14 +89,13 @@ func TestTacacsToOpenConfigAAA(t *testing.T) {
func TestTacacsToOpenConfigAAAJSON(t *testing.T) {
want := `{"system":{"aaa":{"server-groups":{"server-group":[{"config":{"name":"TACACS+","type":"TACACS"},"name":"TACACS+","servers":{"server":[` +
`{"address":"1.1.1.1","config":{"address":"1.1.1.1","priority":2},"tacacs":{"config":{"port":49,"secret-key":"Mypasword1"}}},` +
`{"address":"1.1.1.2","config":{"address":"1.1.1.2","priority":1},"tacacs":{"config":{"port":4949,"secret-key":"Mypasword1"}}}` +
`{"address":"1.1.1.2","config":{"address":"1.1.1.2","priority":1},"tacacs":{"config":{"port":4949,"secret-key":"Mypasword2"}}}` +
`]}}]}}}}`

config := &tacacs.Tacacs{
Passkey: "Mypasword1",
ServerList: []tacacs.Server{
{ServerAddress: "1.1.1.2", Priority: 1, TCPPort: 4949},
{ServerAddress: "1.1.1.1", Priority: 2, TCPPort: 49},
{ServerAddress: "1.1.1.2", Priority: 1, TCPPort: 4949, Passkey: "Mypasword2"},
{ServerAddress: "1.1.1.1", Priority: 2, TCPPort: 49, Passkey: "Mypasword1"},
},
}

Expand Down Expand Up @@ -131,13 +128,11 @@ func TestTacacsToOpenConfigAAADefaults(t *testing.T) {
}
}

// TestTacacsToOpenConfigAAANoServer checks nothing is generated without a server:
// the passkey alone has no place in the model.
// TestTacacsToOpenConfigAAANoServer checks nothing is generated without a server.
func TestTacacsToOpenConfigAAANoServer(t *testing.T) {
tests := map[string]*tacacs.Tacacs{
"no configuration at all": nil,
"empty configuration": {},
"passkey but no server": {Passkey: "Mypasword1"},
}

for name, config := range tests {
Expand Down
10 changes: 6 additions & 4 deletions internal/ingestor/cmdb/tacacs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,20 @@ func TestPrecomputeTacacs(t *testing.T) {
"id":1,
"name":"tor01-01"
},
"passkey":"Mypasword1",
"server_list":[
{
"id":1,
"server_address":"1.1.1.1",
"priority":1,
"tcp_port":49
"tcp_port":49,
"passkey":"Mypasword1"
},
{
"id":2,
"server_address":"1.1.1.2",
"priority":2,
"tcp_port":4949
"tcp_port":4949,
"passkey":"Mypasword2"
}
],
"created":"2026-08-10T07:37:29.238195Z",
Expand All @@ -50,17 +51,18 @@ func TestPrecomputeTacacs(t *testing.T) {
}{
Name: "tor01-01",
},
Passkey: "Mypasword1",
ServerList: []tacacs.Server{
{
ServerAddress: "1.1.1.1",
Priority: 1,
TCPPort: 49,
Passkey: "Mypasword1",
},
{
ServerAddress: "1.1.1.2",
Priority: 2,
TCPPort: 4949,
Passkey: "Mypasword2",
},
},
},
Expand Down
4 changes: 2 additions & 2 deletions internal/model/cmdb/tacacs/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ type Server struct {
ServerAddress string `json:"server_address" validate:"required,ip"`
Priority uint32 `json:"priority" validate:"omitempty"`
TCPPort uint16 `json:"tcp_port" validate:"omitempty"`
Passkey string `json:"passkey" validate:"omitempty"`
}

// Tacacs is the TACACS configuration of a single device: its global settings and its servers.
// Tacacs is the TACACS configuration of a single device: its servers.
type Tacacs struct {
Device struct {
Name string `json:"name" validate:"required"`
} `json:"device" validate:"required"`
Passkey string `json:"passkey" validate:"omitempty"`
ServerList []Server `json:"server_list" validate:"omitempty,dive"`
}
Loading